Updated libOpenMPT to version 0.7.9
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
03b52685bd
commit
d3415a3a25
137 changed files with 2238 additions and 1277 deletions
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors
|
||||
Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors
|
||||
Copyright (c) 1997-2003, Olivier Lapicque
|
||||
All rights reserved.
|
||||
|
||||
|
|
|
@ -585,32 +585,10 @@ endif
|
|||
|
||||
endif
|
||||
|
||||
ifeq ($(OPTIMIZE_FASTMATH),2)
|
||||
CPPFLAGS += -DMPT_CHECK_CXX_IGNORE_WARNING_FASTMATH -DMPT_CHECK_CXX_IGNORE_WARNING_FINITEMATH
|
||||
CXXFLAGS += -ffast-math
|
||||
CFLAGS += -ffast-math
|
||||
else ifeq ($(OPTIMIZE_FASTMATH),1)
|
||||
CPPFLAGS += -DMPT_CHECK_CXX_IGNORE_WARNING_FINITEMATH
|
||||
CXXFLAGS += -fassociative-math
|
||||
CXXFLAGS += -fcx-limited-range
|
||||
CXXFLAGS += -fexcess-precision=fast
|
||||
CXXFLAGS += -ffinite-math-only
|
||||
CXXFLAGS += -freciprocal-math
|
||||
CXXFLAGS += -fno-math-errno
|
||||
CXXFLAGS += -fno-rounding-math
|
||||
CXXFLAGS += -fno-signaling-nans
|
||||
CXXFLAGS += -fno-signed-zeros
|
||||
CXXFLAGS += -fno-trapping-math
|
||||
CFLAGS += -fassociative-math
|
||||
CFLAGS += -fcx-limited-range
|
||||
CFLAGS += -fexcess-precision=fast
|
||||
CFLAGS += -ffinite-math-only
|
||||
CFLAGS += -freciprocal-math
|
||||
CFLAGS += -fno-math-errno
|
||||
CFLAGS += -fno-rounding-math
|
||||
CFLAGS += -fno-signaling-nans
|
||||
CFLAGS += -fno-signed-zeros
|
||||
CFLAGS += -fno-trapping-math
|
||||
ifeq ($(MPT_COMPILER_NOIPARA),1)
|
||||
# See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||
CXXFLAGS += -fno-ipa-ra
|
||||
CFLAGS += -fno-ipa-ra
|
||||
endif
|
||||
|
||||
ifeq ($(CHECKED),1)
|
||||
|
@ -707,10 +685,10 @@ endif
|
|||
endif
|
||||
|
||||
ifeq ($(LOCAL_MPG123),1)
|
||||
CPPFLAGS_MPG123 := -DMPT_WITH_MPG123
|
||||
CPPFLAGS_MPG123 := -DMPT_WITH_MPG123 -DMPG123_NO_LARGENAME
|
||||
LDFLAGS_MPG123 :=
|
||||
LDLIBS_MPG123 :=
|
||||
CPPFLAGS_MPG123 += -Iinclude/mpg123/src/libmpg123/ -Iinclude/mpg123/src/compat/ -Iinclude/mpg123/src/ -Iinclude/mpg123/ports/makefile/
|
||||
CPPFLAGS_MPG123 += -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/
|
||||
LOCAL_MPG123_SOURCES :=
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat.c
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat_str.c
|
||||
|
@ -726,6 +704,7 @@ LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/index.c
|
|||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer1.c
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer2.c
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer3.c
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/lfs_wrap.c
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/libmpg123.c
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/ntom.c
|
||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/optimize.c
|
||||
|
@ -741,6 +720,10 @@ include/mpg123/src/compat/%$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENER
|
|||
include/mpg123/src/compat/%.test$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC
|
||||
include/mpg123/src/libmpg123/%$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC
|
||||
include/mpg123/src/libmpg123/%.test$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC
|
||||
include/mpg123/src/compat/%$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS)
|
||||
include/mpg123/src/compat/%.test$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS)
|
||||
include/mpg123/src/libmpg123/%$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS)
|
||||
include/mpg123/src/libmpg123/%.test$(FLAVOUR_O).o : CPPFLAGS:= -Iinclude/mpg123/src/include/ -Iinclude/mpg123/ports/makefile/ $(CPPFLAGS)
|
||||
else
|
||||
ifeq ($(NO_MPG123),1)
|
||||
else
|
||||
|
@ -795,7 +778,10 @@ ifeq ($(LOCAL_VORBIS),1)
|
|||
CPPFLAGS_VORBIS := -DMPT_WITH_VORBIS
|
||||
LDFLAGS_VORBIS :=
|
||||
LDLIBS_VORBIS :=
|
||||
CPPFLAGS_VORBIS += -Iinclude/vorbis/include/ -Iinclude/vorbis/lib/ -DHAVE_ALLOCA_H
|
||||
CPPFLAGS_VORBIS += -Iinclude/vorbis/include/ -Iinclude/vorbis/lib/
|
||||
ifneq ($(MPT_COMPILER_NOALLOCAH),1)
|
||||
CPPFLAGS_VORBIS += -DHAVE_ALLOCA_H
|
||||
endif
|
||||
LOCAL_VORBIS_SOURCES :=
|
||||
LOCAL_VORBIS_SOURCES += include/vorbis/lib/analysis.c
|
||||
LOCAL_VORBIS_SOURCES += include/vorbis/lib/bitrate.c
|
||||
|
@ -991,6 +977,7 @@ CPPCHECK_FLAGS += -j $(NUMTHREADS)
|
|||
CPPCHECK_FLAGS += --std=c11 --std=c++17
|
||||
CPPCHECK_FLAGS += --quiet
|
||||
CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]'
|
||||
CPPCHECK_FLAGS += --check-level=exhaustive
|
||||
CPPCHECK_FLAGS += --suppress=missingIncludeSystem
|
||||
CPPCHECK_FLAGS += --suppress=uninitMemberVar
|
||||
|
||||
|
@ -1849,7 +1836,6 @@ bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar:
|
|||
mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin
|
||||
mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all
|
||||
cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.js bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.js
|
||||
cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.js.mem bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.js.mem
|
||||
cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.wasm bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.wasm
|
||||
cp bin/$(FLAVOUR_DIR)stage/all/libopenmpt.wasm.js bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all/libopenmpt.wasm.js
|
||||
mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)wasm
|
||||
|
@ -1857,7 +1843,6 @@ bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar:
|
|||
cp bin/$(FLAVOUR_DIR)stage/wasm/libopenmpt.wasm bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)wasm/libopenmpt.wasm
|
||||
mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)js
|
||||
cp bin/$(FLAVOUR_DIR)stage/js/libopenmpt.js bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)js/libopenmpt.js
|
||||
cp bin/$(FLAVOUR_DIR)stage/js/libopenmpt.js.mem bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)js/libopenmpt.js.mem
|
||||
cd bin/$(FLAVOUR_DIR)dist-js/ && tar cv --numeric-owner --owner=0 --group=0 libopenmpt-$(DIST_LIBOPENMPT_VERSION) > libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar
|
||||
|
||||
.PHONY: bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip
|
||||
|
@ -2005,10 +1990,10 @@ ifeq ($(SHARED_LIB),1)
|
|||
endif
|
||||
endif
|
||||
|
||||
contrib/fuzzing/fuzz$(FLAVOUR_O).o: contrib/fuzzing/fuzz.c
|
||||
$(INFO) [CC] $<
|
||||
$(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*$(FLAVOUR_O).d
|
||||
$(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
contrib/fuzzing/fuzz$(FLAVOUR_O).o: contrib/fuzzing/fuzz.cpp
|
||||
$(INFO) [CXX] $<
|
||||
$(VERYSILENT)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*$(FLAVOUR_O).d
|
||||
$(SILENT)$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||
bin/$(FLAVOUR_DIR)fuzz$(EXESUFFIX): contrib/fuzzing/fuzz$(FLAVOUR_O).o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT)
|
||||
$(INFO) [LD] $@
|
||||
$(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) contrib/fuzzing/fuzz$(FLAVOUR_O).o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@
|
||||
|
|
|
@ -56,8 +56,9 @@ How to compile
|
|||
|
||||
- Visual Studio 2017 XP targeting toolset
|
||||
|
||||
- OpenMPT requires the compile host system to be Windows 8.1 (or later) amd64,
|
||||
or Windows 11 (or later) ARM64.
|
||||
- OpenMPT requires the compile host system to be Windows 8.1 (or later) on
|
||||
amd64 for VS2019 and VS2017, Windows 10 (or later) on amd64 for VS2022, or
|
||||
Windows 11 (or later) ARM64.
|
||||
|
||||
- In order to build OpenMPT for Windows XP, the Visual Studio 2017 XP
|
||||
targeting toolset as well as the Windows 8.1 SDK need to be installed. The
|
||||
|
|
|
@ -3,3 +3,5 @@ APP_CFLAGS := -std=c17
|
|||
APP_CPPFLAGS := -std=c++17 -fexceptions -frtti
|
||||
APP_LDFLAGS :=
|
||||
APP_STL := c++_shared
|
||||
|
||||
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
MPT_SVNVERSION=19406
|
||||
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.2
|
||||
MPT_SVNDATE=2023-06-18T13:08:13.199805Z
|
||||
MPT_SVNVERSION=21223
|
||||
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9
|
||||
MPT_SVNDATE=2024-07-21T12:01:13.335584Z
|
||||
|
|
|
@ -7,96 +7,83 @@ set -e
|
|||
cd build 2>&1 > /dev/null || true
|
||||
cd ..
|
||||
|
||||
function download_and_unpack_tar () {
|
||||
set -e
|
||||
MPT_GET_DESTDIR="$1"
|
||||
MPT_GET_URL="$2"
|
||||
MPT_GET_FILE="$3"
|
||||
MPT_GET_SUBDIR="$4"
|
||||
if [ ! -f "$3" ]; then
|
||||
wget "$2" -O "$3"
|
||||
fi
|
||||
cd include
|
||||
if [ -d "$1" ]; then
|
||||
rm -rf "$1"
|
||||
fi
|
||||
if [ "$4" = "." ]; then
|
||||
mkdir "$1"
|
||||
cd "$1"
|
||||
tar xvaf "../../$3"
|
||||
cd ..
|
||||
else
|
||||
tar xvaf "../$3"
|
||||
if [ ! "$4" = "$1" ]; then
|
||||
mv "$4" "$1"
|
||||
fi
|
||||
fi
|
||||
cd ..
|
||||
return 0
|
||||
}
|
||||
|
||||
function download_and_unpack_zip () {
|
||||
set -e
|
||||
MPT_GET_DESTDIR="$1"
|
||||
MPT_GET_URL="$2"
|
||||
MPT_GET_FILE="$3"
|
||||
MPT_GET_SUBDIR="$4"
|
||||
if [ ! -f "$3" ]; then
|
||||
wget "$2" -O "$3"
|
||||
fi
|
||||
cd include
|
||||
if [ -d "$1" ]; then
|
||||
rm -rf "$1"
|
||||
fi
|
||||
if [ "$4" = "." ]; then
|
||||
mkdir "$1"
|
||||
cd "$1"
|
||||
unzip "../../$3"
|
||||
cd ..
|
||||
else
|
||||
unzip "../$3"
|
||||
if [ ! "$4" = "$1" ]; then
|
||||
mv "$4" "$1"
|
||||
fi
|
||||
fi
|
||||
cd ..
|
||||
return 0
|
||||
}
|
||||
|
||||
function download_and_unpack_7z () {
|
||||
set -e
|
||||
MPT_GET_DESTDIR="$1"
|
||||
MPT_GET_URL="$2"
|
||||
MPT_GET_FILE="$3"
|
||||
MPT_GET_SUBDIR="$4"
|
||||
if [ ! -f "$3" ]; then
|
||||
wget "$2" -O "$3"
|
||||
fi
|
||||
cd include
|
||||
if [ -d "$1" ]; then
|
||||
rm -rf "$1"
|
||||
fi
|
||||
if [ "$4" = "." ]; then
|
||||
mkdir "$1"
|
||||
cd "$1"
|
||||
7z x "../../$3"
|
||||
cd ..
|
||||
else
|
||||
7z x "../$3"
|
||||
if [ ! "$4" = "$1" ]; then
|
||||
mv "$4" "$1"
|
||||
fi
|
||||
fi
|
||||
cd ..
|
||||
return 0
|
||||
}
|
||||
|
||||
function download () {
|
||||
set -e
|
||||
MPT_GET_URL="$1"
|
||||
MPT_GET_FILE_NAME="$1"
|
||||
MPT_GET_FILE_SIZE="$2"
|
||||
MPT_GET_FILE_CHECKSUM="$3"
|
||||
MPT_GET_URLS="$4"
|
||||
echo "Checking '$MPT_GET_FILE_NAME' ..."
|
||||
if [ -f "$MPT_GET_FILE_NAME" ]; then
|
||||
FILE_SIZE=$(find "$MPT_GET_FILE_NAME" -printf '%s')
|
||||
if [ ! "x$FILE_SIZE" = "x$MPT_GET_FILE_SIZE" ]; then
|
||||
echo "$FILE_SIZE does not match expected file size $MPT_GET_FILE_SIZE. Redownloading."
|
||||
rm -f "$MPT_GET_FILE_NAME"
|
||||
fi
|
||||
fi
|
||||
if [ -f "$MPT_GET_FILE_NAME" ]; then
|
||||
FILE_CHECKSUM=$(sha512sum "$MPT_GET_FILE_NAME" | awk '{print $1;}')
|
||||
if [ ! "x$FILE_CHECKSUM" = "x$MPT_GET_FILE_CHECKSUM" ]; then
|
||||
echo "$FILE_CHECKSUM does not match expected file checksum $MPT_GET_FILE_CHECKSUM. Redownloading."
|
||||
rm -f "$MPT_GET_FILE_NAME"
|
||||
fi
|
||||
fi
|
||||
for URL in $MPT_GET_URLS; do
|
||||
if [ ! -f "$MPT_GET_FILE_NAME" ]; then
|
||||
echo "Downloading '$MPT_GET_FILE_NAME' from '$URL' ..."
|
||||
curl -o "$MPT_GET_FILE_NAME" "$URL"
|
||||
echo "Verifying '$URL' ..."
|
||||
if [ -f "$MPT_GET_FILE_NAME" ]; then
|
||||
FILE_SIZE=$(find "$MPT_GET_FILE_NAME" -printf '%s')
|
||||
if [ ! "x$FILE_SIZE" = "x$MPT_GET_FILE_SIZE" ]; then
|
||||
echo "$FILE_SIZE does not match expected file size $MPT_GET_FILE_SIZE."
|
||||
rm -f "$MPT_GET_FILE_NAME"
|
||||
fi
|
||||
fi
|
||||
if [ -f "$MPT_GET_FILE_NAME" ]; then
|
||||
FILE_CHECKSUM=$(sha512sum "$MPT_GET_FILE_NAME" | awk '{print $1;}')
|
||||
if [ ! "x$FILE_CHECKSUM" = "x$MPT_GET_FILE_CHECKSUM" ]; then
|
||||
echo "$FILE_CHECKSUM does not match expected file checksum $MPT_GET_FILE_CHECKSUM."
|
||||
rm -f "$MPT_GET_FILE_NAME"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ ! -f "$MPT_GET_FILE_NAME" ]; then
|
||||
echo "Failed to download '$MPT_GET_FILE_NAME'."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function unpack () {
|
||||
set -e
|
||||
MPT_GET_DESTDIR="$1"
|
||||
MPT_GET_FILE="$2"
|
||||
if [ ! -f "$2" ]; then
|
||||
wget "$1" -O "$2"
|
||||
MPT_GET_SUBDIR="$3"
|
||||
echo "Extracting '$MPT_GET_DESTDIR' from '$MPT_GET_FILE:$MPT_GET_SUBDIR' ..."
|
||||
EXTENSION="${MPT_GET_FILE##*.}"
|
||||
if [ -d "$MPT_GET_DESTDIR" ]; then
|
||||
rm -rf "$MPT_GET_DESTDIR"
|
||||
fi
|
||||
mkdir "$MPT_GET_DESTDIR"
|
||||
case "$EXTENSION" in
|
||||
tar)
|
||||
tar -xvaf "$MPT_GET_FILE" -C "$MPT_GET_DESTDIR"
|
||||
;;
|
||||
zip)
|
||||
unzip -d "$MPT_GET_DESTDIR" "$MPT_GET_FILE"
|
||||
;;
|
||||
7z)
|
||||
7z x -o"$MPT_GET_DESTDIR" "$MPT_GET_FILE"
|
||||
;;
|
||||
exe)
|
||||
7z x -o"$MPT_GET_DESTDIR" "$MPT_GET_FILE"
|
||||
;;
|
||||
esac
|
||||
if [ ! "$MPT_GET_SUBDIR" = "." ]; then
|
||||
mv "$MPT_GET_DESTDIR" "$MPT_GET_DESTDIR.tmp"
|
||||
mv "$MPT_GET_DESTDIR.tmp/$MPT_GET_SUBDIR" "$MPT_GET_DESTDIR"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
@ -108,13 +95,16 @@ if [ ! -d "build/tools" ]; then
|
|||
mkdir build/tools
|
||||
fi
|
||||
|
||||
download "build/externals/allegro-4.2.3.1-hg.8+r8500.zip" 3872466 46cd8d4d7138b795dbc66994e953d0abc578c6d3c00615e3580237458529d33d7ad9d269a9778918d4b3719d75750d5cca74ff6bf38ad357a766472799ee9e7b "https://lib.openmpt.org/files/libopenmpt/contrib/allegro/allegro-4.2.3.1-hg.8+r8500.zip"
|
||||
download "build/externals/csdpmi7b.zip" 71339 58c24691d27cead1cec92d334af551f37a3ba31de25a687d99399c28d822ec9f6ffccc9332bfce35e65dae4dd1210b54e54b223a4de17f5adcb11e2da004b834 "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7b.zip https://djgpp.mirror.garr.it/current/v2misc/csdpmi7b.zip"
|
||||
download "build/externals/csdpmi7s.zip" 89872 ea5652d31850d8eb0d15a919de0b51849f58efea0d16ad2aa4687fac4abd223d0ca34a2d1b616b02fafe84651dbef3e506df9262cfb399eb6d9909bffc89bfd3 "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7s.zip https://djgpp.mirror.garr.it/current/v2misc/csdpmi7s.zip"
|
||||
download "build/externals/WA5.55_SDK.exe" 336166 394375db8a16bf155b5de9376f6290488ab339e503dbdfdc4e2f5bede967799e625c559cca363bc988324f1a8e86e5fd28a9f697422abd7bb3dcde4a766607b5 "http://download.nullsoft.com/winamp/plugin-dev/WA5.55_SDK.exe https://web.archive.org/web/20131217072017id_/http://download.nullsoft.com/winamp/plugin-dev/WA5.55_SDK.exe"
|
||||
download "build/externals/xmp-sdk.zip" 322744 62c442d656d4bb380360368a0f5f01da11b4ed54333d7f54f875a9a5ec390b08921e00bd08e62cd7a0a5fe642e3377023f20a950cc2a42898ff4cda9ab88fc91 "https://www.un4seen.com/files/xmp-sdk.zip"
|
||||
|
||||
unpack "include/allegro42" "build/externals/allegro-4.2.3.1-hg.8+r8500.zip" "."
|
||||
unpack "include/cwsdpmi" "build/externals/csdpmi7b.zip" "."
|
||||
unpack "include/winamp" "build/externals/WA5.55_SDK.exe" "."
|
||||
unpack "include/xmplay" "build/externals/xmp-sdk.zip" "."
|
||||
|
||||
download_and_unpack_zip "allegro42" "https://lib.openmpt.org/files/libopenmpt/contrib/allegro/allegro-4.2.3.1-hg.8+r8500.zip" "build/externals/allegro-4.2.3.1-hg.8+r8500.zip" "."
|
||||
download_and_unpack_zip "cwsdpmi" "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7b.zip" "build/externals/csdpmi7b.zip" "."
|
||||
download "https://lib.openmpt.org/files/libopenmpt/contrib/djgpp/cwsdpmi/csdpmi7s.zip" "build/externals/csdpmi7s.zip"
|
||||
#download_and_unpack_zip "cwsdpmi" "https://djgpp.mirror.garr.it/current/v2misc/csdpmi7b.zip" "build/externals/csdpmi7b.zip" "."
|
||||
#download "https://djgpp.mirror.garr.it/current/v2misc/csdpmi7s.zip" "build/externals/csdpmi7s.zip"
|
||||
download_and_unpack_7z "winamp" "https://web.archive.org/web/20131217072017if_/http://download.nullsoft.com/winamp/plugin-dev/WA5.55_SDK.exe" "build/externals/WA5.55_SDK.exe" "."
|
||||
ln -s OUT.H include/winamp/Winamp/out.h
|
||||
download_and_unpack_zip "xmplay" "https://www.un4seen.com/files/xmp-sdk.zip" "build/externals/xmp-sdk.zip" "."
|
||||
|
||||
|
|
|
@ -3,13 +3,7 @@ ifeq ($(HOST),unix)
|
|||
|
||||
ifeq ($(HOST_FLAVOUR),MACOSX)
|
||||
|
||||
NO_PULSEAUDIO?=1
|
||||
include build/make/config-clang.mk
|
||||
# Mac OS X overrides
|
||||
DYNLINK=0
|
||||
SHARED_SONAME=0
|
||||
MPT_COMPILER_NOSECTIONS=1
|
||||
MPT_COMPILER_NOGCSECTIONS=1
|
||||
include build/make/config-macos.mk
|
||||
|
||||
else ifeq ($(HOST_FLAVOUR),MSYS2)
|
||||
|
||||
|
@ -38,11 +32,13 @@ include build/make/config-gcc.mk
|
|||
else ifeq ($(HOST_FLAVOUR),NETBSD)
|
||||
|
||||
include build/make/config-gcc.mk
|
||||
MPT_COMPILER_NOALLOCAH=1
|
||||
NO_PORTAUDIOCPP?=1
|
||||
|
||||
else ifeq ($(HOST_FLAVOUR),FREEBSD)
|
||||
|
||||
include build/make/config-clang.mk
|
||||
MPT_COMPILER_NOALLOCAH=1
|
||||
NO_PORTAUDIOCPP?=1
|
||||
|
||||
else ifeq ($(HOST_FLAVOUR),OPENBSD)
|
||||
|
@ -50,6 +46,7 @@ else ifeq ($(HOST_FLAVOUR),OPENBSD)
|
|||
NO_PORTAUDIOCPP?=1
|
||||
NO_PULSEAUDIO?=1
|
||||
include build/make/config-clang.mk
|
||||
MPT_COMPILER_NOALLOCAH=1
|
||||
|
||||
else ifeq ($(HOST_FLAVOUR),HAIKU)
|
||||
|
||||
|
|
|
@ -222,8 +222,8 @@ ibm/486bl := $(___) -march=i486 $(FPU_NONE) -mtune=i386
|
|||
|
||||
|
||||
|
||||
cyrix/cx486slc := $(___) -march=i386 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386)
|
||||
cyrix/cx486dlc := $(___) -march=i386 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386)
|
||||
cyrix/cx486slc := $(___) -march=i486 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386)
|
||||
cyrix/cx486dlc := $(___) -march=i486 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=1 --param l2-cache-size=$(CACHE_386)
|
||||
cyrix/cx4x86s := $(___) -march=i486 $(FPU_NONE) -mtune=i486 $(OPT_DEF) --param l1-cache-size=2 --param l2-cache-size=$(CACHE_486)
|
||||
|
||||
cyrix/cx4x86dx := $(___) -march=i486 $(FPU_387) -mtune=i486 $(OPT_DEF) --param l1-cache-size=6 --param l2-cache-size=$(CACHE_486)
|
||||
|
@ -376,6 +376,9 @@ ARFLAGS := rcs
|
|||
|
||||
OPTIMIZE_FASTMATH=1
|
||||
|
||||
# See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||
MPT_COMPILER_NOIPARA=1
|
||||
|
||||
include build/make/warnings-gcc.mk
|
||||
|
||||
DYNLINK=0
|
||||
|
@ -393,6 +396,8 @@ MPT_COMPILER_NOVISIBILITY=1
|
|||
# causes crashes on process shutdown with liballegro
|
||||
MPT_COMPILER_NOGCSECTIONS=1
|
||||
|
||||
MPT_COMPILER_NOALLOCAH=1
|
||||
|
||||
ifeq ($(OPTIMIZE_LTO),1)
|
||||
CXXFLAGS += -flto=auto -Wno-attributes
|
||||
CFLAGS += -flto=auto -Wno-attributes
|
||||
|
|
|
@ -70,6 +70,12 @@ CXXFLAGS += -flto
|
|||
CFLAGS += -flto
|
||||
LDFLAGS += -flto
|
||||
|
||||
# Work-around <https://github.com/emscripten-core/emscripten/issues/20810>.
|
||||
# The warning with emscripten 3.1.50 sounds very dangerous,
|
||||
# and since it is apparently caused by removing whitespace,
|
||||
# additional whitespace is a small price to pay for correctness.
|
||||
LDFLAGS += -g1
|
||||
|
||||
ifeq ($(EMSCRIPTEN_TARGET),default)
|
||||
# emits whatever is emscripten's default, currently (1.38.8) this is the same as "wasm" below.
|
||||
CPPFLAGS += -DMPT_BUILD_WASM
|
||||
|
@ -130,6 +136,7 @@ endif
|
|||
CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0
|
||||
CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -fno-strict-aliasing
|
||||
LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -s EXPORT_NAME="'libopenmpt'"
|
||||
SO_LDFLAGS += -s EXPORTED_FUNCTIONS="['_malloc','_free']"
|
||||
|
||||
include build/make/warnings-clang.mk
|
||||
|
||||
|
|
26
Frameworks/OpenMPT/OpenMPT/build/make/config-macos.mk
Normal file
26
Frameworks/OpenMPT/OpenMPT/build/make/config-macos.mk
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
NO_PULSEAUDIO?=1
|
||||
include build/make/config-clang.mk
|
||||
# Mac OS X overrides
|
||||
DYNLINK=0
|
||||
SHARED_SONAME=0
|
||||
MPT_COMPILER_NOSECTIONS=1
|
||||
MPT_COMPILER_NOGCSECTIONS=1
|
||||
|
||||
# 10.13 ..
|
||||
ifeq ($(MACOSX_VERSION_MIN),)
|
||||
else
|
||||
CFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN)
|
||||
CXXFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN)
|
||||
LDFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN)
|
||||
endif
|
||||
|
||||
# arm64/x86_64/i386
|
||||
ifeq ($(ARCH),)
|
||||
else
|
||||
IS_CROSS=1
|
||||
CFLAGS += -arch $(ARCH)
|
||||
CXXFLAGS += -arch $(ARCH)
|
||||
LDFLAGS += -arch $(ARCH)
|
||||
endif
|
||||
|
|
@ -13,17 +13,27 @@ else
|
|||
$(error unknown WINDOWS_ARCH)
|
||||
endif
|
||||
|
||||
ifeq ($(WINDOWS_CRT),)
|
||||
MINGW_CRT = mingw32
|
||||
else ifeq ($(WINDOWS_CRT),crtdll)
|
||||
MINGW_CRT = mingw32crt
|
||||
else ifeq ($(WINDOWS_CRT),msvcrt)
|
||||
MINGW_CRT = mingw32
|
||||
else ifeq ($(WINDOWS_CRT),ucrt)
|
||||
MINGW_CRT = mingw32ucrt
|
||||
endif
|
||||
|
||||
ifeq ($(origin CC),default)
|
||||
CC = $(MINGW_ARCH)-w64-mingw32-gcc$(MINGW_FLAVOUR)
|
||||
CC = $(MINGW_ARCH)-w64-$(MINGW_CRT)-gcc$(MINGW_FLAVOUR)
|
||||
endif
|
||||
ifeq ($(origin CXX),default)
|
||||
CXX = $(MINGW_ARCH)-w64-mingw32-g++$(MINGW_FLAVOUR)
|
||||
CXX = $(MINGW_ARCH)-w64-$(MINGW_CRT)-g++$(MINGW_FLAVOUR)
|
||||
endif
|
||||
ifeq ($(origin LD),default)
|
||||
LD = $(CXX)
|
||||
endif
|
||||
ifeq ($(origin AR),default)
|
||||
AR = $(MINGW_ARCH)-w64-mingw32-ar$(MINGW_FLAVOUR)
|
||||
AR = $(MINGW_ARCH)-w64-$(MINGW_CRT)-ar$(MINGW_FLAVOUR)
|
||||
endif
|
||||
|
||||
ifneq ($(STDCXX),)
|
||||
|
@ -107,6 +117,11 @@ else
|
|||
$(error unknown WINDOWS_VERSION)
|
||||
endif
|
||||
|
||||
ifneq ($(MINGW_COMPILER),clang)
|
||||
# See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||
MPT_COMPILER_NOIPARA=1
|
||||
endif
|
||||
|
||||
ifeq ($(MINGW_COMPILER),clang)
|
||||
include build/make/warnings-clang.mk
|
||||
else
|
||||
|
|
|
@ -73,6 +73,9 @@ CFLAGS += -march=i586 -m80387 -mtune=pentium
|
|||
|
||||
PC_LIBS_PRIVATE += -lole32 -lrpcrt4
|
||||
|
||||
# See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||
MPT_COMPILER_NOIPARA=1
|
||||
|
||||
include build/make/warnings-gcc.mk
|
||||
|
||||
EXESUFFIX=.exe
|
||||
|
|
|
@ -73,6 +73,9 @@ CFLAGS += -march=i386 -m80387 -mtune=i486
|
|||
|
||||
PC_LIBS_PRIVATE += -lole32 -lrpcrt4
|
||||
|
||||
# See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||
MPT_COMPILER_NOIPARA=1
|
||||
|
||||
include build/make/warnings-gcc.mk
|
||||
|
||||
EXESUFFIX=.exe
|
||||
|
|
|
@ -13,7 +13,7 @@ CFLAGS_WARNINGS += -Wframe-larger-than=4000
|
|||
ifeq ($(MODERN),1)
|
||||
CXXFLAGS_WARNINGS +=
|
||||
CFLAGS_WARNINGS +=
|
||||
LDFLAGS_WARNINGS += -Wl,-no-undefined -Wl,--detect-odr-violations
|
||||
LDFLAGS_WARNINGS += -Wl,-no-undefined
|
||||
endif
|
||||
|
||||
CFLAGS_SILENT += -Wno-\#warnings
|
||||
|
@ -24,6 +24,7 @@ CFLAGS_SILENT += -Wno-float-conversion
|
|||
CFLAGS_SILENT += -Wno-frame-larger-than
|
||||
CFLAGS_SILENT += -Wno-missing-prototypes
|
||||
CFLAGS_SILENT += -Wno-sign-compare
|
||||
CFLAGS_SILENT += -Wno-unused-but-set-variable
|
||||
CFLAGS_SILENT += -Wno-unused-function
|
||||
CFLAGS_SILENT += -Wno-unused-parameter
|
||||
CFLAGS_SILENT += -Wno-unused-variable
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
#pragma once
|
||||
#define OPENMPT_VERSION_SVNVERSION "19406"
|
||||
#define OPENMPT_VERSION_REVISION 19406
|
||||
#define OPENMPT_VERSION_SVNVERSION "21223"
|
||||
#define OPENMPT_VERSION_REVISION 21223
|
||||
#define OPENMPT_VERSION_DIRTY 0
|
||||
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
||||
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.2"
|
||||
#define OPENMPT_VERSION_DATE "2023-06-18T13:08:13.199805Z"
|
||||
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9"
|
||||
#define OPENMPT_VERSION_DATE "2024-07-21T12:01:13.335584Z"
|
||||
#define OPENMPT_VERSION_IS_PACKAGE 1
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; };
|
||||
82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; };
|
||||
8ADD039E1D6ACD90864371DE /* icy2utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = C3752BE686E0ED98C5849226 /* icy2utf8.c */; };
|
||||
8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */ = {isa = PBXBuildFile; fileRef = E769BB26AAD57CD8E9792166 /* lfs_wrap.c */; };
|
||||
9CA288B296644524D06346F2 /* index.c in Sources */ = {isa = PBXBuildFile; fileRef = 754AC0BABDBBA4EC025A76FA /* index.c */; };
|
||||
BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; };
|
||||
C2065F0E06550480487C3D4E /* readers.c in Sources */ = {isa = PBXBuildFile; fileRef = 489101566A5CAE88A711D796 /* readers.c */; };
|
||||
|
@ -59,6 +60,7 @@
|
|||
C6BCBF7D1D4A29EFF5C33DBD /* openmpt-mpg123.dll */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; name = "openmpt-mpg123.dll"; path = "openmpt-mpg123.dll"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CDD5DB15FEB9D30711D20955 /* compat_str.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = compat_str.c; path = ../../../include/mpg123/src/compat/compat_str.c; sourceTree = "<group>"; };
|
||||
E598BBB4FCAB56E6ECC751F4 /* icy.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = icy.c; path = ../../../include/mpg123/src/libmpg123/icy.c; sourceTree = "<group>"; };
|
||||
E769BB26AAD57CD8E9792166 /* lfs_wrap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = lfs_wrap.c; path = ../../../include/mpg123/src/libmpg123/lfs_wrap.c; sourceTree = "<group>"; };
|
||||
F4F6A4743D6788A682065AB4 /* dct64.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dct64.c; path = ../../../include/mpg123/src/libmpg123/dct64.c; sourceTree = "<group>"; };
|
||||
F56A100A0C7CAB3CFC98A64A /* id3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = id3.c; path = ../../../include/mpg123/src/libmpg123/id3.c; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
@ -112,6 +114,7 @@
|
|||
748089C69CBB42788456D006 /* layer1.c */,
|
||||
9796B208BFD16ABAA76CF848 /* layer2.c */,
|
||||
BAACDA4AE2E792FCCA83208A /* layer3.c */,
|
||||
E769BB26AAD57CD8E9792166 /* lfs_wrap.c */,
|
||||
54E0A14CA243977E1862978C /* libmpg123.c */,
|
||||
5B10DD6681370D18E83E03A6 /* ntom.c */,
|
||||
1B61528CDECD143E1D70B8CC /* optimize.c */,
|
||||
|
@ -211,6 +214,7 @@
|
|||
26299D7E974C3E704D12EBBE /* layer1.c in Sources */,
|
||||
142B46E0854DE7D23B149520 /* layer2.c in Sources */,
|
||||
022CF042734F913429163E82 /* layer3.c in Sources */,
|
||||
8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */,
|
||||
C39C716430C47FD632576FA4 /* libmpg123.c in Sources */,
|
||||
EAB0691E1B9461102EAC975E /* ntom.c in Sources */,
|
||||
0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */,
|
||||
|
@ -254,9 +258,7 @@
|
|||
SYMROOT = "../../../bin/debug/xcode4-ios/all";
|
||||
USER_HEADER_SEARCH_PATHS = (
|
||||
../../../include/mpg123/ports/Xcode,
|
||||
../../../include/mpg123/src/libmpg123,
|
||||
../../../include/mpg123/src/compat,
|
||||
../../../include/mpg123/src,
|
||||
../../../include/mpg123/src/include,
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -294,9 +296,7 @@
|
|||
SYMROOT = "../../../bin/release/xcode4-ios/all";
|
||||
USER_HEADER_SEARCH_PATHS = (
|
||||
../../../include/mpg123/ports/Xcode,
|
||||
../../../include/mpg123/src/libmpg123,
|
||||
../../../include/mpg123/src/compat,
|
||||
../../../include/mpg123/src,
|
||||
../../../include/mpg123/src/include,
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -462,6 +462,7 @@
|
|||
9E8AC865F88BDF57BFD2D6A5 /* tests_binary.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_binary.hpp; path = ../../src/mpt/binary/tests/tests_binary.hpp; sourceTree = "<group>"; };
|
||||
9EB0D073130B156590EE9EB3 /* Compressor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Compressor.h; path = ../../soundlib/plugins/dmo/Compressor.h; sourceTree = "<group>"; };
|
||||
9F187E69B9E4ED1BBDB584A9 /* outputfile.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = outputfile.hpp; path = ../../src/mpt/io_file/outputfile.hpp; sourceTree = "<group>"; };
|
||||
9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BuildSettingsCompiler.h; path = ../../common/BuildSettingsCompiler.h; sourceTree = "<group>"; };
|
||||
9FCB791A628BD98CBFC0B75A /* dos_memory.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = dos_memory.hpp; path = ../../src/mpt/osinfo/dos_memory.hpp; sourceTree = "<group>"; };
|
||||
A0ADCD61E4FC72D32723ABA1 /* MPEGFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MPEGFrame.h; path = ../../soundlib/MPEGFrame.h; sourceTree = "<group>"; };
|
||||
A105879DAD903A0F287505DD /* source_location.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = source_location.hpp; path = ../../src/mpt/base/source_location.hpp; sourceTree = "<group>"; };
|
||||
|
@ -544,6 +545,7 @@
|
|||
D89E49F2E528FC64600DC832 /* PlugInterface.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PlugInterface.cpp; path = ../../soundlib/plugins/PlugInterface.cpp; sourceTree = "<group>"; };
|
||||
D941724BCA38E03D363C608B /* saturate_round.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = saturate_round.hpp; path = ../../src/mpt/base/saturate_round.hpp; sourceTree = "<group>"; };
|
||||
DA41BBEFE6CC6E6161B13A2F /* transcode.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = transcode.hpp; path = ../../src/mpt/string_transcode/transcode.hpp; sourceTree = "<group>"; };
|
||||
DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_base_numeric.hpp; path = ../../src/mpt/base/tests/tests_base_numeric.hpp; sourceTree = "<group>"; };
|
||||
DAAB5DFD47D36C6F49665C3D /* WindowedFIR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WindowedFIR.h; path = ../../soundlib/WindowedFIR.h; sourceTree = "<group>"; };
|
||||
DC234F403388C232E596DD80 /* versionNumber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = versionNumber.h; path = ../../common/versionNumber.h; sourceTree = "<group>"; };
|
||||
DC3C845FF708F311FAD98A9F /* filecursor.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = filecursor.hpp; path = ../../src/mpt/io_read/filecursor.hpp; sourceTree = "<group>"; };
|
||||
|
@ -982,6 +984,7 @@
|
|||
4D1485535F373B4538387393 /* tests_base_arithmetic_shift.hpp */,
|
||||
44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */,
|
||||
6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */,
|
||||
DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */,
|
||||
32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */,
|
||||
A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */,
|
||||
82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */,
|
||||
|
@ -1024,6 +1027,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
A235F524F99B6816ABA98364 /* BuildSettings.h */,
|
||||
9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */,
|
||||
13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */,
|
||||
0A20B0FECCE111702A15EF3E /* ComponentManager.h */,
|
||||
EF78F5224ABA48941E149362 /* Dither.h */,
|
||||
|
@ -1749,8 +1753,7 @@
|
|||
);
|
||||
SYMROOT = "../../bin/release/xcode4-ios/all";
|
||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||
../../include/mpg123/ports/Xcode,
|
||||
../../include/mpg123/src/libmpg123,
|
||||
../../include/mpg123/src/include,
|
||||
../../include/ogg/include,
|
||||
../../include/vorbis/include,
|
||||
"$(inherited)",
|
||||
|
@ -1795,8 +1798,7 @@
|
|||
);
|
||||
SYMROOT = "../../bin/debug/xcode4-ios/all";
|
||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||
../../include/mpg123/ports/Xcode,
|
||||
../../include/mpg123/src/libmpg123,
|
||||
../../include/mpg123/src/include,
|
||||
../../include/ogg/include,
|
||||
../../include/vorbis/include,
|
||||
"$(inherited)",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; };
|
||||
82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; };
|
||||
8ADD039E1D6ACD90864371DE /* icy2utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = C3752BE686E0ED98C5849226 /* icy2utf8.c */; };
|
||||
8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */ = {isa = PBXBuildFile; fileRef = E769BB26AAD57CD8E9792166 /* lfs_wrap.c */; };
|
||||
9CA288B296644524D06346F2 /* index.c in Sources */ = {isa = PBXBuildFile; fileRef = 754AC0BABDBBA4EC025A76FA /* index.c */; };
|
||||
BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; };
|
||||
C2065F0E06550480487C3D4E /* readers.c in Sources */ = {isa = PBXBuildFile; fileRef = 489101566A5CAE88A711D796 /* readers.c */; };
|
||||
|
@ -59,6 +60,7 @@
|
|||
C6BCBF7D1D4A29EFF5C33DBD /* openmpt-mpg123.dll */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; name = "openmpt-mpg123.dll"; path = "openmpt-mpg123.dll"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CDD5DB15FEB9D30711D20955 /* compat_str.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = compat_str.c; path = ../../../include/mpg123/src/compat/compat_str.c; sourceTree = "<group>"; };
|
||||
E598BBB4FCAB56E6ECC751F4 /* icy.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = icy.c; path = ../../../include/mpg123/src/libmpg123/icy.c; sourceTree = "<group>"; };
|
||||
E769BB26AAD57CD8E9792166 /* lfs_wrap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = lfs_wrap.c; path = ../../../include/mpg123/src/libmpg123/lfs_wrap.c; sourceTree = "<group>"; };
|
||||
F4F6A4743D6788A682065AB4 /* dct64.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dct64.c; path = ../../../include/mpg123/src/libmpg123/dct64.c; sourceTree = "<group>"; };
|
||||
F56A100A0C7CAB3CFC98A64A /* id3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = id3.c; path = ../../../include/mpg123/src/libmpg123/id3.c; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
@ -112,6 +114,7 @@
|
|||
748089C69CBB42788456D006 /* layer1.c */,
|
||||
9796B208BFD16ABAA76CF848 /* layer2.c */,
|
||||
BAACDA4AE2E792FCCA83208A /* layer3.c */,
|
||||
E769BB26AAD57CD8E9792166 /* lfs_wrap.c */,
|
||||
54E0A14CA243977E1862978C /* libmpg123.c */,
|
||||
5B10DD6681370D18E83E03A6 /* ntom.c */,
|
||||
1B61528CDECD143E1D70B8CC /* optimize.c */,
|
||||
|
@ -211,6 +214,7 @@
|
|||
26299D7E974C3E704D12EBBE /* layer1.c in Sources */,
|
||||
142B46E0854DE7D23B149520 /* layer2.c in Sources */,
|
||||
022CF042734F913429163E82 /* layer3.c in Sources */,
|
||||
8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */,
|
||||
C39C716430C47FD632576FA4 /* libmpg123.c in Sources */,
|
||||
EAB0691E1B9461102EAC975E /* ntom.c in Sources */,
|
||||
0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */,
|
||||
|
@ -254,9 +258,7 @@
|
|||
SYMROOT = "../../../bin/debug/xcode4-macosx/all";
|
||||
USER_HEADER_SEARCH_PATHS = (
|
||||
../../../include/mpg123/ports/Xcode,
|
||||
../../../include/mpg123/src/libmpg123,
|
||||
../../../include/mpg123/src/compat,
|
||||
../../../include/mpg123/src,
|
||||
../../../include/mpg123/src/include,
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -294,9 +296,7 @@
|
|||
SYMROOT = "../../../bin/release/xcode4-macosx/all";
|
||||
USER_HEADER_SEARCH_PATHS = (
|
||||
../../../include/mpg123/ports/Xcode,
|
||||
../../../include/mpg123/src/libmpg123,
|
||||
../../../include/mpg123/src/compat,
|
||||
../../../include/mpg123/src,
|
||||
../../../include/mpg123/src/include,
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -462,6 +462,7 @@
|
|||
9E8AC865F88BDF57BFD2D6A5 /* tests_binary.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_binary.hpp; path = ../../src/mpt/binary/tests/tests_binary.hpp; sourceTree = "<group>"; };
|
||||
9EB0D073130B156590EE9EB3 /* Compressor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Compressor.h; path = ../../soundlib/plugins/dmo/Compressor.h; sourceTree = "<group>"; };
|
||||
9F187E69B9E4ED1BBDB584A9 /* outputfile.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = outputfile.hpp; path = ../../src/mpt/io_file/outputfile.hpp; sourceTree = "<group>"; };
|
||||
9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BuildSettingsCompiler.h; path = ../../common/BuildSettingsCompiler.h; sourceTree = "<group>"; };
|
||||
9FCB791A628BD98CBFC0B75A /* dos_memory.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = dos_memory.hpp; path = ../../src/mpt/osinfo/dos_memory.hpp; sourceTree = "<group>"; };
|
||||
A0ADCD61E4FC72D32723ABA1 /* MPEGFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MPEGFrame.h; path = ../../soundlib/MPEGFrame.h; sourceTree = "<group>"; };
|
||||
A105879DAD903A0F287505DD /* source_location.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = source_location.hpp; path = ../../src/mpt/base/source_location.hpp; sourceTree = "<group>"; };
|
||||
|
@ -544,6 +545,7 @@
|
|||
D89E49F2E528FC64600DC832 /* PlugInterface.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PlugInterface.cpp; path = ../../soundlib/plugins/PlugInterface.cpp; sourceTree = "<group>"; };
|
||||
D941724BCA38E03D363C608B /* saturate_round.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = saturate_round.hpp; path = ../../src/mpt/base/saturate_round.hpp; sourceTree = "<group>"; };
|
||||
DA41BBEFE6CC6E6161B13A2F /* transcode.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = transcode.hpp; path = ../../src/mpt/string_transcode/transcode.hpp; sourceTree = "<group>"; };
|
||||
DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests_base_numeric.hpp; path = ../../src/mpt/base/tests/tests_base_numeric.hpp; sourceTree = "<group>"; };
|
||||
DAAB5DFD47D36C6F49665C3D /* WindowedFIR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WindowedFIR.h; path = ../../soundlib/WindowedFIR.h; sourceTree = "<group>"; };
|
||||
DC234F403388C232E596DD80 /* versionNumber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = versionNumber.h; path = ../../common/versionNumber.h; sourceTree = "<group>"; };
|
||||
DC3C845FF708F311FAD98A9F /* filecursor.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = filecursor.hpp; path = ../../src/mpt/io_read/filecursor.hpp; sourceTree = "<group>"; };
|
||||
|
@ -982,6 +984,7 @@
|
|||
4D1485535F373B4538387393 /* tests_base_arithmetic_shift.hpp */,
|
||||
44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */,
|
||||
6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */,
|
||||
DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */,
|
||||
32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */,
|
||||
A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */,
|
||||
82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */,
|
||||
|
@ -1024,6 +1027,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
A235F524F99B6816ABA98364 /* BuildSettings.h */,
|
||||
9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */,
|
||||
13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */,
|
||||
0A20B0FECCE111702A15EF3E /* ComponentManager.h */,
|
||||
EF78F5224ABA48941E149362 /* Dither.h */,
|
||||
|
@ -1749,8 +1753,7 @@
|
|||
);
|
||||
SYMROOT = "../../bin/release/xcode4-macosx/all";
|
||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||
../../include/mpg123/ports/Xcode,
|
||||
../../include/mpg123/src/libmpg123,
|
||||
../../include/mpg123/src/include,
|
||||
../../include/ogg/include,
|
||||
../../include/vorbis/include,
|
||||
"$(inherited)",
|
||||
|
@ -1795,8 +1798,7 @@
|
|||
);
|
||||
SYMROOT = "../../bin/debug/xcode4-macosx/all";
|
||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||
../../include/mpg123/ports/Xcode,
|
||||
../../include/mpg123/src/libmpg123,
|
||||
../../include/mpg123/src/include,
|
||||
../../include/ogg/include,
|
||||
../../include/vorbis/include,
|
||||
"$(inherited)",
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
#endif
|
||||
|
||||
|
||||
#include "BuildSettingsCompiler.h"
|
||||
|
||||
|
||||
|
||||
#include "mpt/base/detect_compiler.hpp"
|
||||
#include "mpt/base/detect_os.hpp"
|
||||
#include "mpt/base/detect_quirks.hpp"
|
||||
|
@ -366,46 +370,6 @@
|
|||
|
||||
|
||||
|
||||
// compiler configuration
|
||||
|
||||
#if MPT_COMPILER_MSVC
|
||||
|
||||
#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
|
||||
|
||||
|
||||
|
||||
// third-party library configuration
|
||||
|
||||
#ifdef MPT_WITH_STBVORBIS
|
||||
|
|
73
Frameworks/OpenMPT/OpenMPT/common/BuildSettingsCompiler.h
Normal file
73
Frameworks/OpenMPT/OpenMPT/common/BuildSettingsCompiler.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* BuildSettingsCompiler.h
|
||||
* -----------------------
|
||||
* Purpose: Global compiler setting overrides
|
||||
* 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"
|
||||
|
||||
|
||||
|
||||
// compiler configuration
|
||||
|
||||
#if MPT_COMPILER_MSVC
|
||||
|
||||
#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_GCC
|
||||
|
||||
#ifdef MPT_COMPILER_SETTING_QUIRK_GCC_BROKEN_IPA
|
||||
// See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||
#if MPT_GCC_BEFORE(9, 0, 0)
|
||||
// Earlier GCC get confused about setting a global function attribute.
|
||||
// We need to check for 9.0 instead of 9.1 because of
|
||||
// <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1028580>.
|
||||
// It also gets confused when setting global optimization -O1,
|
||||
// so we have no way of fixing GCC 8 or earlier.
|
||||
//#pragma GCC optimize("O1")
|
||||
#else
|
||||
#pragma GCC optimize("no-ipa-ra")
|
||||
#endif
|
||||
#endif // MPT_COMPILER_SETTING_QUIRK_GCC_BROKEN_IPA
|
||||
|
||||
#endif // MPT_COMPILER_GCC
|
||||
|
||||
#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
|
|
@ -55,6 +55,16 @@ mpt::thread_safe_prng<mpt::default_prng> & global_prng()
|
|||
return g_global_prng;
|
||||
}
|
||||
|
||||
#ifdef MPT_BUILD_FUZZER
|
||||
void reinit_global_random()
|
||||
{
|
||||
global_prng().~thread_safe_prng<mpt::default_prng>();
|
||||
global_random_device().~random_device();
|
||||
new(&global_random_device()) mpt::random_device{};
|
||||
new(&global_prng()) thread_safe_prng<mpt::default_prng>{global_random_device()};
|
||||
}
|
||||
#endif // MPT_BUILD_FUZZER
|
||||
|
||||
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
|
||||
|
||||
} // namespace mpt
|
||||
|
|
|
@ -128,6 +128,10 @@ using default_prng = mpt::good_prng;
|
|||
mpt::random_device & global_random_device();
|
||||
mpt::thread_safe_prng<mpt::default_prng> & global_prng();
|
||||
|
||||
#ifdef MPT_BUILD_FUZZER
|
||||
void reinit_global_random();
|
||||
#endif // MPT_BUILD_FUZZER
|
||||
|
||||
#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<mpt::default_prng> *rng);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "mptStringBuffer.h"
|
||||
|
||||
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
|
||||
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
|
||||
#include <chrono>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include "openmpt/all/BuildSettings.hpp"
|
||||
|
||||
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
|
||||
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#endif
|
||||
|
@ -175,7 +175,7 @@ Local UnixAsLocal(Unix tp);
|
|||
|
||||
} // namespace nochrono
|
||||
|
||||
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
|
||||
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
|
||||
|
||||
using Unix = std::chrono::system_clock::time_point;
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@
|
|||
#endif
|
||||
|
||||
|
||||
#include "mpt/base/span.hpp"
|
||||
#include "mpt/check/compiler.hpp"
|
||||
#include "mpt/check/libc.hpp"
|
||||
#if defined(MPT_WITH_MFC)
|
||||
|
@ -73,6 +72,8 @@
|
|||
#if MPT_OS_WINDOWS
|
||||
#include "mpt/check/windows.hpp"
|
||||
#endif
|
||||
|
||||
#include "mpt/base/span.hpp"
|
||||
#include "mpt/exception/exception.hpp"
|
||||
#include "mpt/exception/exception_text.hpp"
|
||||
#include "mpt/out_of_memory/out_of_memory.hpp"
|
||||
|
|
|
@ -600,12 +600,12 @@ mpt::ustring GetFullCreditsString()
|
|||
"libopenmpt (based on OpenMPT / Open ModPlug Tracker)\n"
|
||||
#endif
|
||||
"\n"
|
||||
"Copyright \xC2\xA9 2004-2023 OpenMPT Project Developers and Contributors\n"
|
||||
"Copyright \xC2\xA9 2004-2024 OpenMPT Project Developers and Contributors\n"
|
||||
"Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n"
|
||||
"\n"
|
||||
"Developers:\n"
|
||||
"Johannes Schultz (2008-2023)\n"
|
||||
"J\xC3\xB6rn Heusipp (2012-2023)\n"
|
||||
"Johannes Schultz (2008-2024)\n"
|
||||
"J\xC3\xB6rn Heusipp (2012-2024)\n"
|
||||
"Ahti Lepp\xC3\xA4nen (2005-2011)\n"
|
||||
"Robin Fernandes (2004-2007)\n"
|
||||
"Sergiy Pylypenko (2007)\n"
|
||||
|
@ -776,7 +776,7 @@ mpt::ustring GetFullCreditsString()
|
|||
"\n"
|
||||
#endif
|
||||
"Daniel Collin (emoon/TBL) for providing test infrastructure\n"
|
||||
"https://twitter.com/daniel_collin\n"
|
||||
"https://mastodon.gamedev.place/@daniel_collin\n"
|
||||
"\n"
|
||||
"The people in the ModPlug community for crucial contribution\n"
|
||||
"in the form of ideas, testing and support;\n"
|
||||
|
@ -800,7 +800,7 @@ mpt::ustring GetFullCreditsString()
|
|||
mpt::ustring GetLicenseString()
|
||||
{
|
||||
return MPT_UTF8(
|
||||
"Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors" "\n"
|
||||
"Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors" "\n"
|
||||
"Copyright (c) 1997-2003, Olivier Lapicque" "\n"
|
||||
"All rights reserved." "\n"
|
||||
"" "\n"
|
||||
|
|
|
@ -205,7 +205,7 @@ MPT_CONSTEXPRINLINE bool operator > (const Version &a, const Version &b) noexcep
|
|||
}
|
||||
|
||||
|
||||
MPT_CONSTEVAL Version operator "" _LiteralVersionImpl (const char * str, std::size_t len)
|
||||
MPT_CONSTEVAL Version operator ""_LiteralVersionImpl (const char * str, std::size_t len)
|
||||
{
|
||||
return Version::LiteralParser::Parse(str, len);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN
|
|||
// Version definitions. The only thing that needs to be changed when changing version number.
|
||||
#define VER_MAJORMAJOR 1
|
||||
#define VER_MAJOR 31
|
||||
#define VER_MINOR 03
|
||||
#define VER_MINOR 09
|
||||
#define VER_MINORMINOR 00
|
||||
|
||||
OPENMPT_NAMESPACE_END
|
||||
|
|
|
@ -8,7 +8,7 @@ amf="AMF\x0A"
|
|||
amf="DMF\x0E"
|
||||
|
||||
ams="Extreme"
|
||||
ams="AMShdr\x1A\x02\x02"
|
||||
ams="AMShdr\x1A\x00\x02\x02"
|
||||
|
||||
#dbm="DBM0"
|
||||
dbm="NAME"
|
||||
|
@ -54,7 +54,7 @@ far="\x0D\x0A\x1A"
|
|||
fmt="FMTracker\x01\x01"
|
||||
|
||||
gdm="GDM\xFE"
|
||||
gdm="GMFS"
|
||||
gdm="\x0D\x0A\x1AGMFS\x01\x00"
|
||||
|
||||
gtk="GTK\x04"
|
||||
|
||||
|
@ -227,7 +227,7 @@ psm16="PSAH"
|
|||
psm16="PPAT"
|
||||
|
||||
ptm="PTMF"
|
||||
ptm="\x1A\x03\x02"
|
||||
ptm="\x1A\x03\x02\x00"
|
||||
|
||||
s3m="SCRM"
|
||||
#s3m="SCRS"
|
||||
|
|
|
@ -10,4 +10,4 @@ mkdir $FUZZING_TEMPDIR/bin
|
|||
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
|
||||
|
||||
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01
|
||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz
|
||||
|
|
|
@ -3,4 +3,4 @@ cd "${0%/*}"
|
|||
. ./fuzz-settings.sh
|
||||
|
||||
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02
|
||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz
|
||||
|
|
|
@ -3,4 +3,4 @@ cd "${0%/*}"
|
|||
. ./fuzz-settings.sh
|
||||
|
||||
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -f $FUZZING_TEMPDIR/infile03 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile03
|
||||
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* fuzz.c
|
||||
* ------
|
||||
* Purpose: Tiny libopenmpt user to be used by fuzzing tools
|
||||
* Notes : (currently none)
|
||||
* Authors: OpenMPT Devs
|
||||
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||||
*/
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libopenmpt/libopenmpt.h>
|
||||
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
|
||||
|
||||
#define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512)
|
||||
#define SAMPLERATE 22050
|
||||
|
||||
static int16_t buffer[BUFFERSIZE];
|
||||
|
||||
static int ErrFunc (int error, void *)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case OPENMPT_ERROR_INVALID_ARGUMENT:
|
||||
case OPENMPT_ERROR_OUT_OF_RANGE:
|
||||
case OPENMPT_ERROR_LENGTH:
|
||||
case OPENMPT_ERROR_DOMAIN:
|
||||
case OPENMPT_ERROR_LOGIC:
|
||||
case OPENMPT_ERROR_UNDERFLOW:
|
||||
case OPENMPT_ERROR_OVERFLOW:
|
||||
case OPENMPT_ERROR_RANGE:
|
||||
case OPENMPT_ERROR_RUNTIME:
|
||||
case OPENMPT_ERROR_EXCEPTION:
|
||||
abort();
|
||||
default:
|
||||
return OPENMPT_ERROR_FUNC_RESULT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char * argv[] ) {
|
||||
static FILE * file = NULL;
|
||||
static openmpt_module * mod = NULL;
|
||||
static size_t count = 0;
|
||||
static int i = 0;
|
||||
(void)argc;
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
__AFL_INIT();
|
||||
#endif
|
||||
file = fopen( argv[1], "rb" );
|
||||
mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, NULL, NULL, ErrFunc, NULL, NULL, NULL, NULL );
|
||||
fclose( file );
|
||||
if ( mod == NULL )
|
||||
return 1;
|
||||
|
||||
// verify API contract : If the file can be loaded, header probing must be successful too.
|
||||
if ( openmpt_probe_file_header_from_stream( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, openmpt_stream_get_file_callbacks(), file, NULL, NULL, ErrFunc, NULL, NULL, NULL ) == OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE )
|
||||
abort();
|
||||
|
||||
openmpt_module_ctl_set( mod, "render.resampler.emulate_amiga", (openmpt_module_get_num_orders( mod ) & 1) ? "0" : "1" );
|
||||
// render about a second of the module for fuzzing the actual mix routines
|
||||
for(; i < 50; i++) {
|
||||
count = openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||
if ( count == 0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
openmpt_module_set_position_seconds( mod, 1.0 );
|
||||
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||
openmpt_module_set_position_order_row( mod, 3, 16 );
|
||||
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||
|
||||
// fuzz string-related stuff
|
||||
openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) );
|
||||
openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) );
|
||||
openmpt_module_destroy( mod );
|
||||
return 0;
|
||||
}
|
88
Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.cpp
Normal file
88
Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* fuzz.cpp
|
||||
* --------
|
||||
* Purpose: Tiny libopenmpt user to be used by fuzzing tools
|
||||
* Notes : (currently none)
|
||||
* Authors: OpenMPT Devs
|
||||
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libopenmpt/libopenmpt.h>
|
||||
|
||||
#include "../../common/mptRandom.h"
|
||||
|
||||
#define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512)
|
||||
#define SAMPLERATE 22050
|
||||
|
||||
static int16_t buffer[BUFFERSIZE];
|
||||
|
||||
static int ErrFunc (int error, void *)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case OPENMPT_ERROR_INVALID_ARGUMENT:
|
||||
case OPENMPT_ERROR_OUT_OF_RANGE:
|
||||
case OPENMPT_ERROR_LENGTH:
|
||||
case OPENMPT_ERROR_DOMAIN:
|
||||
case OPENMPT_ERROR_LOGIC:
|
||||
case OPENMPT_ERROR_UNDERFLOW:
|
||||
case OPENMPT_ERROR_OVERFLOW:
|
||||
case OPENMPT_ERROR_RANGE:
|
||||
case OPENMPT_ERROR_RUNTIME:
|
||||
case OPENMPT_ERROR_EXCEPTION:
|
||||
std::abort();
|
||||
default:
|
||||
return OPENMPT_ERROR_FUNC_RESULT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
__AFL_FUZZ_INIT();
|
||||
|
||||
int main( int argc, char * argv[] ) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
openmpt_module_create_from_memory2( buffer, BUFFERSIZE, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr );
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
__AFL_INIT();
|
||||
#endif
|
||||
|
||||
unsigned char *fileBuffer = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT and before __AFL_LOOP!
|
||||
|
||||
while (__AFL_LOOP(10000)) {
|
||||
int fileSize = __AFL_FUZZ_TESTCASE_LEN;
|
||||
OpenMPT::mpt::reinit_global_random();
|
||||
openmpt_module * mod = openmpt_module_create_from_memory2( fileBuffer, fileSize, nullptr, nullptr, ErrFunc, nullptr, nullptr, nullptr, nullptr);
|
||||
if ( mod == NULL )
|
||||
break;
|
||||
|
||||
// verify API contract: If the file can be loaded, header probing must be successful too.
|
||||
if ( openmpt_probe_file_header( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, fileBuffer, fileSize, fileSize, nullptr, nullptr, ErrFunc, nullptr, nullptr, nullptr ) == OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE )
|
||||
std::abort();
|
||||
|
||||
openmpt_module_ctl_set( mod, "render.resampler.emulate_amiga", (openmpt_module_get_num_orders( mod ) & 1) ? "0" : "1" );
|
||||
// render about a second of the module for fuzzing the actual mix routines
|
||||
for(int i = 0; i < 50; i++) {
|
||||
size_t count = openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||
if ( count == 0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
openmpt_module_set_position_seconds( mod, 1.0 );
|
||||
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||
openmpt_module_set_position_order_row( mod, 3, 16 );
|
||||
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
|
||||
|
||||
// fuzz string-related stuff
|
||||
openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) );
|
||||
openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) );
|
||||
openmpt_module_destroy( mod );
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -12,7 +12,7 @@ wget $AFL_URL || exit
|
|||
tar -xzvf $AFL_FILENAME
|
||||
rm $AFL_FILENAME
|
||||
cd AFLplusplus-*
|
||||
make source-only || exit
|
||||
make PERFORMANCE=1 source-only || exit
|
||||
cd ..
|
||||
rm -rf afl
|
||||
mv AFLplusplus-* afl
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors
|
||||
Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors
|
||||
Copyright (c) 1997-2003, Olivier Lapicque
|
||||
All rights reserved.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors
|
||||
Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors
|
||||
Copyright (c) 1997-2003, Olivier Lapicque
|
||||
All rights reserved.
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ problems can happen at:
|
|||
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),
|
||||
* [IRC channel (`Libera.Chat/#openmpt`)](ircs://irc.libera.chat:6697/#openmpt),
|
||||
preferred for shorter questions
|
||||
* [GitHub pull requests](https://github.com/OpenMPT/openmpt/pulls), please
|
||||
only use for rather tiny fixes, see below
|
||||
|
|
|
@ -51,12 +51,17 @@
|
|||
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
|
||||
#endif
|
||||
|
||||
#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#if defined( __clang__ )
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
||||
#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#endif
|
||||
#include <portaudio.h>
|
||||
#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#if defined( __clang__ )
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
|
|
@ -23,12 +23,17 @@
|
|||
|
||||
#include <libopenmpt/libopenmpt.h>
|
||||
|
||||
#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#if defined( __clang__ )
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
||||
#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#endif
|
||||
#include <portaudio.h>
|
||||
#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#if defined( __clang__ )
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
|
|
@ -25,12 +25,17 @@
|
|||
#include <libopenmpt/libopenmpt.h>
|
||||
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
|
||||
|
||||
#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#if defined( __clang__ )
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
||||
#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#endif
|
||||
#include <portaudio.h>
|
||||
#if defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#if defined( __clang__ )
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined( __GNUC__ ) && !defined( __clang__ ) && !defined( _MSC_VER )
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1370,7 +1370,7 @@ Declare Function openmpt_module_highlight_pattern_row_channel_ Alias "openmpt_mo
|
|||
- subsong: 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. The song end is considered to be reached after the number of reptitions set by openmpt_module_set_repeat_count was played, so if the song is set to repeat infinitely, its end is never considered to be 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 loop start (if the song is not programmed to loop, playback resumsed from the song start).
|
||||
- "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumed from the song 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.
|
||||
|
|
|
@ -223,7 +223,7 @@ static void config( HWND hwndParent ) {
|
|||
static void about( HWND hwndParent ) {
|
||||
std::ostringstream about;
|
||||
about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
|
||||
about << " Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl;
|
||||
about << " Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl;
|
||||
about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl;
|
||||
about << std::endl;
|
||||
about << openmpt::string::get( "contact" ) << std::endl;
|
||||
|
|
|
@ -1421,7 +1421,7 @@ LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmp
|
|||
* - 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. The song end is considered to be reached after the number of reptitions set by openmpt_module_set_repeat_count was played, so if the song is set to repeat infinitely, its end is never considered to be 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 loop start (if the song is not programmed to loop, playback resumsed from the song start).
|
||||
* - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumed from the song 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.
|
||||
|
|
|
@ -1074,7 +1074,7 @@ public:
|
|||
- 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. The song end is considered to be reached after the number of reptitions set by openmpt::module::set_repeat_count was played, so if the song is set to repeat infinitely, its end is never considered to be 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 loop start (if the song is not programmed to loop, playback resumsed from the song start).
|
||||
- "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the loop start (if the song is not programmed to loop, playback resumed from the song 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.
|
||||
|
|
|
@ -1115,7 +1115,7 @@ double module_impl::set_position_seconds( double seconds ) {
|
|||
subsong = &subsongs[i];
|
||||
break;
|
||||
}
|
||||
base_seconds += subsong->duration;
|
||||
base_seconds += subsongs[i].duration;
|
||||
}
|
||||
seconds -= base_seconds;
|
||||
} else {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/*! \brief libopenmpt minor version number */
|
||||
#define OPENMPT_API_VERSION_MINOR 7
|
||||
/*! \brief libopenmpt patch version number */
|
||||
#define OPENMPT_API_VERSION_PATCH 2
|
||||
#define OPENMPT_API_VERSION_PATCH 9
|
||||
/*! \brief libopenmpt pre-release tag */
|
||||
#define OPENMPT_API_VERSION_PREREL ""
|
||||
/*! \brief libopenmpt pre-release flag */
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
LIBOPENMPT_VERSION_MAJOR=0
|
||||
LIBOPENMPT_VERSION_MINOR=7
|
||||
LIBOPENMPT_VERSION_PATCH=2
|
||||
LIBOPENMPT_VERSION_PATCH=9
|
||||
LIBOPENMPT_VERSION_PREREL=
|
||||
|
||||
LIBOPENMPT_LTVER_CURRENT=4
|
||||
LIBOPENMPT_LTVER_REVISION=2
|
||||
LIBOPENMPT_LTVER_REVISION=9
|
||||
LIBOPENMPT_LTVER_AGE=4
|
||||
|
|
|
@ -192,7 +192,7 @@ BEGIN
|
|||
VALUE "FileDescription", VER_FILEDESC_STR
|
||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||
VALUE "InternalName", VER_FILENAME_STR
|
||||
VALUE "LegalCopyright", "Copyright © 2004-2023 OpenMPT Project Developers and Contributors, Copyright © 1997-2003 Olivier Lapicque"
|
||||
VALUE "LegalCopyright", "Copyright © 2004-2024 OpenMPT Project Developers and Contributors, Copyright © 1997-2003 Olivier Lapicque"
|
||||
VALUE "OriginalFilename", VER_FILENAME_STR
|
||||
VALUE "ProductName", "libopenmpt"
|
||||
VALUE "ProductVersion", VER_FILEVERSION_STR
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <numeric>
|
||||
|
||||
#include <tchar.h>
|
||||
|
||||
|
@ -244,6 +245,9 @@ static std::string StringUpperCase( std::string str ) {
|
|||
}
|
||||
|
||||
static std::string seconds_to_string( double time ) {
|
||||
if ( !std::isnormal( time ) ) {
|
||||
return "?";
|
||||
}
|
||||
std::int64_t time_ms = static_cast<std::int64_t>( time * 1000 );
|
||||
std::int64_t seconds = ( time_ms / 1000 ) % 60;
|
||||
std::int64_t minutes = ( time_ms / ( 1000 * 60 ) ) % 60;
|
||||
|
@ -485,7 +489,7 @@ static void clear_current_timeinfo() {
|
|||
static void WINAPI openmpt_About( HWND win ) {
|
||||
std::ostringstream about;
|
||||
about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
|
||||
about << " Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl;
|
||||
about << " Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl;
|
||||
about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl;
|
||||
about << std::endl;
|
||||
about << openmpt::string::get( "contact" ) << std::endl;
|
||||
|
@ -551,10 +555,10 @@ std::streambuf::int_type xmplay_streambuf::underflow() {
|
|||
start += put_back_count;
|
||||
}
|
||||
std::size_t n = xmpffile->Read( file, start, buffer.size() - ( start - base ) );
|
||||
setg( base, start, start + n );
|
||||
if ( n == 0 ) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
setg( base, start, start + n );
|
||||
return traits_type::to_int_type( *gptr() );
|
||||
}
|
||||
|
||||
|
@ -707,14 +711,11 @@ static char * build_xmplay_tags( const openmpt::module & mod, int32_t subsong =
|
|||
if ( subsong >= 0 && static_cast<size_t>( subsong ) < subsong_names.size() ) {
|
||||
first_subsong += subsong;
|
||||
last_subsong = first_subsong + 1;
|
||||
} else
|
||||
{
|
||||
last_subsong = first_subsong + 1;
|
||||
}
|
||||
|
||||
for ( auto subsong_name = first_subsong; subsong_name != last_subsong; subsong_name++ ) {
|
||||
append_xmplay_tag( tags, "filetype", convert_to_native( StringUpperCase( mod.get_metadata( "type" ) ) ) );
|
||||
append_xmplay_tag( tags, "title", convert_to_native( ( subsong_name->empty() || subsong == -1 ) ? title : *subsong_name ) );
|
||||
append_xmplay_tag( tags, "title", convert_to_native( ( subsong_name->empty() || subsong == -1 || subsong_names.size() == 1 ) ? title : *subsong_name ) );
|
||||
append_xmplay_tag( tags, "artist", convert_to_native( mod.get_metadata( "artist" ) ) );
|
||||
append_xmplay_tag( tags, "album", convert_to_native( mod.get_metadata( "xmplay-album" ) ) ); // todo, libopenmpt does not support that
|
||||
append_xmplay_tag( tags, "date", convert_to_native( extract_date( mod ) ) );
|
||||
|
@ -731,17 +732,38 @@ static char * build_xmplay_tags( const openmpt::module & mod, int32_t subsong =
|
|||
return result;
|
||||
}
|
||||
|
||||
static float * build_xmplay_length( const openmpt::module & /* mod */ ) {
|
||||
float * result = static_cast<float*>( xmpfmisc->Alloc( sizeof( float ) * self->subsong_lengths.size() ) );
|
||||
static std::vector<double> build_subsong_lengths( openmpt::module & mod ) {
|
||||
std::int32_t num_subsongs = mod.get_num_subsongs();
|
||||
std::vector<double> subsong_lengths( num_subsongs );
|
||||
for ( std::int32_t i = 0; i < num_subsongs; ++i ) {
|
||||
mod.select_subsong( i );
|
||||
subsong_lengths[i] = mod.get_duration_seconds();
|
||||
}
|
||||
return subsong_lengths;
|
||||
}
|
||||
|
||||
static float * build_xmplay_length( openmpt::module & mod ) {
|
||||
const auto subsong_lengths = build_subsong_lengths( mod );
|
||||
float * result = static_cast<float*>( xmpfmisc->Alloc( sizeof( float ) * subsong_lengths.size() ) );
|
||||
if ( !result ) {
|
||||
return nullptr;
|
||||
}
|
||||
for ( std::size_t i = 0; i < self->subsong_lengths.size(); ++i ) {
|
||||
result[i] = static_cast<float>( self->subsong_lengths[i] );
|
||||
for ( std::size_t i = 0; i < subsong_lengths.size(); ++i ) {
|
||||
result[i] = static_cast<float>( subsong_lengths[i] );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static DWORD build_xmplay_file_info( openmpt::module & mod, float ** length, char ** tags ) {
|
||||
if ( length ) {
|
||||
*length = build_xmplay_length( mod );
|
||||
}
|
||||
if ( tags ) {
|
||||
*tags = build_xmplay_tags( mod );
|
||||
}
|
||||
return static_cast<DWORD>( mod.get_num_subsongs() );
|
||||
}
|
||||
|
||||
static void clear_xmplay_string( char * str ) {
|
||||
if ( !str ) {
|
||||
return;
|
||||
|
@ -851,6 +873,7 @@ static BOOL WINAPI openmpt_CheckFile( const char * filename, XMPFILE file ) {
|
|||
|
||||
static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, float * * length, char * * tags ) {
|
||||
static_cast<void>( filename );
|
||||
DWORD subsongs = 0;
|
||||
try {
|
||||
std::map< std::string, std::string > ctls
|
||||
{
|
||||
|
@ -863,12 +886,7 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl
|
|||
case XMPFILE_TYPE_MEMORY:
|
||||
{
|
||||
openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls );
|
||||
if ( length ) {
|
||||
*length = build_xmplay_length( mod );
|
||||
}
|
||||
if ( tags ) {
|
||||
*tags = build_xmplay_tags( mod );
|
||||
}
|
||||
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||
}
|
||||
break;
|
||||
case XMPFILE_TYPE_FILE:
|
||||
|
@ -878,50 +896,30 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl
|
|||
{
|
||||
xmplay_istream s( file );
|
||||
openmpt::module mod( s, std::clog, ctls );
|
||||
if ( length ) {
|
||||
*length = build_xmplay_length( mod );
|
||||
}
|
||||
if ( tags ) {
|
||||
*tags = build_xmplay_tags( mod );
|
||||
}
|
||||
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if ( xmpffile->GetType( file ) == XMPFILE_TYPE_MEMORY ) {
|
||||
openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls );
|
||||
if ( length ) {
|
||||
*length = build_xmplay_length( mod );
|
||||
}
|
||||
if ( tags ) {
|
||||
*tags = build_xmplay_tags( mod );
|
||||
}
|
||||
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||
} else {
|
||||
openmpt::module mod( read_XMPFILE_vector( file ), std::clog, ctls );
|
||||
if ( length ) {
|
||||
*length = build_xmplay_length( mod );
|
||||
}
|
||||
if ( tags ) {
|
||||
*tags = build_xmplay_tags( mod );
|
||||
}
|
||||
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
std::ifstream s( filename, std::ios_base::binary );
|
||||
openmpt::module mod( s, std::clog, ctls );
|
||||
if ( length ) {
|
||||
*length = build_xmplay_length( mod );
|
||||
}
|
||||
if ( tags ) {
|
||||
*tags = build_xmplay_tags( mod );
|
||||
}
|
||||
#endif
|
||||
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||
#endif
|
||||
} catch ( ... ) {
|
||||
if ( length ) *length = nullptr;
|
||||
if ( tags ) *tags = nullptr;
|
||||
return 0;
|
||||
}
|
||||
return self->subsong_lengths.size() + XMPIN_INFO_NOSUBTAGS;
|
||||
return subsongs;
|
||||
}
|
||||
|
||||
// open a file for playback
|
||||
|
@ -967,12 +965,7 @@ static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) {
|
|||
reset_timeinfos();
|
||||
apply_options();
|
||||
|
||||
std::int32_t num_subsongs = self->mod->get_num_subsongs();
|
||||
self->subsong_lengths.resize( num_subsongs );
|
||||
for ( std::int32_t i = 0; i < num_subsongs; ++i ) {
|
||||
self->mod->select_subsong( i );
|
||||
self->subsong_lengths[i] = self->mod->get_duration_seconds();
|
||||
}
|
||||
self->subsong_lengths = build_subsong_lengths( *self->mod );
|
||||
self->subsong_names = self->mod->get_subsong_names();
|
||||
self->mod->select_subsong( 0 );
|
||||
self->tempo_factor = 0;
|
||||
|
@ -1240,7 +1233,7 @@ static void add_names( std::ostream & str, const std::string & title, const std:
|
|||
}
|
||||
str << title << " Names:" << "\r";
|
||||
for ( std::size_t i = 0; i < names.size(); i++ ) {
|
||||
str << std::setfill('0') << std::setw(2) << (display_offset + i) << std::setw(0) << "\t" << convert_to_native( names[i] ) << "\r";
|
||||
str << std::setfill( '0' ) << std::setw( 2 ) << ( display_offset + i ) << std::setw( 0 ) << "\t" << convert_to_native( sanitize_xmplay_info_string( names[i] ) ) << "\r";
|
||||
}
|
||||
str << "\r";
|
||||
}
|
||||
|
@ -1261,11 +1254,7 @@ static void WINAPI openmpt_GetSamples( char * buf ) {
|
|||
}
|
||||
|
||||
static DWORD WINAPI openmpt_GetSubSongs( float * length ) {
|
||||
double tmp = 0.0;
|
||||
for ( auto sub_length : self->subsong_lengths ) {
|
||||
tmp += sub_length;
|
||||
}
|
||||
*length = static_cast<float>( tmp );
|
||||
*length = static_cast<float>( std::accumulate( self->subsong_lengths.cbegin(), self->subsong_lengths.cend(), 0.0 ) );
|
||||
return static_cast<DWORD>( self->subsong_lengths.size() );
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
static const char * const license =
|
||||
"Copyright (c) 2004-2023, OpenMPT Project Developers and Contributors" "\n"
|
||||
"Copyright (c) 2004-2024, OpenMPT Project Developers and Contributors" "\n"
|
||||
"Copyright (c) 1997-2003, Olivier Lapicque" "\n"
|
||||
"All rights reserved." "\n"
|
||||
"" "\n"
|
||||
|
@ -283,6 +283,7 @@ static concat_stream<mpt::ustring> & operator << ( concat_stream<mpt::ustring> &
|
|||
s << MPT_USTRING("Banner: ") << flags.banner << lf;
|
||||
s << MPT_USTRING("Verbose: ") << flags.verbose << lf;
|
||||
s << MPT_USTRING("Mode : ") << mode_to_string( flags.mode ) << lf;
|
||||
s << MPT_USTRING("Terminal size : ") << flags.terminal_width << MPT_USTRING("*") << flags.terminal_height << lf;
|
||||
s << MPT_USTRING("Show progress: ") << flags.show_progress << lf;
|
||||
s << MPT_USTRING("Show peak meters: ") << flags.show_meters << lf;
|
||||
s << MPT_USTRING("Show channel peak meters: ") << flags.show_channel_meters << lf;
|
||||
|
@ -384,7 +385,7 @@ static void show_banner( concat_stream<mpt::ustring> & log, verbosity banner ) {
|
|||
return;
|
||||
}
|
||||
log << MPT_USTRING("openmpt123") << MPT_USTRING(" v") << mpt::transcode<mpt::ustring>( mpt::source_encoding, OPENMPT123_VERSION_STRING ) << MPT_USTRING(", libopenmpt ") << mpt::transcode<mpt::ustring>( libopenmpt_encoding, openmpt::string::get( "library_version" ) ) << MPT_USTRING(" (") << MPT_USTRING("OpenMPT ") << mpt::transcode<mpt::ustring>( libopenmpt_encoding, openmpt::string::get( "core_version" ) ) << MPT_USTRING(")") << lf;
|
||||
log << MPT_USTRING("Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors <https://lib.openmpt.org/>") << lf;
|
||||
log << MPT_USTRING("Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors <https://lib.openmpt.org/>") << lf;
|
||||
if ( banner == verbosity_normal ) {
|
||||
log << lf;
|
||||
return;
|
||||
|
@ -461,7 +462,7 @@ static void show_banner( concat_stream<mpt::ustring> & log, verbosity banner ) {
|
|||
static void show_man_version( textout & log ) {
|
||||
log << MPT_USTRING("openmpt123") << MPT_USTRING(" v") << mpt::transcode<mpt::ustring>( mpt::source_encoding, OPENMPT123_VERSION_STRING ) << lf;
|
||||
log << lf;
|
||||
log << MPT_USTRING("Copyright (c) 2013-2023 OpenMPT Project Developers and Contributors <https://lib.openmpt.org/>") << lf;
|
||||
log << MPT_USTRING("Copyright (c) 2013-2024 OpenMPT Project Developers and Contributors <https://lib.openmpt.org/>") << lf;
|
||||
}
|
||||
|
||||
static void show_short_version( textout & log ) {
|
||||
|
@ -970,7 +971,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
|||
meter_type meter;
|
||||
|
||||
const bool multiline = flags.show_ui;
|
||||
|
||||
|
||||
const bool narrow = (flags.terminal_width < 72) && (flags.terminal_height > 25);
|
||||
|
||||
int lines = 0;
|
||||
|
||||
int pattern_lines = 0;
|
||||
|
@ -980,10 +983,17 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
|||
// cppcheck-suppress identicalInnerCondition
|
||||
if ( flags.show_ui ) {
|
||||
lines += 1;
|
||||
if ( narrow ) {
|
||||
lines += 1;
|
||||
}
|
||||
}
|
||||
if ( flags.show_meters ) {
|
||||
for ( int channel = 0; channel < flags.channels; ++channel ) {
|
||||
if ( narrow ) {
|
||||
lines += 1;
|
||||
} else {
|
||||
for ( int channel = 0; channel < flags.channels; ++channel ) {
|
||||
lines += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( flags.show_channel_meters ) {
|
||||
|
@ -993,6 +1003,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
|||
lines += 1;
|
||||
if ( flags.show_progress ) {
|
||||
lines += 1;
|
||||
if ( narrow ) {
|
||||
lines += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( flags.show_progress ) {
|
||||
|
@ -1120,7 +1133,13 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
|||
log.cursor_up( lines );
|
||||
log << lf;
|
||||
if ( flags.show_meters ) {
|
||||
draw_meters( log, meter, flags );
|
||||
if ( narrow ) {
|
||||
log << MPT_USTRING("Level......: ");
|
||||
draw_meters_tiny( log, meter, flags );
|
||||
log << lf;
|
||||
} else {
|
||||
draw_meters( log, meter, flags );
|
||||
}
|
||||
}
|
||||
if ( flags.show_channel_meters ) {
|
||||
int width = ( flags.terminal_width - 3 ) / mod.get_num_channels();
|
||||
|
@ -1183,12 +1202,23 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
|||
}
|
||||
}
|
||||
if ( flags.show_ui ) {
|
||||
log << MPT_USTRING("Settings...: ");
|
||||
log << MPT_USTRING("Gain: ") << static_cast<float>( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Filter: ") << flags.filtertaps << MPT_USTRING(" taps") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Ramping: ") << flags.ramping << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
if ( narrow ) {
|
||||
log << MPT_USTRING("Settings...: ");
|
||||
log << MPT_USTRING("Gain: ") << static_cast<float>( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
log << MPT_USTRING("Filter.....: ");
|
||||
log << MPT_USTRING("Length: ") << flags.filtertaps << MPT_USTRING(" taps") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Ramping: ") << flags.ramping << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
} else {
|
||||
log << MPT_USTRING("Settings...: ");
|
||||
log << MPT_USTRING("Gain: ") << static_cast<float>( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Filter: ") << flags.filtertaps << MPT_USTRING(" taps") << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Ramping: ") << flags.ramping << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
}
|
||||
}
|
||||
if ( flags.show_details ) {
|
||||
log << MPT_USTRING("Mixer......: ");
|
||||
|
@ -1198,18 +1228,37 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
|||
log << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
if ( flags.show_progress ) {
|
||||
log << MPT_USTRING("Player.....: ");
|
||||
log << MPT_USTRING("Ord:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_order() ) << MPT_USTRING("/") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_num_orders() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Pat:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_pattern() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Row:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_row() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Spd:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 2, mod.get_current_speed() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Tmp:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 6, mpt::format<mpt::ustring>::fix( mod.get_current_tempo2(), 2 ) );
|
||||
log << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
if ( narrow ) {
|
||||
log << MPT_USTRING("Player.....: ");
|
||||
log << MPT_USTRING("Ord:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_order() ) << MPT_USTRING("/") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_num_orders() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
log << MPT_USTRING("Pattern....: ");
|
||||
log << MPT_USTRING("Pat:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_pattern() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Row:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_row() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
log << MPT_USTRING("Tempo......: ");
|
||||
log << MPT_USTRING("Spd:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 2, mod.get_current_speed() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Tmp:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 6, mpt::format<mpt::ustring>::fix( mod.get_current_tempo2(), 2 ) );
|
||||
log << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
} else {
|
||||
log << MPT_USTRING("Player.....: ");
|
||||
log << MPT_USTRING("Ord:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_order() ) << MPT_USTRING("/") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_num_orders() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Pat:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_pattern() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Row:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_row() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Spd:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 2, mod.get_current_speed() );
|
||||
log << MPT_USTRING(" ");
|
||||
log << MPT_USTRING("Tmp:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 6, mpt::format<mpt::ustring>::fix( mod.get_current_tempo2(), 2 ) );
|
||||
log << MPT_USTRING(" ");
|
||||
log << lf;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( flags.show_progress ) {
|
||||
|
|
|
@ -76,7 +76,7 @@ struct MixLoopState
|
|||
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)
|
||||
if(inSustainLoop || (chn.nLoopStart == chn.pModSample->nLoopStart && chn.nLoopEnd == chn.pModSample->nLoopEnd))
|
||||
{
|
||||
SmpLength lookaheadOffset = 3 * InterpolationLookaheadBufferSize + chn.pModSample->nLength - chn.nLoopEnd;
|
||||
if(inSustainLoop)
|
||||
|
|
|
@ -11,13 +11,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "openmpt/all/BuildSettings.hpp"
|
||||
|
||||
#include "../soundlib/ModInstrument.h"
|
||||
#include "../soundlib/ModSample.h"
|
||||
#include "../soundlib/SampleIO.h"
|
||||
#include "openmpt/base/Endian.hpp"
|
||||
#include "Snd_defs.h"
|
||||
|
||||
OPENMPT_NAMESPACE_BEGIN
|
||||
|
||||
struct InstrumentEnvelope;
|
||||
struct ModInstrument;
|
||||
struct ModSample;
|
||||
class CSoundFile;
|
||||
class SampleIO;
|
||||
|
||||
struct ITFileHeader
|
||||
{
|
||||
// Header Flags
|
||||
|
|
|
@ -401,6 +401,7 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
InitializeGlobals(MOD_TYPE_AMS);
|
||||
InitializeChannels();
|
||||
|
||||
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
|
||||
m_nChannels = (fileHeader.channelConfig & 0x1F) + 1;
|
||||
|
@ -435,7 +436,6 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// Read channel names
|
||||
for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
|
||||
{
|
||||
ChnSettings[chn].Reset();
|
||||
file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
|
||||
}
|
||||
|
||||
|
@ -826,12 +826,12 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
uint8 numSamples = file.ReadUint8();
|
||||
uint8 sampleAssignment[120];
|
||||
MemsetZero(sampleAssignment); // Only really needed for v2.0, where the lowest and highest octave aren't cleared.
|
||||
std::array<uint8, 120> sampleAssignment;
|
||||
sampleAssignment.fill(0); // 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
|
||||
|| (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment)) // v2.01+: 120 Notes
|
||||
|| (fileHeader.versionLow == 0 && !file.ReadRaw(mpt::as_span(sampleAssignment).subspan(12, 96)).size())) // v2.0: 96 Notes
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -878,18 +878,17 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// 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;
|
||||
std::string sampleName;
|
||||
for(SAMPLEINDEX smp = 0; smp < numSamples; smp++)
|
||||
{
|
||||
if(firstSmp + smp >= MAX_SAMPLES)
|
||||
{
|
||||
file.Skip(sizeof(AMS2SampleHeader));
|
||||
break;
|
||||
}
|
||||
file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[firstSmp + smp]);
|
||||
|
||||
file.ReadSizedString<uint8le, mpt::String::spacePadded>(sampleName);
|
||||
AMS2SampleHeader sampleHeader;
|
||||
file.ReadStruct(sampleHeader);
|
||||
if(firstSmp + smp >= MAX_SAMPLES)
|
||||
continue;
|
||||
|
||||
sampleHeader.ConvertToMPT(Samples[firstSmp + smp]);
|
||||
m_szNames[firstSmp + smp] = sampleName;
|
||||
|
||||
uint16 settings = (instrHeader.shadowInstr & instrIndexMask)
|
||||
| ((smp << sampleIndexShift) & sampleIndexMask)
|
||||
|
|
|
@ -38,17 +38,17 @@ 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"),
|
||||
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;
|
||||
|
@ -85,18 +85,43 @@ struct DBMInstrument
|
|||
{
|
||||
enum DBMInstrFlags
|
||||
{
|
||||
smpLoop = 0x01,
|
||||
smpPingPongLoop = 0x02,
|
||||
smpLoop = 0x01,
|
||||
smpPingPongLoop = 0x02,
|
||||
};
|
||||
|
||||
char name[30];
|
||||
uint16be sample; // Sample reference
|
||||
uint16be volume; // 0...64
|
||||
uint16be sample; // Sample reference
|
||||
uint16be volume; // 0...64
|
||||
uint32be sampleRate;
|
||||
uint32be loopStart;
|
||||
uint32be loopLength;
|
||||
int16be panning; // -128...128
|
||||
uint16be flags; // See DBMInstrFlags
|
||||
int16be panning; // -128...128
|
||||
uint16be flags; // See DBMInstrFlags
|
||||
|
||||
void ConvertToMPT(ModInstrument &mptIns) const
|
||||
{
|
||||
mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name);
|
||||
mptIns.nFadeOut = 0;
|
||||
mptIns.nPan = static_cast<uint16>(panning + 128);
|
||||
LimitMax(mptIns.nPan, uint32(256));
|
||||
mptIns.dwFlags.set(INS_SETPANNING);
|
||||
}
|
||||
|
||||
void ConvertToMPT(ModSample &mptSmp) const
|
||||
{
|
||||
mptSmp.Initialize();
|
||||
mptSmp.nVolume = std::min(static_cast<uint16>(volume), uint16(64)) * 4u;
|
||||
mptSmp.nC5Speed = Util::muldivr(sampleRate, 8303, 8363);
|
||||
|
||||
if(loopLength && (flags & (smpLoop | smpPingPongLoop)))
|
||||
{
|
||||
mptSmp.nLoopStart = loopStart;
|
||||
mptSmp.nLoopEnd = mptSmp.nLoopStart + loopLength;
|
||||
mptSmp.uFlags.set(CHN_LOOP);
|
||||
if(flags & smpPingPongLoop)
|
||||
mptSmp.uFlags.set(CHN_PINGPONGLOOP);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(DBMInstrument, 50)
|
||||
|
@ -107,19 +132,55 @@ struct DBMEnvelope
|
|||
{
|
||||
enum DBMEnvelopeFlags
|
||||
{
|
||||
envEnabled = 0x01,
|
||||
envSustain = 0x02,
|
||||
envLoop = 0x04,
|
||||
envEnabled = 0x01,
|
||||
envSustain1 = 0x02,
|
||||
envLoop = 0x04,
|
||||
envSustain2 = 0x08,
|
||||
};
|
||||
|
||||
uint16be instrument;
|
||||
uint8be flags; // See DBMEnvelopeFlags
|
||||
uint8be numSegments; // Number of envelope points - 1
|
||||
uint8be flags; // See DBMEnvelopeFlags
|
||||
uint8be numSegments; // Number of envelope points - 1
|
||||
uint8be sustain1;
|
||||
uint8be loopBegin;
|
||||
uint8be loopEnd;
|
||||
uint8be sustain2; // Second sustain point
|
||||
uint8be sustain2; // Second sustain point
|
||||
uint16be data[2 * 32];
|
||||
|
||||
void ConvertToMPT(InstrumentEnvelope &mptEnv, bool scaleEnv) const
|
||||
{
|
||||
if(numSegments)
|
||||
{
|
||||
if(flags & envEnabled) mptEnv.dwFlags.set(ENV_ENABLED);
|
||||
if(flags & (envSustain1 | envSustain2)) mptEnv.dwFlags.set(ENV_SUSTAIN);
|
||||
if(flags & envLoop) mptEnv.dwFlags.set(ENV_LOOP);
|
||||
}
|
||||
|
||||
uint8 numPoints = std::min(numSegments.get(), uint8(31)) + 1;
|
||||
mptEnv.resize(numPoints);
|
||||
|
||||
mptEnv.nLoopStart = loopBegin;
|
||||
mptEnv.nLoopEnd = loopEnd;
|
||||
if((flags & (envSustain1 | envSustain2)) == envSustain1)
|
||||
mptEnv.nSustainStart = mptEnv.nSustainEnd = sustain1;
|
||||
else if((flags & (envSustain1 | envSustain2)) == envSustain2)
|
||||
mptEnv.nSustainStart = mptEnv.nSustainEnd = sustain2;
|
||||
else
|
||||
mptEnv.nSustainStart = mptEnv.nSustainEnd = std::min(sustain1, sustain2);
|
||||
|
||||
for(uint8 i = 0; i < numPoints; i++)
|
||||
{
|
||||
mptEnv[i].tick = data[i * 2];
|
||||
uint16 val = data[i * 2 + 1];
|
||||
if(scaleEnv)
|
||||
{
|
||||
// Panning envelopes are -128...128 in DigiBooster Pro 3.x
|
||||
val = static_cast<uint16>((val + 128) / 4);
|
||||
}
|
||||
LimitMax(val, uint16(64));
|
||||
mptEnv[i].value = static_cast<uint8>(val);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(DBMEnvelope, 136)
|
||||
|
@ -137,11 +198,11 @@ static constexpr EffectCommand dbmEffects[] =
|
|||
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
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -197,15 +258,15 @@ static std::pair<EffectCommand, uint8> ConvertDBMEffect(const uint8 cmd, uint8 p
|
|||
case CMD_MODCMDEX:
|
||||
switch(param & 0xF0)
|
||||
{
|
||||
case 0x30: // Play backwards
|
||||
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)
|
||||
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
|
||||
case 0x50: // Turn on/off channel
|
||||
// TODO: Apparently this should also kill the playing note.
|
||||
if((param & 0x0F) <= 0x01)
|
||||
{
|
||||
|
@ -213,7 +274,7 @@ static std::pair<EffectCommand, uint8> ConvertDBMEffect(const uint8 cmd, uint8 p
|
|||
param = (param == 0x50) ? 0x00 : 0x40;
|
||||
}
|
||||
break;
|
||||
case 0x70: // Coarse offset
|
||||
case 0x70: // Coarse offset
|
||||
command = CMD_S3MCMDEX;
|
||||
param = 0xA0 | (param & 0x0F);
|
||||
break;
|
||||
|
@ -258,35 +319,7 @@ static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundF
|
|||
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<uint8>(val);
|
||||
}
|
||||
dbmEnv.ConvertToMPT(sndFile.Instruments[dbmIns]->GetEnvelope(envType), scaleEnv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -356,6 +389,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_playBehaviour.set(kSlidesAtSpeed1);
|
||||
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
||||
m_playBehaviour.reset(kITArpeggio);
|
||||
m_playBehaviour.reset(kITInstrWithNoteOff);
|
||||
m_playBehaviour.reset(kITInstrWithNoteOffOldEffects);
|
||||
|
||||
m_modFormat.formatName = U_("DigiBooster Pro");
|
||||
m_modFormat.type = U_("dbm");
|
||||
|
@ -407,41 +442,41 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
#endif // MPT_DBM_USE_REAL_SUBSONGS
|
||||
|
||||
// Read instruments
|
||||
std::map<SAMPLEINDEX, SAMPLEINDEX> copySample;
|
||||
if(FileReader instChunk = chunks.GetChunk(DBMChunk::idINST))
|
||||
{
|
||||
std::set<SAMPLEINDEX> sampleUsed;
|
||||
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)
|
||||
SAMPLEINDEX mappedSample = instrHeader.sample;
|
||||
if(sampleUsed.count(mappedSample) && CanAddMoreSamples())
|
||||
{
|
||||
ModSample mptSmp;
|
||||
instrHeader.ConvertToMPT(mptSmp);
|
||||
const ModSample &origSmp = Samples[mappedSample];
|
||||
if(mptSmp.nVolume != origSmp.nVolume
|
||||
|| mptSmp.uFlags != origSmp.uFlags
|
||||
|| mptSmp.nLoopStart != origSmp.nLoopStart
|
||||
|| mptSmp.nLoopEnd != origSmp.nLoopEnd
|
||||
|| mptSmp.nC5Speed != origSmp.nC5Speed)
|
||||
{
|
||||
// Need to duplicate
|
||||
mappedSample = ++m_nSamples;
|
||||
copySample.emplace(mappedSample, instrHeader.sample);
|
||||
}
|
||||
}
|
||||
ModInstrument *mptIns = AllocateInstrument(i, mappedSample);
|
||||
if(mptIns == nullptr || mappedSample >= 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<uint16>(instrHeader.panning + 128);
|
||||
LimitMax(mptIns->nPan, uint32(256));
|
||||
mptIns->dwFlags.set(INS_SETPANNING);
|
||||
|
||||
instrHeader.ConvertToMPT(*mptIns);
|
||||
// Sample Info
|
||||
ModSample &mptSmp = Samples[instrHeader.sample];
|
||||
mptSmp.Initialize();
|
||||
mptSmp.nVolume = std::min(static_cast<uint16>(instrHeader.volume), uint16(64)) * 4u;
|
||||
mptSmp.nC5Speed = Util::muldivr(instrHeader.sampleRate, 8303, 8363);
|
||||
|
||||
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);
|
||||
}
|
||||
instrHeader.ConvertToMPT(Samples[mappedSample]);
|
||||
m_szNames[mappedSample] = mptIns->name;
|
||||
sampleUsed.insert(mappedSample);
|
||||
}
|
||||
|
||||
// Read envelopes
|
||||
|
@ -466,7 +501,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(patternChunk.IsValid() && (loadFlags & loadPatternData))
|
||||
{
|
||||
FileReader patternNameChunk = chunks.GetChunk(DBMChunk::idPNAM);
|
||||
patternNameChunk.Skip(1); // Encoding, should be UTF-8 or ASCII
|
||||
patternNameChunk.Skip(1); // Encoding (0 = unspecified ASCII-compatible 8-bit encoding, 106 = UTF-8)
|
||||
|
||||
Patterns.ResizeArray(infoData.patterns);
|
||||
std::vector<std::pair<EffectCommand, ModCommand::PARAM>> lostGlobalCommands;
|
||||
|
@ -538,6 +573,14 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
std::swap(cmd1, cmd2);
|
||||
std::swap(param1, param2);
|
||||
} else if(cmd1 == CMD_TONEPORTAMENTO && cmd2 == CMD_OFFSET && param2 == 0)
|
||||
{
|
||||
// Offset + Portmaneto: Ignore portamento. If the offset command has a non-zero parameter, keep it for effect memory.
|
||||
cmd2 = CMD_NONE;
|
||||
} else if(cmd2 == CMD_TONEPORTAMENTO && cmd1 == CMD_OFFSET && param1 == 0)
|
||||
{
|
||||
// Ditto
|
||||
cmd1 = CMD_NONE;
|
||||
}
|
||||
|
||||
const auto lostCommand = m.FillInTwoCommands(cmd1, param1, cmd2, param2);
|
||||
|
@ -641,6 +684,13 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
|
||||
{
|
||||
if(auto copyFrom = copySample.find(smp); copyFrom != copySample.end())
|
||||
{
|
||||
Samples[smp].nLength = Samples[copyFrom->second].nLength;
|
||||
Samples[smp].CopyWaveform(Samples[copyFrom->second]);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 sampleFlags = sampleChunk.ReadUint32BE();
|
||||
uint32 sampleLength = sampleChunk.ReadUint32BE();
|
||||
|
||||
|
|
|
@ -346,7 +346,7 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, const uint8 fileVersion,
|
|||
// Counters for channel packing (including global track)
|
||||
std::vector<uint8> channelCounter(numChannels + 1, 0);
|
||||
|
||||
for(ROWINDEX row = 0; row < numRows; row++)
|
||||
for(ROWINDEX row = 0; row < numRows && file.CanRead(1); row++)
|
||||
{
|
||||
// Global track info counter reached 0 => read global track data
|
||||
if(channelCounter[0] == 0)
|
||||
|
@ -934,7 +934,7 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// 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;
|
||||
chunkLength = mpt::saturate_cast<uint32>(file.BytesLeft());
|
||||
chunks.chunks.push_back(ChunkReader::Item<DMFChunk>{chunkHeader, file.ReadChunk(chunkLength)});
|
||||
file.Skip(chunkSkip);
|
||||
}
|
||||
|
@ -970,6 +970,8 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
|
|||
const uint8 headerSize = fileHeader.version < 3 ? 9 : 8;
|
||||
chunk.Skip(headerSize - sizeof(uint32le));
|
||||
const uint32 patLength = chunk.ReadUint32LE();
|
||||
if(!chunk.CanRead(patLength))
|
||||
return false;
|
||||
chunk.SkipBack(headerSize);
|
||||
patternChunk = chunk.ReadChunk(headerSize + patLength);
|
||||
}
|
||||
|
|
|
@ -401,7 +401,7 @@ bool CSoundFile::ReadDSm(FileReader &file, ModLoadingFlags loadFlags)
|
|||
return true;
|
||||
|
||||
InitializeGlobals(MOD_TYPE_MOD);
|
||||
m_SongFlags.set(SONG_IMPORTED);
|
||||
m_SongFlags = SONG_IMPORTED;
|
||||
m_nChannels = fileHeader.numChannels;
|
||||
static_assert(MAX_BASECHANNELS >= 32 && MAX_SAMPLES > 255);
|
||||
m_nSamples = fileHeader.numSamples;
|
||||
|
@ -511,7 +511,7 @@ bool CSoundFile::ReadDSm(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
if(loadFlags & loadSampleData)
|
||||
{
|
||||
for(SAMPLEINDEX smp = 1; smp <= m_nSamplePreAmp; smp++)
|
||||
for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
|
||||
{
|
||||
SampleIO(Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
|
||||
SampleIO::mono,
|
||||
|
|
|
@ -97,7 +97,12 @@ struct DTMSample
|
|||
// 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);
|
||||
int32 transposeAmount = 0;
|
||||
#ifdef MODPLUG_TRACKER
|
||||
transposeAmount = MOD2XMFineTune(finetune);
|
||||
#else
|
||||
mptSmp.nFineTune = MOD2XMFineTune(finetune);
|
||||
#endif
|
||||
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).
|
||||
|
@ -231,6 +236,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
InitializeGlobals(MOD_TYPE_DTM);
|
||||
InitializeChannels();
|
||||
m_SongFlags.set(SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS);
|
||||
m_playBehaviour.reset(kPeriodsAreHertz);
|
||||
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
||||
// Various files have a default speed or tempo of 0
|
||||
if(fileHeader.tempo)
|
||||
|
@ -436,26 +442,18 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
m->note = note + NOTE_MIN + 12;
|
||||
if(position.rem)
|
||||
{
|
||||
m->command = CMD_MODCMDEX;
|
||||
m->param = 0xD0 | static_cast<ModCommand::PARAM>(std::min(position.rem, 15));
|
||||
}
|
||||
m->SetEffectCommand(CMD_MODCMDEX, static_cast<ModCommand::PARAM>(0xD0 | 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<ModCommand::PARAM>(std::min(position.rem, 15));
|
||||
} else
|
||||
{
|
||||
m->SetEffectCommand(CMD_MODCMDEX, static_cast<ModCommand::PARAM>(0xC0 |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.
|
||||
m->SetVolumeCommand(VOLCMD_VOLUME, std::min(volume, uint8(64))); // Volume can go up to 255, but we do not support over-amplification at the moment.
|
||||
}
|
||||
if(instr)
|
||||
{
|
||||
|
@ -578,6 +576,9 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(patternFormat == DTM_206_PATTERN_FORMAT)
|
||||
{
|
||||
tracker = U_("Digital Home Studio");
|
||||
} else if(patternFormat == DTM_PT_PATTERN_FORMAT)
|
||||
{
|
||||
tracker = U_("Digital Tracker 2.3");
|
||||
} else if(FileReader chunk = chunks.GetChunk(DTMChunk::idVERS))
|
||||
{
|
||||
uint32 version = chunk.ReadUint32BE();
|
||||
|
|
|
@ -172,7 +172,7 @@ struct IMFSample
|
|||
uint8le unused3[5];
|
||||
uint16le ems; // Reserved for internal usage
|
||||
uint32le dram; // Reserved for internal usage
|
||||
char is10[4]; // 'IS10'
|
||||
char is10[4]; // 'IS10' or 'IW10' (not verified by Orpheus)
|
||||
|
||||
// Convert an IMFSample to OpenMPT's internal sample representation.
|
||||
void ConvertToMPT(ModSample &mptSmp) const
|
||||
|
@ -596,8 +596,9 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags)
|
|||
file.ReadStruct(sampleHeader);
|
||||
|
||||
const SAMPLEINDEX smpID = firstSample + smp;
|
||||
if(memcmp(sampleHeader.is10, "IS10", 4) || smpID >= MAX_SAMPLES)
|
||||
if(smpID >= MAX_SAMPLES)
|
||||
{
|
||||
file.Skip(sampleHeader.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, mpt::Charset
|
|||
CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str);
|
||||
if(localTuning)
|
||||
{
|
||||
std::unique_ptr<CTuning> pNewTuning = std::unique_ptr<CTuning>(new CTuning(*localTuning));
|
||||
std::unique_ptr<CTuning> pNewTuning = std::make_unique<CTuning>(*localTuning);
|
||||
CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning));
|
||||
if(pT)
|
||||
{
|
||||
|
@ -687,12 +687,6 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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();
|
||||
}
|
||||
|
||||
bool hasModPlugExtensions = false;
|
||||
|
||||
// Read pattern names: "PNAM"
|
||||
|
@ -1224,9 +1218,10 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
madeWithTracker = U_("ChibiTracker");
|
||||
m_playBehaviour.reset(kITShortSampleRetrig);
|
||||
m_nSamplePreAmp /= 2;
|
||||
} 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"))
|
||||
&& m_nSamples > 1 && (Samples[1].filename == "XXXXXXXX.YYY"))
|
||||
{
|
||||
madeWithTracker = U_("CheeseTracker");
|
||||
} else if(fileHeader.cwtv == 0 && madeWithTracker.empty())
|
||||
|
@ -1291,6 +1286,26 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// Initial note memory for channel is C-0: Added 2023-03-09, https://github.com/schismtracker/schismtracker/commit/73e9d60676c2b48c8e94e582373e29517105b2b1
|
||||
if(schismDateVersion < SchismVersionFromDate<2023, 03, 9>::date)
|
||||
m_playBehaviour.reset(kITInitialNoteMemory);
|
||||
// DCT note comparison: Added 2023-10-17, https://github.com/schismtracker/schismtracker/commit/31d36dc00013fc5ab0efa20c782af18e8b006e07
|
||||
if(schismDateVersion < SchismVersionFromDate<2023, 10, 17>::date)
|
||||
m_playBehaviour.reset(kITDCTBehaviour);
|
||||
if(schismDateVersion < SchismVersionFromDate<2023, 10, 19>::date)
|
||||
{
|
||||
// Panbrello sample & hold random waveform: Added 2023-10-19, https://github.com/schismtracker/schismtracker/commit/411ec16b190ba1a486d8b0907ad8d74f8fdc2840
|
||||
m_playBehaviour.reset(kITSampleAndHoldPanbrello);
|
||||
// Don't apply any portamento if no previous note is playing: Added 2023-10-19, https://github.com/schismtracker/schismtracker/commit/8ff0a86a715efb50c89770fb9095d4c4089ff187
|
||||
m_playBehaviour.reset(kITPortaNoNote);
|
||||
}
|
||||
if(schismDateVersion < SchismVersionFromDate<2023, 10, 22>::date)
|
||||
{
|
||||
// Note delay delays first-tick behaviour for slides: Added 2023-10-22, https://github.com/schismtracker/schismtracker/commit/b9609e4f827e1b6ce9ebe6573b85e69388ca0ea0
|
||||
m_playBehaviour.reset(kITFirstTickHandling);
|
||||
// https://github.com/schismtracker/schismtracker/commit/a9e5df533ab52c35190fcc1cbfed4f0347b660bb
|
||||
m_playBehaviour.reset(kITMultiSampleInstrumentNumber);
|
||||
}
|
||||
// Panbrello hold: Added 2024-03-09, https://github.com/schismtracker/schismtracker/commit/ebdebaa8c8a735a7bf49df55debded1b7aac3605
|
||||
if(schismDateVersion < SchismVersionFromDate<2024, 03, 9>::date)
|
||||
m_playBehaviour.reset(kITPanbrelloHold);
|
||||
break;
|
||||
case 4:
|
||||
madeWithTracker = MPT_UFORMAT("pyIT {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF));
|
||||
|
@ -1305,7 +1320,12 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
madeWithTracker = MPT_UFORMAT("ITMCK {}.{}.{}")((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
|
||||
break;
|
||||
case 0xD:
|
||||
madeWithTracker = U_("spc2it");
|
||||
if(fileHeader.cwtv == 0xDAEB)
|
||||
madeWithTracker = U_("spc2it");
|
||||
else if(fileHeader.cwtv == 0xD1CE)
|
||||
madeWithTracker = U_("itwriter");
|
||||
else
|
||||
madeWithTracker = U_("Unknown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1313,6 +1333,17 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(anyADPCM)
|
||||
madeWithTracker += U_(" (ADPCM packed)");
|
||||
|
||||
// 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.
|
||||
// Example: denonde.it by Mystical
|
||||
// Note: Only checking the cwtv "made with" version is not enough: spx-visionsofthepast.it has the strange combination of cwtv=2.00, cmwt=2.16
|
||||
// Hence to be sure, we check that both values are below 2.14.
|
||||
// Note that all ModPlug Tracker alpha versions do not support filters yet. Earlier alphas identify as cwtv=2.02, cmwt=2.00, but later alpha versions identify as IT 2.14.
|
||||
// Apart from that, there's an unknown XM conversion tool declaring a lower comaptible version, which naturally also does not support filters, so it's okay that it is caught here.
|
||||
if((fileHeader.cwtv < 0x0214 && fileHeader.cmwt < 0x0214) || (m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MPT_V("1.00.00.A6")))
|
||||
{
|
||||
m_MidiCfg.ClearZxxMacros();
|
||||
}
|
||||
|
||||
if(GetType() == MOD_TYPE_MPT)
|
||||
{
|
||||
// START - mpt specific:
|
||||
|
|
|
@ -168,7 +168,7 @@ static constexpr EffectCommand MDLEffTrans[] =
|
|||
/* Either column */
|
||||
/* 7 */ CMD_TEMPO,
|
||||
/* 8 */ CMD_PANNING8,
|
||||
/* 9 */ CMD_SETENVPOSITION,
|
||||
/* 9 */ CMD_S3MCMDEX,
|
||||
/* A */ CMD_NONE,
|
||||
/* B */ CMD_POSITIONJUMP,
|
||||
/* C */ CMD_GLOBALVOLUME,
|
||||
|
@ -203,6 +203,16 @@ static std::pair<EffectCommand, uint8> ConvertMDLCommand(const uint8 command, ui
|
|||
case 0x08: // Panning
|
||||
param = (param & 0x7F) * 2u;
|
||||
break;
|
||||
case 0x09: // Set Envelope (we can only have one envelope per type...)
|
||||
if(param < 0x40)
|
||||
param = 0x78; // Enable the one volume envelope we have
|
||||
else if (param < 0x80)
|
||||
param = 0x7A; // Enable the one panning envelope we have
|
||||
else if(param < 0xC0)
|
||||
param = 0x7C; // Enable the one pitch envelope we have
|
||||
else
|
||||
cmd = CMD_NONE;
|
||||
break;
|
||||
case 0x0C: // Global volume
|
||||
param = (param + 1) / 2u;
|
||||
break;
|
||||
|
@ -468,6 +478,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_SongFlags = SONG_ITCOMPATGXX;
|
||||
m_playBehaviour.set(kPerChannelGlobalVolSlide);
|
||||
m_playBehaviour.set(kApplyOffsetWithoutNote);
|
||||
m_playBehaviour.reset(kPeriodsAreHertz);
|
||||
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
||||
m_playBehaviour.reset(kITSCxStopsSample); // Gate effect in underbeat.mdl
|
||||
|
||||
|
|
|
@ -84,10 +84,10 @@ struct MMD2Song
|
|||
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
|
||||
uint8 mixEchoType; // 0 = nothing, 1 = normal, 2 = cross
|
||||
uint8 mixEchoDepth; // 1 - 6, 0 = default
|
||||
uint16be mixEchoLength; // Echo length in milliseconds
|
||||
int8be mixStereoSep; // Stereo separation
|
||||
int8 mixStereoSep; // Stereo separation
|
||||
char pad0[223];
|
||||
};
|
||||
|
||||
|
@ -101,7 +101,7 @@ struct MMDSong
|
|||
{
|
||||
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_JUMP8TH = 0x04, // Jump 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
|
||||
|
@ -392,53 +392,61 @@ static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPer
|
|||
}
|
||||
|
||||
|
||||
static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &m, const uint8 command, const bool is8ch, const bool bpmMode, const uint8 rowsPerBeat, const bool volHex)
|
||||
struct TranslateMEDPatternContext
|
||||
{
|
||||
m.command = CMD_NONE;
|
||||
const int16 transpose;
|
||||
const CHANNELINDEX numTracks;
|
||||
const uint8 version;
|
||||
const uint8 rowsPerBeat;
|
||||
const bool hardwareMixSamples : 1;
|
||||
const bool is8Ch : 1;
|
||||
const bool bpmMode : 1;
|
||||
const bool volHex : 1;
|
||||
};
|
||||
|
||||
|
||||
static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &m, const uint8 command, const uint8 param, const uint8 param2, const TranslateMEDPatternContext ctx)
|
||||
{
|
||||
const uint8 nibbleLo = std::min(param, uint8(0x0F));
|
||||
switch(command)
|
||||
{
|
||||
case 0x04: // Vibrato (twice as deep as in ProTracker)
|
||||
m.command = CMD_VIBRATO;
|
||||
m.param = (std::min<uint8>(m.param >> 3, 0x0F) << 4) | std::min<uint8>((m.param & 0x0F) * 2, 0x0F);
|
||||
m.SetEffectCommand(CMD_VIBRATO, (std::min<uint8>(param >> 3, 0x0F) << 4) | std::min<uint8>((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;
|
||||
if(param > 0 && param <= 20)
|
||||
m.SetEffectCommand(CMD_SPEED, param);
|
||||
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;
|
||||
case 0x0C: // Set Volume (note: parameters >= 0x80 (only in hex mode?) should set the default instrument volume, which we don't support)
|
||||
if(!ctx.volHex && param < 0x99)
|
||||
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>((param >> 4) * 10 + (param & 0x0F)));
|
||||
else if(ctx.volHex && ctx.version < 3)
|
||||
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>(std::min(param & 0x7F, 64)));
|
||||
else if(ctx.volHex)
|
||||
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>(((param & 0x7F) + 1) / 2));
|
||||
break;
|
||||
case 0x0D:
|
||||
m.command = CMD_VOLUMESLIDE;
|
||||
m.SetEffectCommand(CMD_VOLUMESLIDE, param);
|
||||
break;
|
||||
case 0x0E: // Synth jump
|
||||
m.command = CMD_NONE;
|
||||
break;
|
||||
case 0x0F: // Misc
|
||||
if(m.param == 0)
|
||||
if(param == 0)
|
||||
{
|
||||
m.command = CMD_PATTERNBREAK;
|
||||
} else if(m.param <= 0xF0)
|
||||
m.SetEffectCommand(CMD_PATTERNBREAK, param);
|
||||
} else if(param <= 0xF0)
|
||||
{
|
||||
m.command = CMD_TEMPO;
|
||||
if(m.param < 0x03)
|
||||
if(param < 0x03)
|
||||
{
|
||||
// This appears to be a bug in OctaMED which is not emulated in MED Soundstudio on Windows.
|
||||
m.param = 0x70;
|
||||
} else
|
||||
{
|
||||
uint16 tempo = mpt::saturate_round<uint16>(MMDTempoToBPM(m.param, is8ch, bpmMode, rowsPerBeat).ToDouble());
|
||||
uint16 tempo = mpt::saturate_round<uint16>(MMDTempoToBPM(param, ctx.is8Ch, ctx.bpmMode, ctx.rowsPerBeat).ToDouble());
|
||||
if(tempo <= Util::MaxValueOfType(m.param))
|
||||
{
|
||||
m.param = static_cast<ModCommand::PARAM>(tempo);
|
||||
|
@ -452,156 +460,183 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
|
|||
if(m.param < 0x20)
|
||||
m.param = 0x20;
|
||||
#endif // MODPLUG_TRACKER
|
||||
} else switch(command)
|
||||
} else switch(param)
|
||||
{
|
||||
case 0xF1: // Play note twice
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0x93;
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0x93);
|
||||
break;
|
||||
case 0xF2: // Delay note
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0xD3;
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xD3);
|
||||
break;
|
||||
case 0xF3: // Play note three times
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0x92;
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0x92);
|
||||
break;
|
||||
case 0xF8: // Turn filter off
|
||||
case 0xF9: // Turn filter on
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0xF9 - m.param;
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xF9 - 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;
|
||||
m.SetEffectCommand(CMD_MIDI, 0x80 | param);
|
||||
break;
|
||||
case 0x11: // Slide pitch up
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0x10 | std::min<uint8>(m.param, 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0x10 | nibbleLo);
|
||||
break;
|
||||
case 0x12: // Slide pitch down
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0x20 | std::min<uint8>(m.param, 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0x20 | nibbleLo);
|
||||
break;
|
||||
case 0x14: // Vibrato (ProTracker compatible depth, but faster)
|
||||
m.command = CMD_VIBRATO;
|
||||
m.param = (std::min<uint8>((m.param >> 4) + 1, 0x0F) << 4) | (m.param & 0x0F);
|
||||
m.SetEffectCommand(CMD_VIBRATO, (std::min<uint8>((param >> 4) + 1, 0x0F) << 4) | (param & 0x0F));
|
||||
break;
|
||||
case 0x15: // Set finetune
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0x50 | (m.param & 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0x50 | (param & 0x0F));
|
||||
break;
|
||||
case 0x16: // Loop
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0x60 | std::min<uint8>(m.param, 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0x60 | nibbleLo);
|
||||
break;
|
||||
case 0x18: // Stop note
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0xC0 | std::min<uint8>(m.param, 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xC0 | nibbleLo);
|
||||
break;
|
||||
case 0x19: // Sample Offset
|
||||
m.command = CMD_OFFSET;
|
||||
m.SetEffectCommand(CMD_OFFSET, param);
|
||||
break;
|
||||
case 0x1A: // Slide volume up once
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0xA0 | std::min<uint8>(m.param, 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xA0 | nibbleLo);
|
||||
break;
|
||||
case 0x1B: // Slide volume down once
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0xB0 | std::min<uint8>(m.param, 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xB0 | nibbleLo);
|
||||
break;
|
||||
case 0x1C: // MIDI program
|
||||
if(m.param > 0 && m.param <= 128)
|
||||
{
|
||||
m.command = CMD_MIDI;
|
||||
m.param--;
|
||||
} else
|
||||
{
|
||||
m.command = CMD_NONE;
|
||||
}
|
||||
if(param > 0 && param <= 128)
|
||||
m.SetEffectCommand(CMD_MIDI, param - 1);
|
||||
break;
|
||||
case 0x1D: // Pattern break (in hex)
|
||||
m.command = CMD_PATTERNBREAK;
|
||||
m.SetEffectCommand(CMD_PATTERNBREAK, param);
|
||||
break;
|
||||
case 0x1E: // Repeat row
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0xE0 | std::min<uint8>(m.param, 0x0F);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xE0 | std::min<uint8>(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;
|
||||
}
|
||||
if(param & 0xF0)
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xD0 | (param >> 4));
|
||||
else if(param & 0x0F)
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0x90 | param);
|
||||
break;
|
||||
}
|
||||
case 0x20: // Reverse sample + skip samples
|
||||
if(m.param == 0 && m.vol == 0)
|
||||
if(param == 0 && param2 == 0)
|
||||
{
|
||||
if(m.IsNote())
|
||||
{
|
||||
m.command = CMD_S3MCMDEX;
|
||||
m.param = 0x9F;
|
||||
m.SetEffectCommand(CMD_XFINEPORTAUPDOWN, 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<ModCommand::PARAM>(Util::muldiv_unsigned(m.param, 0x100, m.vol));
|
||||
} else
|
||||
{
|
||||
m.command = CMD_NONE;
|
||||
}
|
||||
if(param2 > 0)
|
||||
m.SetEffectCommand(CMD_OFFSETPERCENTAGE, mpt::saturate_cast<ModCommand::PARAM>(Util::muldiv_unsigned(param, 0x100, param2)));
|
||||
break;
|
||||
case 0x2E: // Set panning
|
||||
if(m.param <= 0x10 || m.param >= 0xF0)
|
||||
{
|
||||
m.command = CMD_PANNING8;
|
||||
m.param = mpt::saturate_cast<ModCommand::PARAM>(((m.param ^ 0x80) - 0x70) * 8);
|
||||
} else
|
||||
{
|
||||
m.command = CMD_NONE;
|
||||
}
|
||||
if(param <= 0x10 || param >= 0xF0)
|
||||
m.SetEffectCommand(CMD_PANNING8, mpt::saturate_cast<ModCommand::PARAM>(((param ^ 0x80) - 0x70) * 8));
|
||||
break;
|
||||
default:
|
||||
if(command < 0x10)
|
||||
CSoundFile::ConvertModCommand(m, command, m.param);
|
||||
else
|
||||
m.command = CMD_NONE;
|
||||
if((command > 0 || param) && command < 0x10)
|
||||
CSoundFile::ConvertModCommand(m, command, param);
|
||||
break;
|
||||
}
|
||||
return std::make_pair(CMD_NONE, ModCommand::PARAM(0));
|
||||
}
|
||||
|
||||
|
||||
static bool TranslateMEDPattern(FileReader &file, FileReader &cmdExt, CPattern &pattern, const TranslateMEDPatternContext ctx, const bool isExtraPage)
|
||||
{
|
||||
bool needInstruments = false;
|
||||
for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
|
||||
{
|
||||
ModCommand *m = pattern.GetpModCommand(row, 0);
|
||||
for(CHANNELINDEX chn = 0; chn < ctx.numTracks; chn++, m++)
|
||||
{
|
||||
auto oldCmd = std::make_pair(m->command, m->param);
|
||||
int note = NOTE_NONE;
|
||||
uint8 cmd = 0, param1 = 0, param2 = 0;
|
||||
if(ctx.version < 1)
|
||||
{
|
||||
const auto [noteInstr, instrCmd, param] = file.ReadArray<uint8, 3>();
|
||||
|
||||
if(noteInstr & 0x3F)
|
||||
note = (noteInstr & 0x3F) + ctx.transpose;
|
||||
|
||||
m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1);
|
||||
|
||||
cmd = instrCmd & 0x0F;
|
||||
param1 = param;
|
||||
} else if(isExtraPage)
|
||||
{
|
||||
const auto [command, param] = file.ReadArray<uint8, 2>();
|
||||
param2 = cmdExt.ReadUint8();
|
||||
cmd = command;
|
||||
param1 = param;
|
||||
} else
|
||||
{
|
||||
const auto [noteVal, instr, command, param] = file.ReadArray<uint8, 4>();
|
||||
param2 = cmdExt.ReadUint8();
|
||||
|
||||
if(noteVal & 0x7F)
|
||||
note = (noteVal & 0x7F) + ctx.transpose;
|
||||
else if(noteVal == 0x80)
|
||||
m->note = NOTE_NOTECUT;
|
||||
|
||||
if(instr & 0x3F)
|
||||
m->instr = instr & 0x3F;
|
||||
cmd = command;
|
||||
param1 = param;
|
||||
}
|
||||
// Octave wrapping for 4-channel modules
|
||||
if(ctx.hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12)
|
||||
needInstruments = true;
|
||||
|
||||
if(note >= NOTE_MIN && note <= NOTE_MAX)
|
||||
m->note = static_cast<ModCommand::NOTE>(note);
|
||||
|
||||
if(!cmd && !param1)
|
||||
continue;
|
||||
const auto extraCmd = ConvertMEDEffect(*m, cmd, param1, param2, ctx);
|
||||
|
||||
if(oldCmd.first != CMD_NONE && m->command != oldCmd.first)
|
||||
{
|
||||
if(!ModCommand::CombineEffects(m->command, m->param, oldCmd.first, oldCmd.second) && m->volcmd == VOLCMD_NONE)
|
||||
m->FillInTwoCommands(m->command, m->param, oldCmd.first, oldCmd.second);
|
||||
// Reset X-Param to 8-bit value if this cell was overwritten with a "useful" effect
|
||||
if(row > 0 && oldCmd.first == CMD_XPARAM && m->command != CMD_XPARAM)
|
||||
pattern.GetpModCommand(row - 1, chn)->param = Util::MaxValueOfType(m->param);
|
||||
}
|
||||
if(extraCmd.first != CMD_NONE)
|
||||
{
|
||||
if(row < (pattern.GetNumRows() - 1))
|
||||
pattern.GetpModCommand(row + 1, chn)->SetEffectCommand(extraCmd);
|
||||
else
|
||||
m->param = Util::MaxValueOfType(m->param); // No space :(
|
||||
}
|
||||
}
|
||||
}
|
||||
return needInstruments;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MPT_WITH_VST
|
||||
static std::wstring ReadMEDStringUTF16BE(FileReader &file)
|
||||
{
|
||||
|
@ -761,12 +796,13 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// - 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));
|
||||
m_nMinPeriod = hardwareMixSamples ? (113 * 4) : (55 * 4);
|
||||
|
||||
bool needInstruments = false;
|
||||
bool anySynthInstrs = false;
|
||||
#ifdef MPT_WITH_VST
|
||||
#ifndef NO_PLUGINS
|
||||
PLUGINDEX numPlugins = 0;
|
||||
#endif // MPT_WITH_VST
|
||||
#endif // !NO_PLUGINS
|
||||
for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++)
|
||||
{
|
||||
if(!AllocateInstrument(ins, smp))
|
||||
|
@ -796,7 +832,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(type == L"VST")
|
||||
{
|
||||
auto &mixPlug = m_MixPlugins[numPlugins];
|
||||
mixPlug = {};
|
||||
mpt::reconstruct(mixPlug);
|
||||
mixPlug.Info.dwPluginId1 = Vst::kEffectMagic;
|
||||
mixPlug.Info.gain = 10;
|
||||
mixPlug.Info.szName = mpt::ToCharset(mpt::Charset::Locale, name);
|
||||
|
@ -818,6 +854,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
instr.AssignSample(0);
|
||||
}
|
||||
|
||||
MMD0Sample sampleHeader;
|
||||
sampleHeaderChunk.ReadStruct(sampleHeader);
|
||||
|
||||
uint8 numSamples = 1;
|
||||
static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7};
|
||||
if(!isSynth && maskedType < std::size(SamplesPerType))
|
||||
|
@ -850,10 +889,10 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// 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++)
|
||||
for(int note = 0, i = 12 * octave; note < 12; note++, i++)
|
||||
{
|
||||
instr.Keyboard[12 * octave + note] = smp + OctSampleMap[numSamples - 2][octave - 4];
|
||||
instr.NoteMap[12 * octave + note] += OctTransposeMap[numSamples - 2][octave - 4];
|
||||
instr.Keyboard[i] = smp + OctSampleMap[numSamples - 2][octave - 4];
|
||||
instr.NoteMap[i] = static_cast<uint8>(instr.NoteMap[i] + OctTransposeMap[numSamples - 2][octave - 4]);
|
||||
}
|
||||
}
|
||||
} else if(maskedType == MMDInstrHeader::EXTSAMPLE)
|
||||
|
@ -862,18 +901,14 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
instr.Transpose(-24);
|
||||
} else if(!isSynth && hardwareMixSamples)
|
||||
{
|
||||
for(int octave = 7; octave < 10; octave++)
|
||||
for(auto ¬e : instr.NoteMap)
|
||||
{
|
||||
for(int note = 0; note < 12; note++)
|
||||
{
|
||||
instr.NoteMap[12 * octave + note] -= static_cast<uint8>((octave - 6) * 12);
|
||||
}
|
||||
int realNote = note + sampleHeader.sampleTranspose;
|
||||
if(realNote >= NOTE_MIDDLEC + 24)
|
||||
note -= static_cast<uint8>(mpt::align_down(realNote - NOTE_MIDDLEC - 12, 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)
|
||||
{
|
||||
|
@ -884,7 +919,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(!isSynth)
|
||||
{
|
||||
auto &mixPlug = m_MixPlugins[numPlugins];
|
||||
mixPlug = {};
|
||||
mpt::reconstruct(mixPlug);
|
||||
mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P');
|
||||
mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D');
|
||||
mixPlug.Info.gain = 10;
|
||||
|
@ -1121,7 +1156,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
ChnSettings[chn].nVolume = std::min<uint8>(file.ReadUint8(), 64);
|
||||
}
|
||||
}
|
||||
if(header.trackPanOffset && file.Seek(header.trackPanOffset))
|
||||
if((freePan || version > 2) && header.trackPanOffset && file.Seek(header.trackPanOffset))
|
||||
{
|
||||
for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
|
||||
{
|
||||
|
@ -1132,6 +1167,35 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
SetupMODPanning(true);
|
||||
}
|
||||
|
||||
#ifndef NO_PLUGINS
|
||||
if((header.mixEchoType == 1 || header.mixEchoType == 2) && numPlugins < MAX_MIXPLUGINS)
|
||||
{
|
||||
// Emulating MED echo using the DMO echo requires to compensate for the differences in initial feedback in the latter.
|
||||
const float feedback = 1.0f / (1 << std::max(header.mixEchoDepth, uint8(1))); // The feedback we want
|
||||
const float initialFeedback = std::sqrt(1.0f - (feedback * feedback)); // Actual strength of first delay's feedback
|
||||
const float wetFactor = feedback / initialFeedback; // Factor to compensate for this
|
||||
const float delay = (std::max(header.mixEchoLength.get(), uint16(1)) - 1) / 1999.0f;
|
||||
SNDMIXPLUGIN &mixPlug = m_MixPlugins[numPlugins];
|
||||
mpt::reconstruct(mixPlug);
|
||||
memcpy(&mixPlug.Info.dwPluginId1, "OMXD", 4);
|
||||
memcpy(&mixPlug.Info.dwPluginId2, "\x2C\x93\x3E\xEF", 4);
|
||||
mixPlug.Info.routingFlags = SNDMIXPLUGININFO::irApplyToMaster | SNDMIXPLUGININFO::irAutoSuspend;
|
||||
mixPlug.fDryRatio = 1.0f - wetFactor / (wetFactor + 1.0f);
|
||||
mixPlug.Info.gain = 10;
|
||||
mixPlug.Info.szName = "Echo";
|
||||
mixPlug.Info.szLibraryName = "Echo";
|
||||
|
||||
std::array<float32le, 6> params{};
|
||||
params[1] = 1.0f; // WetDryMix
|
||||
params[2] = feedback; // Feedback
|
||||
params[3] = delay; // LeftDelay
|
||||
params[4] = delay; // RightDelay
|
||||
params[5] = header.mixEchoType - 1.0f; // PanDelay
|
||||
mixPlug.pluginData.resize(sizeof(params));
|
||||
memcpy(mixPlug.pluginData.data(), params.data(), sizeof(params));
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<uint16be> sections;
|
||||
if(!file.Seek(header.sectionTableOffset)
|
||||
|| !file.CanRead(songHeader.songLength * 2)
|
||||
|
@ -1153,12 +1217,12 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(!order.empty())
|
||||
order.push_back(order.GetIgnoreIndex());
|
||||
|
||||
const size_t orderStart = order.size();
|
||||
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++)
|
||||
{
|
||||
|
@ -1198,12 +1262,15 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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<uint8, uint8>(songHeader.tempo2, 1, 32);
|
||||
if(bpmMode)
|
||||
if(song == 0)
|
||||
{
|
||||
m_nDefaultRowsPerBeat = rowsPerBeat;
|
||||
m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u;
|
||||
m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat);
|
||||
m_nDefaultSpeed = Clamp<uint8, uint8>(songHeader.tempo2, 1, 32);
|
||||
if(bpmMode)
|
||||
{
|
||||
m_nDefaultRowsPerBeat = rowsPerBeat;
|
||||
m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u;
|
||||
}
|
||||
}
|
||||
|
||||
if(songHeader.masterVol)
|
||||
|
@ -1316,8 +1383,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
CHANNELINDEX numTracks;
|
||||
ROWINDEX numRows;
|
||||
std::string patName;
|
||||
int transpose = NOTE_MIN + 47 + songHeader.playTranspose;
|
||||
FileReader cmdExt;
|
||||
int16 transpose = NOTE_MIN + 47 + songHeader.playTranspose;
|
||||
uint16 numPages = 0;
|
||||
FileReader cmdExt, commandPages;
|
||||
|
||||
if(version < 1)
|
||||
{
|
||||
|
@ -1339,16 +1407,27 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
file.Seek(patHeader.blockInfoOffset);
|
||||
MMDBlockInfo blockInfo;
|
||||
file.ReadStruct(blockInfo);
|
||||
if(file.Seek(blockInfo.nameOffset))
|
||||
if(blockInfo.nameLength
|
||||
&& blockInfo.nameOffset
|
||||
&& file.Seek(blockInfo.nameOffset))
|
||||
{
|
||||
// We have now chased four pointers to get this far... lovely format.
|
||||
file.ReadString<mpt::String::maybeNullTerminated>(patName, blockInfo.nameLength);
|
||||
}
|
||||
if(blockInfo.pageTableOffset
|
||||
&& file.Seek(blockInfo.pageTableOffset)
|
||||
&& file.CanRead(8))
|
||||
{
|
||||
numPages = file.ReadUint16BE();
|
||||
file.Skip(2);
|
||||
commandPages = file.ReadChunk(4 * numPages);
|
||||
}
|
||||
|
||||
if(blockInfo.cmdExtTableOffset
|
||||
&& file.Seek(blockInfo.cmdExtTableOffset)
|
||||
&& file.Seek(file.ReadUint32BE()))
|
||||
{
|
||||
cmdExt = file.ReadChunk(numTracks * numRows);
|
||||
cmdExt = file.ReadChunk(numTracks * numRows * (1 + numPages));
|
||||
}
|
||||
|
||||
file.Seek(offset);
|
||||
|
@ -1362,68 +1441,20 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
pattern.SetName(patName);
|
||||
LimitMax(numTracks, m_nChannels);
|
||||
|
||||
for(ROWINDEX row = 0; row < numRows; row++)
|
||||
TranslateMEDPatternContext context{transpose, numTracks, version, rowsPerBeat, hardwareMixSamples, is8Ch, bpmMode, volHex};
|
||||
needInstruments |= TranslateMEDPattern(file, cmdExt, pattern, context, false);
|
||||
|
||||
for(uint16 page = 0; page < numPages; page++)
|
||||
{
|
||||
ModCommand *m = pattern.GetpModCommand(row, 0);
|
||||
for(CHANNELINDEX chn = 0; chn < numTracks; chn++, m++)
|
||||
{
|
||||
const auto oldCmd = std::make_pair(m->command, m->param);
|
||||
int note = NOTE_NONE;
|
||||
uint8 cmd = 0;
|
||||
if(version < 1)
|
||||
{
|
||||
const auto [noteInstr, instrCmd, param] = file.ReadArray<uint8, 3>();
|
||||
|
||||
if(noteInstr & 0x3F)
|
||||
note = (noteInstr & 0x3F) + transpose;
|
||||
|
||||
m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1);
|
||||
|
||||
cmd = instrCmd & 0x0F;
|
||||
m->param = param;
|
||||
} else
|
||||
{
|
||||
const auto [noteVal, instr, command, param1] = file.ReadArray<uint8, 4>();
|
||||
m->vol = cmdExt.ReadUint8();
|
||||
|
||||
if(noteVal & 0x7F)
|
||||
note = (noteVal & 0x7F) + transpose;
|
||||
else if(noteVal == 0x80)
|
||||
m->note = NOTE_NOTECUT;
|
||||
|
||||
m->instr = instr & 0x3F;
|
||||
cmd = 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<ModCommand::NOTE>(note);
|
||||
const auto extraCmd = ConvertMEDEffect(*m, cmd, is8Ch, bpmMode, rowsPerBeat, volHex);
|
||||
|
||||
if(oldCmd.first == CMD_XPARAM)
|
||||
{
|
||||
// Restore X-Param if it was overwritten by an empty effect, or restrict to 8-bit value if this cell was overwritten with a "useful" effect
|
||||
if(m->command == CMD_NONE)
|
||||
m->SetEffectCommand(oldCmd);
|
||||
else if(row > 0)
|
||||
pattern.GetpModCommand(row - 1, chn)->param = Util::MaxValueOfType(m->param);
|
||||
}
|
||||
if(extraCmd.first != CMD_NONE)
|
||||
{
|
||||
if(row < (numRows - 1))
|
||||
pattern.GetpModCommand(row + 1, chn)->SetEffectCommand(extraCmd);
|
||||
else
|
||||
m->param = Util::MaxValueOfType(m->param); // No space :(
|
||||
}
|
||||
}
|
||||
const uint32 pageOffset = commandPages.ReadUint32BE();
|
||||
if(!pageOffset || !file.Seek(pageOffset))
|
||||
continue;
|
||||
TranslateMEDPattern(file, cmdExt, pattern, context, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix jump order commands
|
||||
for(const auto & [from, to] : jumpTargets)
|
||||
for(const auto &[from, to] : jumpTargets)
|
||||
{
|
||||
PATTERNINDEX pat;
|
||||
if(from > 0 && order.IsValidPat(from - 1))
|
||||
|
@ -1443,6 +1474,21 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
|||
numPatterns = pat + 1;
|
||||
}
|
||||
|
||||
if(numSongs > 1)
|
||||
{
|
||||
PATTERNINDEX firstPat = order.EnsureUnique(order.GetFirstValidIndex());
|
||||
if(firstPat != PATTERNINDEX_INVALID)
|
||||
{
|
||||
for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
|
||||
{
|
||||
Patterns[firstPat].WriteEffect(EffectWriter(CMD_CHANNELVOLUME, static_cast<ModCommand::PARAM>(ChnSettings[chn].nVolume)).Channel(chn).RetryNextRow());
|
||||
Patterns[firstPat].WriteEffect(EffectWriter(CMD_PANNING8, mpt::saturate_cast<ModCommand::PARAM>(ChnSettings[chn].nPan)).Channel(chn).RetryNextRow());
|
||||
}
|
||||
if(firstPat >= numPatterns)
|
||||
numPatterns = firstPat + 1;
|
||||
}
|
||||
}
|
||||
|
||||
basePattern += numPatterns;
|
||||
|
||||
if(!expData.nextModOffset || !file.Seek(expData.nextModOffset))
|
||||
|
|
|
@ -270,9 +270,12 @@ const char *szMidiPercussionNames[61] =
|
|||
};
|
||||
|
||||
|
||||
static constexpr uint8 NUM_MIDI_CHANNELS = 32;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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)
|
||||
uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<NUM_MIDI_CHANNELS> drumChns)
|
||||
{
|
||||
ModInstrument *pIns;
|
||||
program &= 0x7F;
|
||||
|
@ -361,6 +364,7 @@ struct TrackState
|
|||
FileReader track;
|
||||
tick_t nextEvent = 0;
|
||||
uint8 command = 0;
|
||||
uint8 midiBaseChannel = 0;
|
||||
bool finished = false;
|
||||
};
|
||||
|
||||
|
@ -506,7 +510,7 @@ static CHANNELINDEX FindUnusedChannel(uint8 midiCh, ModCommand::NOTE note, const
|
|||
}
|
||||
|
||||
|
||||
static void MIDINoteOff(MidiChannelState &midiChn, std::vector<ModChannelState> &modChnStatus, uint8 note, uint8 delay, mpt::span<ModCommand> patRow, std::bitset<16> drumChns)
|
||||
static void MIDINoteOff(MidiChannelState &midiChn, std::vector<ModChannelState> &modChnStatus, uint8 note, uint8 delay, mpt::span<ModCommand> patRow, std::bitset<NUM_MIDI_CHANNELS> drumChns)
|
||||
{
|
||||
CHANNELINDEX chn = midiChn.noteOn[note];
|
||||
if(chn == CHANNELINDEX_INVALID)
|
||||
|
@ -666,13 +670,14 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
|||
ppqn = 96;
|
||||
Order().clear();
|
||||
|
||||
MidiChannelState midiChnStatus[16];
|
||||
std::array<MidiChannelState, NUM_MIDI_CHANNELS> midiChnStatus;
|
||||
const CHANNELINDEX tempoChannel = m_nChannels - 2, globalVolChannel = m_nChannels - 1;
|
||||
const uint16 numTracks = fileHeader.numTracks;
|
||||
std::vector<TrackState> tracks(numTracks);
|
||||
std::vector<ModChannelState> modChnStatus(m_nChannels);
|
||||
std::bitset<16> drumChns;
|
||||
std::bitset<NUM_MIDI_CHANNELS> drumChns;
|
||||
drumChns.set(MIDI_DRUMCHANNEL - 1);
|
||||
drumChns.set(MIDI_DRUMCHANNEL + 15);
|
||||
|
||||
tick_t timeShift = 0;
|
||||
for(auto &track : tracks)
|
||||
|
@ -820,6 +825,9 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
|||
case 8: // Patch name
|
||||
case 9: // Port name
|
||||
break;
|
||||
case 0x21: // MIDI port
|
||||
tracks[t].midiBaseChannel = chunk.ReadUint8() * 16u;
|
||||
break;
|
||||
case 0x2F: // End Of Track
|
||||
tracks[t].finished = true;
|
||||
break;
|
||||
|
@ -840,6 +848,14 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
|||
tempo = newTempo;
|
||||
}
|
||||
break;
|
||||
case 0x7F: // Sequencer specific
|
||||
{
|
||||
// Yamaha MIDI port selection
|
||||
uint32 data = chunk.ReadUint32BE();
|
||||
if(chunk.LengthIs(4) && (data & 0xFFFFFF00) == 0x43000100)
|
||||
tracks[t].midiBaseChannel = static_cast<uint8>((data & 0xFF) * 16u);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -857,7 +873,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
|||
data1 = track.ReadUint8();
|
||||
}
|
||||
}
|
||||
uint8 midiCh = command & 0x0F;
|
||||
const uint8 midiCh = ((command & 0x0F) + tracks[t].midiBaseChannel) % NUM_MIDI_CHANNELS;
|
||||
|
||||
switch(command & 0xF0)
|
||||
{
|
||||
|
@ -1079,7 +1095,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
|||
midiChnStatus[midiCh].SetPitchbend(data1 | (track.ReadUint8() << 7));
|
||||
break;
|
||||
case 0xF0: // General / Immediate
|
||||
switch(midiCh)
|
||||
switch(command & 0x0F)
|
||||
{
|
||||
case MIDIEvents::sysExStart: // SysEx
|
||||
case MIDIEvents::sysExEnd: // SysEx (continued)
|
||||
|
|
|
@ -740,7 +740,9 @@ static bool ValidateHeader(const MO3ContainerHeader &containerHeader)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= uint32_max / 2u)
|
||||
// Due to the LZ algorithm's unbounded back window, we could reach gigantic sizes with just a few dozen bytes.
|
||||
// 512 MB of music data (not samples) is chosen as a safeguard that is probably (hopefully) *way* beyond anything a real-world module will ever reach.
|
||||
if(containerHeader.musicSize <= sizeof(MO3FileHeader) || containerHeader.musicSize >= 0x2000'0000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1322,6 +1324,8 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
|||
sample.uFlags.set(CHN_STEREO);
|
||||
|
||||
FileReader sampleData = file.ReadChunk(smpHeader.compressedSize);
|
||||
if(!smpHeader.length)
|
||||
continue;
|
||||
const uint8 numChannels = sample.GetNumChannels();
|
||||
|
||||
if(compression == MO3Sample::smpDeltaCompression || compression == MO3Sample::smpDeltaPrediction)
|
||||
|
|
|
@ -322,6 +322,11 @@ struct MODSampleHeader
|
|||
+ ((loopStart > length * 2) ? 1 : 0);
|
||||
}
|
||||
|
||||
bool HasDiskName() const
|
||||
{
|
||||
return (!memcmp(name, "st-", 3) || !memcmp(name, "ST-", 3)) && name[5] == ':';
|
||||
}
|
||||
|
||||
// Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore
|
||||
static constexpr uint32 INVALID_BYTE_THRESHOLD = 40;
|
||||
|
||||
|
@ -543,9 +548,9 @@ static uint32 ReadSample(const MODSampleHeader &sampleHeader, ModSample &sample,
|
|||
|
||||
|
||||
// Count malformed bytes in MOD pattern data
|
||||
static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, const bool allow31Samples)
|
||||
static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, const bool extendedFormat)
|
||||
{
|
||||
const uint8 mask = allow31Samples ? 0xE0 : 0xF0;
|
||||
const uint8 mask = extendedFormat ? 0xE0 : 0xF0;
|
||||
uint32 malformedBytes = 0;
|
||||
for(const auto &row : patternData)
|
||||
{
|
||||
|
@ -553,6 +558,18 @@ static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, co
|
|||
{
|
||||
if(data[0] & mask)
|
||||
malformedBytes++;
|
||||
if(!extendedFormat)
|
||||
{
|
||||
const uint16 period = (((static_cast<uint16>(data[0]) & 0x0F) << 8) | data[1]);
|
||||
if(period && period != 0xFFF)
|
||||
{
|
||||
// Allow periods to deviate by +/-1 as found in some files
|
||||
const auto CompareFunc = [](uint16 l, uint16 r) { return l > (r + 1); };
|
||||
const auto PeriodTable = mpt::as_span(ProTrackerPeriodTable).subspan(24, 36);
|
||||
if(!std::binary_search(PeriodTable.begin(), PeriodTable.end(), period, CompareFunc))
|
||||
malformedBytes += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return malformedBytes;
|
||||
|
@ -561,12 +578,12 @@ static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, co
|
|||
|
||||
// Check if number of malformed bytes in MOD pattern data exceeds some threshold
|
||||
template <typename TFileReader>
|
||||
static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, const bool allow31Samples)
|
||||
static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, const bool extendedFormat)
|
||||
{
|
||||
MODPatternData patternData;
|
||||
if(!file.Read(patternData))
|
||||
return false;
|
||||
return CountMalformedMODPatternData(patternData, allow31Samples) <= threshold;
|
||||
return CountMalformedMODPatternData(patternData, extendedFormat) <= threshold;
|
||||
}
|
||||
|
||||
|
||||
|
@ -885,6 +902,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
SmpLength totalSampleLen = 0, wowSampleLen = 0;
|
||||
m_nSamples = 31;
|
||||
uint32 invalidBytes = 0;
|
||||
bool hasLongSamples = false;
|
||||
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
||||
{
|
||||
MODSampleHeader sampleHeader = ReadAndSwap<MODSampleHeader>(file, modMagicResult.swapBytes);
|
||||
|
@ -894,7 +912,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(isHMNT)
|
||||
Samples[smp].nFineTune = -static_cast<int8>(sampleHeader.finetune << 3);
|
||||
else if(Samples[smp].nLength > 65535)
|
||||
isNoiseTracker = false;
|
||||
hasLongSamples = true;
|
||||
|
||||
if(sampleHeader.length && !sampleHeader.loopLength)
|
||||
hasRepLen0 = true;
|
||||
|
@ -994,7 +1012,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
// 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.
|
||||
// In this case, the parameter of Dxx commands needs to be ignored (see 1.11song2.mod, 2-3song6.mod).
|
||||
// - 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;
|
||||
|
@ -1003,13 +1021,13 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
const uint8 ENABLE_MOD_PANNING_THRESHOLD = 0x30;
|
||||
if(!isNoiseTracker)
|
||||
{
|
||||
const uint32 patternLength = m_nChannels * 64;
|
||||
bool leftPanning = false, extendedPanning = false; // For detecting 800-880 panning
|
||||
isNoiseTracker = isMdKd;
|
||||
isNoiseTracker = isMdKd && !hasEmptySampleWithVolume && !hasLongSamples;
|
||||
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
||||
{
|
||||
uint16 patternBreaks = 0;
|
||||
|
||||
for(uint32 i = 0; i < 256; i++)
|
||||
for(uint32 i = 0; i < patternLength; i++)
|
||||
{
|
||||
ModCommand m;
|
||||
const auto data = ReadAndSwap<std::array<uint8, 4>>(file, modMagicResult.swapBytes && pat == 0);
|
||||
|
@ -1044,9 +1062,10 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
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
|
||||
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 = hasLongSamples; // 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;
|
||||
|
@ -1245,6 +1264,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
file.Seek(nextSample);
|
||||
}
|
||||
}
|
||||
if(isMdKd && file.ReadArray<char, 9>() == std::array<char, 9>{0x00, 0x11, 0x55, 0x33, 0x22, 0x11, 0x04, 0x01, 0x01})
|
||||
modMagicResult.madeWithTracker = UL_("Tetramed");
|
||||
}
|
||||
|
||||
#if defined(MPT_EXTERNAL_SAMPLES) || defined(MPT_BUILD_FUZZER)
|
||||
|
@ -1361,8 +1382,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
|
||||
// Check if a name string is valid (i.e. doesn't contain binary garbage data)
|
||||
template <size_t N>
|
||||
static uint32 CountInvalidChars(const char (&name)[N])
|
||||
static uint32 CountInvalidChars(const mpt::span<const char> name) noexcept
|
||||
{
|
||||
uint32 invalidChars = 0;
|
||||
for(int8 c : name) // char can be signed or unsigned
|
||||
|
@ -1375,6 +1395,34 @@ static uint32 CountInvalidChars(const char (&name)[N])
|
|||
}
|
||||
|
||||
|
||||
enum class NameClassification
|
||||
{
|
||||
Empty,
|
||||
ValidASCII,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
// Check if a name is a valid null-terminated ASCII string with no garbage after the null terminator, or if it's empty
|
||||
static NameClassification ClassifyName(const mpt::span<const char> name) noexcept
|
||||
{
|
||||
bool foundNull = false, foundNormal = false;
|
||||
for(auto c : name)
|
||||
{
|
||||
if(c > 0 && c < ' ')
|
||||
return NameClassification::Invalid;
|
||||
if(c == 0)
|
||||
foundNull = true;
|
||||
else if(foundNull)
|
||||
return NameClassification::Invalid;
|
||||
else
|
||||
foundNormal = true;
|
||||
}
|
||||
if(!foundNull)
|
||||
return NameClassification::Invalid;
|
||||
return foundNormal ? NameClassification::ValidASCII : NameClassification::Empty;
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
|
@ -1407,15 +1455,14 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
|||
// 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;
|
||||
}
|
||||
// or more than 5 invalid characters just in the title alone
|
||||
uint32 invalidCharsInTitle = CountInvalidChars(fileHeaders.songname);
|
||||
uint32 invalidChars = invalidCharsInTitle;
|
||||
|
||||
SmpLength totalSampleLen = 0;
|
||||
uint8 allVolumes = 0;
|
||||
uint8 validNameCount = 0;
|
||||
bool invalidNames = false;
|
||||
|
||||
for(SAMPLEINDEX smp = 0; smp < 15; smp++)
|
||||
{
|
||||
|
@ -1423,11 +1470,19 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
|||
|
||||
invalidChars += CountInvalidChars(sampleHeader.name);
|
||||
|
||||
// schmokk.mod has a non-zero value here but it should not be treated as finetune
|
||||
if(sampleHeader.finetune != 0)
|
||||
invalidChars += 16;
|
||||
if(const auto nameType = ClassifyName(sampleHeader.name); nameType == NameClassification::ValidASCII)
|
||||
validNameCount++;
|
||||
else if(nameType == NameClassification::Invalid)
|
||||
invalidNames = true;
|
||||
|
||||
// Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874)
|
||||
// Sample length adjusted for romantic.stk which has a (valid) sample of length 72222
|
||||
if(invalidChars > 48
|
||||
|| sampleHeader.volume > 64
|
||||
|| sampleHeader.finetune != 0
|
||||
|| sampleHeader.length > 32768)
|
||||
|| sampleHeader.length > 37000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1436,6 +1491,12 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
|||
allVolumes |= sampleHeader.volume;
|
||||
}
|
||||
|
||||
// scramble_2.mod has a lot of garbage in the song title, but it has lots of properly-formatted sample names, so we consider those to be more important than the garbage bytes.
|
||||
if(invalidCharsInTitle > 5 && (validNameCount < 4 || invalidNames))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
@ -1534,13 +1595,15 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
|||
file.Seek(20);
|
||||
for(SAMPLEINDEX smp = 1; smp <= 15; smp++)
|
||||
{
|
||||
ModSample &mptSmp = Samples[smp];
|
||||
MODSampleHeader sampleHeader;
|
||||
file.ReadStruct(sampleHeader);
|
||||
ReadSample(sampleHeader, Samples[smp], m_szNames[smp], true);
|
||||
mptSmp.nFineTune = 0;
|
||||
|
||||
totalSampleLen += Samples[smp].nLength;
|
||||
totalSampleLen += mptSmp.nLength;
|
||||
|
||||
if(m_szNames[smp][0] && ((memcmp(m_szNames[smp].buf, "st-", 3) && memcmp(m_szNames[smp].buf, "ST-", 3)) || m_szNames[smp][5] != ':'))
|
||||
if(m_szNames[smp][0] && sampleHeader.HasDiskName())
|
||||
{
|
||||
// Ultimate Soundtracker 1.8 and D.O.C. SoundTracker IX always have sample names containing disk names.
|
||||
hasDiskNames = false;
|
||||
|
@ -1549,9 +1612,9 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// 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();
|
||||
mptSmp.nLoopStart = sampleHeader.loopStart;
|
||||
mptSmp.nLoopEnd = sampleHeader.loopStart + sampleHeader.loopLength * 2;
|
||||
mptSmp.SanitizeLoops();
|
||||
}
|
||||
|
||||
// UST only handles samples up to 9999 bytes. Master Soundtracker 1.0 and SoundTracker 2.0 introduce 32KB samples.
|
||||
|
@ -1636,7 +1699,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// "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)
|
||||
if(illegalBytes > std::max(512u, numPatterns * 128u))
|
||||
return false;
|
||||
}
|
||||
for(ROWINDEX row = 0; row < 64; row++)
|
||||
|
@ -1829,7 +1892,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
}
|
||||
|
||||
const mpt::uchar *madeWithTracker = UL_("");
|
||||
[[maybe_unused]] /* silence clang-tidy deadcode.DeadStores */ const mpt::uchar *madeWithTracker = UL_("");
|
||||
switch(minVersion)
|
||||
{
|
||||
case UST1_00:
|
||||
|
|
|
@ -29,8 +29,8 @@ struct OktIffChunk
|
|||
idSBOD = MagicBE("SBOD"),
|
||||
};
|
||||
|
||||
uint32be signature; // IFF chunk name
|
||||
uint32be chunksize; // chunk size without header
|
||||
uint32be signature; // IFF chunk name
|
||||
uint32be chunksize; // chunk size without header
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(OktIffChunk, 8)
|
||||
|
@ -79,6 +79,19 @@ static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile)
|
|||
}
|
||||
|
||||
|
||||
// Turn negative arpeggio offset into equivalent positive arpeggio offset
|
||||
static uint8 InvertArpeggioParam(uint8 param)
|
||||
{
|
||||
param &= 0x0F;
|
||||
if(!param)
|
||||
return param;
|
||||
else if(param <= 0x0C)
|
||||
return (0x0C - param);
|
||||
else
|
||||
return (0x18 - param);
|
||||
}
|
||||
|
||||
|
||||
// Parse a pattern block
|
||||
static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndFile, const std::array<int8, 8> pairedChn)
|
||||
{
|
||||
|
@ -104,6 +117,8 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
|
|||
for(CHANNELINDEX chn = 0; chn < chns; chn++)
|
||||
{
|
||||
ModCommand &m = rowCmd[chn];
|
||||
const auto oldCmd = m.command;
|
||||
const auto oldParam = m.param;
|
||||
const auto [note, instr, effect, param] = chunk.ReadArray<uint8, 4>();
|
||||
|
||||
if(note > 0 && note <= 36)
|
||||
|
@ -147,18 +162,22 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
|
|||
}
|
||||
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)
|
||||
if(param)
|
||||
{
|
||||
m.command = CMD_ARPEGGIO;
|
||||
m.param = (param & 0x0F) | (InvertArpeggioParam(param >> 4) << 4);
|
||||
}
|
||||
break;
|
||||
|
||||
case 11: // B Arpeggio 2 (orig, up, orig, down)
|
||||
if(param)
|
||||
{
|
||||
m.command = CMD_ARPEGGIO;
|
||||
m.param = param;
|
||||
m.param = (param & 0xF0) | InvertArpeggioParam(param & 0x0F);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
// This one is close enough to "standard" arpeggio -- I think!
|
||||
case 12: // C Arpeggio 3 (up, up, orig)
|
||||
if(param)
|
||||
|
@ -286,9 +305,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
|
|||
#endif
|
||||
|
||||
default:
|
||||
m.command = CMD_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
// In case we overwrote the volume command from a mixed channel
|
||||
if(oldCmd != CMD_NONE && m.command != oldCmd)
|
||||
{
|
||||
m.FillInTwoCommands(m.command, m.param, oldCmd, oldParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -304,19 +328,6 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, co
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
@ -330,7 +341,6 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
return false;
|
||||
}
|
||||
|
||||
// prepare some arrays to store offsets etc.
|
||||
std::vector<FileReader> patternChunks;
|
||||
std::vector<FileReader> sampleChunks;
|
||||
std::array<int8, 8> pairedChn{{}};
|
||||
|
@ -347,15 +357,11 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
OktIffChunk iffHead;
|
||||
if(!file.ReadStruct(iffHead))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
FileReader chunk = file.ReadChunk(iffHead.chunksize);
|
||||
if(!chunk.IsValid())
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
|
||||
switch(iffHead.signature)
|
||||
{
|
||||
|
@ -433,12 +439,6 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
sampleChunks.push_back(chunk);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Non-ASCII chunk ID?
|
||||
if(iffHead.signature & 0x80808080)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -263,19 +263,6 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, co
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ void CSoundFile::S3MConvert(ModCommand &m, const uint8 command, const uint8 para
|
|||
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 'C': m.command = CMD_PATTERNBREAK; if(!fromIT) m.param = static_cast<uint8>((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;
|
||||
|
@ -86,8 +86,8 @@ void CSoundFile::S3MSaveConvert(const ModCommand &source, uint8 &command, uint8
|
|||
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_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;
|
||||
|
@ -110,11 +110,11 @@ void CSoundFile::S3MSaveConvert(const ModCommand &source, uint8 &command, uint8
|
|||
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; }
|
||||
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)))
|
||||
} else if(!toIT && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD)))
|
||||
{
|
||||
param >>= 1;
|
||||
}
|
||||
|
@ -240,54 +240,70 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
bool nonCompatTracker = false;
|
||||
bool isST3 = false;
|
||||
bool isSchism = false;
|
||||
const bool usePanningTable = fileHeader.usePanningTable == S3MFileHeader::idPanning;
|
||||
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");
|
||||
madeWithTracker = UL_("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)
|
||||
if(!memcmp(&fileHeader.reserved2, "SCLUB2.0", 8))
|
||||
{
|
||||
madeWithTracker = UL_("Sound Club 2");
|
||||
} else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0 && usePanningTable)
|
||||
{
|
||||
// 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");
|
||||
m_dwLastSavedWithVersion = MPT_V("1.16");
|
||||
madeWithTracker = UL_("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");
|
||||
// MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 alpha6 does.
|
||||
m_dwLastSavedWithVersion = MPT_V("1.00.00.A0");
|
||||
madeWithTracker = UL_("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)
|
||||
} else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 0 && !usePanningTable)
|
||||
{
|
||||
madeWithTracker = U_("Velvet Studio");
|
||||
if(fileHeader.globalVol == 64 && fileHeader.masterVolume == 48)
|
||||
madeWithTracker = UL_("PlayerPRO");
|
||||
else // Always stereo
|
||||
madeWithTracker = UL_("Velvet Studio");
|
||||
} else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 8 && !usePanningTable)
|
||||
{
|
||||
madeWithTracker = UL_("Impulse Tracker < 1.03"); // Not sure if 1.02 saves like this as I don't have it
|
||||
} 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");
|
||||
madeWithTracker = UL_("Scream Tracker");
|
||||
formatTrackerStr = true;
|
||||
isST3 = true;
|
||||
}
|
||||
break;
|
||||
case S3MFileHeader::trkImagoOrpheus:
|
||||
madeWithTracker = U_("Imago Orpheus");
|
||||
formatTrackerStr = true;
|
||||
formatTrackerStr = (fileHeader.cwtv != S3MFileHeader::trkPlayerPRO);
|
||||
if(formatTrackerStr)
|
||||
madeWithTracker = UL_("Imago Orpheus");
|
||||
else
|
||||
madeWithTracker = UL_("PlayerPRO");
|
||||
nonCompatTracker = true;
|
||||
break;
|
||||
case S3MFileHeader::trkImpulseTracker:
|
||||
if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14)
|
||||
{
|
||||
madeWithTracker = U_("Impulse Tracker");
|
||||
madeWithTracker = UL_("Impulse Tracker");
|
||||
formatTrackerStr = true;
|
||||
} else if(fileHeader.cwtv == S3MFileHeader::trkIT1_old)
|
||||
{
|
||||
madeWithTracker = UL_("Impulse Tracker 1.03"); // Could also be 1.02, maybe? I don't have that one
|
||||
} else
|
||||
{
|
||||
madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - S3MFileHeader::trkIT2_14);
|
||||
|
@ -311,7 +327,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
case S3MFileHeader::trkSchismTracker:
|
||||
if(fileHeader.cwtv == S3MFileHeader::trkBeRoTrackerOld)
|
||||
{
|
||||
madeWithTracker = U_("BeRoTracker");
|
||||
madeWithTracker = UL_("BeRoTracker");
|
||||
m_playBehaviour.set(kST3LimitPeriod);
|
||||
} else
|
||||
{
|
||||
|
@ -327,28 +343,40 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
nonCompatTracker = true;
|
||||
break;
|
||||
case S3MFileHeader::trkOpenMPT:
|
||||
if(fileHeader.cwtv != S3MFileHeader::trkGraoumfTracker)
|
||||
if((fileHeader.cwtv & 0xFF00) == S3MFileHeader::trkNESMusa)
|
||||
{
|
||||
madeWithTracker = UL_("NESMusa");
|
||||
formatTrackerStr = true;
|
||||
} else if(fileHeader.reserved2 == 0 && fileHeader.ultraClicks == 16 && fileHeader.channels[1] != 1)
|
||||
{
|
||||
// Liquid Tracker's ID clashes with OpenMPT's.
|
||||
// OpenMPT started writing full version information with OpenMPT 1.29 and later changed the ultraClicks value from 8 to 16.
|
||||
// Liquid Tracker writes an ultraClicks value of 16.
|
||||
// So we assume that a file was saved with Liquid Tracker if the reserved fields are 0 and ultraClicks is 16.
|
||||
madeWithTracker = UL_("Liquid Tracker");
|
||||
formatTrackerStr = true;
|
||||
} else if(fileHeader.cwtv != S3MFileHeader::trkGraoumfTracker)
|
||||
{
|
||||
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);
|
||||
madeWithTracker = UL_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion);
|
||||
} else
|
||||
{
|
||||
madeWithTracker = U_("Graoumf Tracker");
|
||||
madeWithTracker = UL_("Graoumf Tracker");
|
||||
}
|
||||
break;
|
||||
case S3MFileHeader::trkBeRoTracker:
|
||||
madeWithTracker = U_("BeRoTracker");
|
||||
madeWithTracker = UL_("BeRoTracker");
|
||||
m_playBehaviour.set(kST3LimitPeriod);
|
||||
break;
|
||||
case S3MFileHeader::trkCreamTracker:
|
||||
madeWithTracker = U_("CreamTracker");
|
||||
madeWithTracker = UL_("CreamTracker");
|
||||
break;
|
||||
default:
|
||||
if(fileHeader.cwtv == S3MFileHeader::trkCamoto)
|
||||
madeWithTracker = U_("Camoto");
|
||||
madeWithTracker = UL_("Camoto");
|
||||
break;
|
||||
}
|
||||
if(formatTrackerStr)
|
||||
|
@ -356,8 +384,8 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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.formatName = UL_("Scream Tracker 3");
|
||||
m_modFormat.type = UL_("s3m");
|
||||
m_modFormat.madeWithTracker = std::move(madeWithTracker);
|
||||
m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437;
|
||||
|
||||
|
@ -371,7 +399,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_playBehaviour.reset(kST3OffsetWithoutInstrument);
|
||||
m_playBehaviour.reset(kApplyUpperPeriodLimit);
|
||||
}
|
||||
if (fileHeader.cwtv <= S3MFileHeader::trkST3_01)
|
||||
if(fileHeader.cwtv <= S3MFileHeader::trkST3_01)
|
||||
{
|
||||
// This broken behaviour is not present in ST3.01
|
||||
m_playBehaviour.reset(kST3TonePortaWithAdlibNote);
|
||||
|
@ -439,15 +467,18 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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;
|
||||
|
||||
const bool isStereo = (fileHeader.masterVolume & 0x80) != 0 || m_dwLastSavedWithVersion;
|
||||
if(!isStereo)
|
||||
{
|
||||
m_nSamplePreAmp = Util::muldivr_unsigned(m_nSamplePreAmp, 8, 11);
|
||||
m_nVSTiVolume = Util::muldivr_unsigned(m_nVSTiVolume, 8, 11);
|
||||
}
|
||||
|
||||
// Channel setup
|
||||
m_nChannels = 4;
|
||||
std::bitset<32> isAdlibChannel;
|
||||
|
@ -489,17 +520,28 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
file.ReadVector(patternOffsets, fileHeader.patNum);
|
||||
|
||||
// Read extended channel panning
|
||||
if(fileHeader.usePanningTable == S3MFileHeader::idPanning)
|
||||
if(usePanningTable)
|
||||
{
|
||||
uint8 pan[32];
|
||||
file.ReadArray(pan);
|
||||
bool hasChannelsWithoutPanning = false;
|
||||
const auto pan = file.ReadArray<uint8, 32>();
|
||||
for(CHANNELINDEX i = 0; i < 32; i++)
|
||||
{
|
||||
if((pan[i] & 0x20) != 0 && (!isST3 || !isAdlibChannel[i]))
|
||||
{
|
||||
ChnSettings[i].nPan = (static_cast<uint16>(pan[i] & 0x0F) * 256 + 8) / 15;
|
||||
} else if(pan[i] < 0x10)
|
||||
{
|
||||
hasChannelsWithoutPanning = true;
|
||||
}
|
||||
}
|
||||
if(m_nChannels < 32 && m_dwLastSavedWithVersion == MPT_V("1.16"))
|
||||
{
|
||||
// MPT 1.0 alpha 6 up to 1.16.203 set ths panning bit for all channels, regardless of whether they are used or not.
|
||||
if(hasChannelsWithoutPanning)
|
||||
m_modFormat.madeWithTracker = UL_("ModPlug Tracker 1.16 / OpenMPT 1.17");
|
||||
else
|
||||
m_modFormat.madeWithTracker = UL_("ModPlug Tracker");
|
||||
}
|
||||
}
|
||||
|
||||
// Reading sample headers
|
||||
|
@ -520,11 +562,11 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
|
||||
{
|
||||
const uint32 sampleOffset = sampleHeader.GetSampleOffset();
|
||||
if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset))
|
||||
if(sampleHeader.length != 0)
|
||||
{
|
||||
SampleIO sampleIO = sampleHeader.GetSampleFormat((fileHeader.formatVersion == S3MFileHeader::oldVersion));
|
||||
sampleIO.ReadSample(Samples[smp + 1], file);
|
||||
if((loadFlags & loadSampleData) && file.Seek(sampleHeader.GetSampleOffset()))
|
||||
sampleIO.ReadSample(Samples[smp + 1], file);
|
||||
anySamples = true;
|
||||
if(sampleIO.GetEncoding() == SampleIO::ADPCM)
|
||||
anyADPCM = true;
|
||||
|
@ -540,7 +582,17 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// 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;
|
||||
MPT_UNUSED(isST3);
|
||||
m_modFormat.madeWithTracker = U_("Unknown");
|
||||
m_modFormat.madeWithTracker = UL_("Unknown");
|
||||
// Check these only after we are certain that it can't be ST3.01 because that version doesn't sanitize the ultraClicks value yet
|
||||
if(fileHeader.cwtv == S3MFileHeader::trkST3_01 && fileHeader.ultraClicks == 0)
|
||||
{
|
||||
if(!(fileHeader.flags & ~(S3MFileHeader::fastVolumeSlides | S3MFileHeader::amigaLimits)) && (fileHeader.masterVolume & 0x80) && usePanningTable)
|
||||
m_modFormat.madeWithTracker = UL_("UNMO3");
|
||||
else if(!fileHeader.flags && fileHeader.globalVol == 48 && fileHeader.masterVolume == 176 && fileHeader.tempo == 150 && !usePanningTable)
|
||||
m_modFormat.madeWithTracker = UL_("deMODifier"); // SoundSmith to S3M converter
|
||||
else if(!fileHeader.flags && fileHeader.globalVol == 64 && (fileHeader.masterVolume & 0x7F) == 48 && fileHeader.speed == 6 && fileHeader.tempo == 125 && !usePanningTable)
|
||||
m_modFormat.madeWithTracker = UL_("Kosmic To-S3M"); // MTM to S3M converter by Zab/Kosmic
|
||||
}
|
||||
} 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).
|
||||
|
@ -557,7 +609,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
if(anyADPCM)
|
||||
m_modFormat.madeWithTracker += U_(" (ADPCM packed)");
|
||||
m_modFormat.madeWithTracker += UL_(" (ADPCM packed)");
|
||||
|
||||
// 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
|
||||
|
@ -839,7 +891,8 @@ bool CSoundFile::SaveS3M(std::ostream &f) const
|
|||
|
||||
// Write patterns
|
||||
enum class S3MChannelType : uint8 { kUnused = 0, kPCM = 1, kAdlib = 2 };
|
||||
FlagSet<S3MChannelType> channelType[32] = { S3MChannelType::kUnused };
|
||||
std::array<FlagSet<S3MChannelType>, 32> channelType;
|
||||
channelType.fill(S3MChannelType::kUnused);
|
||||
bool globalCmdOnMutedChn = false;
|
||||
for(PATTERNINDEX pat = 0; pat < writePatterns; pat++)
|
||||
{
|
||||
|
|
|
@ -207,6 +207,8 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags)
|
|||
SFXSampleHeader sampleHeader;
|
||||
|
||||
file.ReadStruct(sampleHeader);
|
||||
// cppcheck false-positive
|
||||
// cppcheck-suppress uninitvar
|
||||
sampleHeader.ConvertToMPT(Samples[smp], sampleLen[smp - 1]);
|
||||
|
||||
// Get rid of weird characters in sample names.
|
||||
|
|
|
@ -230,6 +230,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
return true;
|
||||
|
||||
InitializeGlobals(MOD_TYPE_STM);
|
||||
InitializeChannels();
|
||||
|
||||
m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname);
|
||||
|
||||
|
@ -258,13 +259,6 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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++)
|
||||
|
@ -461,6 +455,7 @@ bool CSoundFile::ReadSTX(FileReader &file, ModLoadingFlags loadFlags)
|
|||
return true;
|
||||
|
||||
InitializeGlobals(MOD_TYPE_STM);
|
||||
InitializeChannels();
|
||||
|
||||
m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName);
|
||||
|
||||
|
@ -479,13 +474,6 @@ bool CSoundFile::ReadSTX(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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<uint16le> patternOffsets, sampleOffsets;
|
||||
file.Seek(fileHeader.patTableOffset << 4);
|
||||
file.ReadVector(patternOffsets, fileHeader.numPatterns);
|
||||
|
|
|
@ -441,18 +441,16 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
const auto [instr, note, command, param] = file.ReadArray<uint8, 4>();
|
||||
|
||||
m.instr = instr;
|
||||
m.note = note;
|
||||
m.param = param;
|
||||
|
||||
if(m.note)
|
||||
if(note)
|
||||
{
|
||||
m.note += 24 + NOTE_MIN;
|
||||
m.note = NOTE_MIDDLEC - 36 + note;
|
||||
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);
|
||||
// Volume slides not only have their nibbles swapped, but the up and down parameters also add up
|
||||
const int totalSlide = -static_cast<int>(m.param >> 4) + (m.param & 0x0F);
|
||||
const uint8 slideParam = static_cast<uint8>((totalSlide > 0) ? totalSlide << 4 : -totalSlide);
|
||||
|
||||
if((command & 0xF0) == 0xF0)
|
||||
{
|
||||
|
@ -462,138 +460,93 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
m.param = mpt::saturate_round<ModCommand::PARAM>(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;
|
||||
globalVolSlide = slideParam;
|
||||
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;
|
||||
chnMem.autoVolSlide = slideParam;
|
||||
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;
|
||||
}
|
||||
if(totalSlide < 0)
|
||||
m.SetVolumeCommand(VOLCMD_VOLSLIDEDOWN, slideParam & 0x0F);
|
||||
else if(totalSlide > 0)
|
||||
m.SetVolumeCommand(VOLCMD_VOLSLIDEUP, slideParam >> 4);
|
||||
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);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 1 ^ (m.param ? 1 : 0));
|
||||
break;
|
||||
|
||||
case 0x0F: // set speed
|
||||
m.command = CMD_SPEED;
|
||||
speedFrac = m.param & 0x0F;
|
||||
m.param >>= 4;
|
||||
m.SetEffectCommand(CMD_SPEED, 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;
|
||||
|
@ -601,13 +554,10 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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())
|
||||
{
|
||||
|
@ -620,10 +570,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m.vol = m.param;
|
||||
}
|
||||
}
|
||||
|
||||
m.command = CMD_NONE;
|
||||
break;
|
||||
|
||||
case 0x17: // play only loop nn
|
||||
if(m.instr && m.instr <= loopInfo.size())
|
||||
{
|
||||
|
@ -637,10 +584,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
|
||||
}
|
||||
}
|
||||
|
||||
m.command = CMD_NONE;
|
||||
break;
|
||||
|
||||
case 0x18: // play sequence without loop
|
||||
if(m.instr && m.instr <= loopInfo.size())
|
||||
{
|
||||
|
@ -657,10 +601,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
nonLooped[m.instr - 1] = ++m_nSamples;
|
||||
m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
|
||||
}
|
||||
|
||||
m.command = CMD_NONE;
|
||||
break;
|
||||
|
||||
case 0x19: // play only loop nn without loop
|
||||
if(m.instr && m.instr <= loopInfo.size())
|
||||
{
|
||||
|
@ -674,54 +615,37 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m.instr = static_cast<ModCommand::INSTR>(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;
|
||||
if(totalSlide < 0) // slide down
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xB0 | (slideParam & 0x0F));
|
||||
else if(totalSlide > 0)
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xA0 | (slideParam >> 4));
|
||||
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);
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 0xC0 | (m.param & 0xF));
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x21: // note delay
|
||||
m.command = CMD_MODCMDEX;
|
||||
m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15));
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 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));
|
||||
m.SetEffectCommand(CMD_MODCMDEX, 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)
|
||||
{
|
||||
|
@ -732,9 +656,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m.command = CMD_TEMPO;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
m.command = CMD_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -665,7 +665,7 @@ struct SymInstrument
|
|||
loopLen = (loopLen << 16) + loopLenFine;
|
||||
|
||||
const double loopScale = static_cast<double>(mptSmp.nLength) / (100 << 16);
|
||||
loopStart = mpt::saturate_cast<SmpLength>(loopStart * loopScale);
|
||||
loopStart = std::min(mptSmp.nLength, mpt::saturate_cast<SmpLength>(loopStart * loopScale));
|
||||
loopLen = std::min(mptSmp.nLength - loopStart, mpt::saturate_cast<SmpLength>(loopLen * loopScale));
|
||||
} else if(mptSmp.HasSampleData())
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ struct UltFileHeader
|
|||
{
|
||||
char signature[14]; // "MAS_UTrack_V00"
|
||||
uint8 version; // '1'...'4'
|
||||
char songName[32]; // Song Name, not guaranteed to be null-terminated
|
||||
char songName[32]; // Song Name, space-padded
|
||||
uint8 messageLength; // Number of Lines
|
||||
};
|
||||
|
||||
|
@ -51,7 +51,7 @@ struct UltSample
|
|||
mptSmp.Initialize();
|
||||
mptSmp.Set16BitCuePoints();
|
||||
|
||||
mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename);
|
||||
mptSmp.filename = mpt::String::ReadBuf(mpt::String::spacePadded, filename);
|
||||
|
||||
if(sizeEnd <= sizeStart)
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ struct UltSample
|
|||
mptSmp.nSustainEnd = std::min(static_cast<SmpLength>(loopEnd), mptSmp.nLength);
|
||||
mptSmp.nVolume = volume;
|
||||
|
||||
mptSmp.nC5Speed = speed;
|
||||
mptSmp.nC5Speed = speed * 2; // Doubled to fit the note range
|
||||
if(finetune)
|
||||
{
|
||||
mptSmp.Transpose(finetune / (12.0 * 32768.0));
|
||||
|
@ -207,7 +207,7 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
|
|||
b = file.ReadUint8();
|
||||
}
|
||||
|
||||
m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE;
|
||||
m.note = (b > 0 && b < 97) ? (b + 23 + NOTE_MIN) : NOTE_NONE;
|
||||
|
||||
const auto [instr, cmd, para1, para2] = file.ReadArray<uint8, 4>();
|
||||
|
||||
|
@ -385,7 +385,7 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
InitializeGlobals(MOD_TYPE_ULT);
|
||||
m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName);
|
||||
m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName);
|
||||
|
||||
const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")};
|
||||
m_modFormat.formatName = U_("UltraTracker");
|
||||
|
@ -419,7 +419,7 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
|
|||
}
|
||||
|
||||
sampleHeader.ConvertToMPT(Samples[smp]);
|
||||
m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name);
|
||||
m_szNames[smp] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
|
||||
}
|
||||
|
||||
ReadOrderFromFile<uint8>(Order(), file, 256, 0xFF, 0xFE);
|
||||
|
|
|
@ -244,18 +244,16 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo
|
|||
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;
|
||||
const uint32 headerSize = file.ReadUint32LE();
|
||||
if(headerSize < 8 || !file.CanRead(headerSize - 4))
|
||||
break;
|
||||
file.Skip(1); // Pack method (= 0)
|
||||
|
||||
ROWINDEX numRows;
|
||||
if(fileHeader.version == 0x0102)
|
||||
{
|
||||
numRows = file.ReadUint8() + 1;
|
||||
} else
|
||||
{
|
||||
else
|
||||
numRows = file.ReadUint16LE();
|
||||
}
|
||||
|
||||
// A packed size of 0 indicates a completely empty pattern.
|
||||
const uint16 packedSize = file.ReadUint16LE();
|
||||
|
@ -268,10 +266,8 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo
|
|||
file.Seek(curPos + headerSize);
|
||||
FileReader patternChunk = file.ReadChunk(packedSize);
|
||||
|
||||
if(!sndFile.Patterns.Insert(pat, numRows) || packedSize == 0)
|
||||
{
|
||||
if(pat >= MAX_PATTERNS || !sndFile.Patterns.Insert(pat, numRows) || packedSize == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
enum PatternFlags
|
||||
{
|
||||
|
@ -287,6 +283,9 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo
|
|||
|
||||
for(auto &m : sndFile.Patterns[pat])
|
||||
{
|
||||
if(!file.CanRead(1))
|
||||
break;
|
||||
|
||||
uint8 info = patternChunk.ReadUint8();
|
||||
|
||||
uint8 vol = 0, command = 0;
|
||||
|
@ -359,19 +358,20 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo
|
|||
|
||||
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.
|
||||
verUnknown = 0x00, // Probably not made with MPT
|
||||
verOldModPlug = 0x01, // Made with MPT Alpha / Beta
|
||||
verNewModPlug = 0x02, // Made with MPT (not Alpha / Beta)
|
||||
verModPlugBidiFlag = 0x04, // MPT up to v1.11 sets both normal loop and pingpong loop flags
|
||||
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)
|
||||
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
|
||||
verPlayerPRO = 0x100, // Could be PlayerPRO
|
||||
verDigiTrakker = 0x200, // Probably DigiTrakker
|
||||
verUNMO3 = 0x400, // TODO: UNMO3-ed XMs are detected as MPT 1.16
|
||||
verEmptyOrders = 0x800, // 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)
|
||||
|
||||
|
@ -480,6 +480,13 @@ static bool ReadSampleData(ModSample &sample, SampleIO sampleFlags, FileReader &
|
|||
{
|
||||
decodedSamples = ret;
|
||||
LimitMax(decodedSamples, mpt::saturate_cast<long>(sample.nLength - offset));
|
||||
if(offset == 0 && channels == 1 && sample.GetNumChannels() == 2)
|
||||
{
|
||||
// oggmod doesn't know what stereo samples are, so it treats them as mono samples, but doesn't clear the unknown stereo flag.
|
||||
// We just take the left channel in this case, as it is difficult (if possible at all) to properly reconstruct the waveform of the right channel.
|
||||
// Due to XM's delta-encoding and Vorbis being a lossless codec, samples could distort easily even when the delta encoding was off by a very small amount.
|
||||
sample.uFlags.reset(CHN_STEREO);
|
||||
}
|
||||
if(decodedSamples > 0 && channels == sample.GetNumChannels())
|
||||
{
|
||||
if(sample.uFlags[CHN_16BIT])
|
||||
|
@ -606,13 +613,30 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
if(!memcmp(fileHeader.trackerName, "FastTracker v2.00 ", 20) && fileHeader.size == 276)
|
||||
{
|
||||
const std::string_view songName{fileHeader.songName, sizeof(fileHeader.songName)};
|
||||
if(fileHeader.version < 0x0104)
|
||||
{
|
||||
madeWith = verFT2Generic | verConfirmed;
|
||||
else if(memchr(fileHeader.songName, '\0', 20) != nullptr)
|
||||
} else if(const auto firstNull = songName.find('\0'); firstNull != std::string_view::npos)
|
||||
{
|
||||
// FT2 pads the song title with spaces, some other trackers use null chars
|
||||
madeWith = verFT2Clone | verNewModPlug | verEmptyOrders;
|
||||
else
|
||||
madeWith = verFT2Generic | verNewModPlug;
|
||||
// PlayerPRO filles the remaining buffer after the null terminator with space characters.
|
||||
// PlayerPRO does not support song restart position.
|
||||
if(fileHeader.restartPos)
|
||||
madeWith = verFT2Clone | verNewModPlug | verEmptyOrders;
|
||||
else if(firstNull == songName.size() - 1)
|
||||
madeWith = verFT2Clone | verNewModPlug | verPlayerPRO | verEmptyOrders;
|
||||
else if(songName.find_first_not_of(' ', firstNull + 1) == std::string_view::npos)
|
||||
madeWith = verPlayerPRO | verConfirmed;
|
||||
else
|
||||
madeWith = verFT2Clone | verNewModPlug | verEmptyOrders;
|
||||
} else
|
||||
{
|
||||
if(fileHeader.restartPos)
|
||||
madeWith = verFT2Generic | verNewModPlug;
|
||||
else
|
||||
madeWith = verFT2Generic | verNewModPlug | verPlayerPRO;
|
||||
}
|
||||
} else if(!memcmp(fileHeader.trackerName, "FastTracker v 2.00 ", 20))
|
||||
{
|
||||
// MPT 1.0 (exact version to be determined later)
|
||||
|
@ -646,6 +670,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// Fix arpeggios in kragle_-_happy_day.xm
|
||||
m_playBehaviour.reset(kFT2Arpeggio);
|
||||
isMadTracker = true;
|
||||
if(memcmp(fileHeader.trackerName + 15, "\0\0\0\0", 4))
|
||||
madeWithTracker = UL_("MadTracker 2 (registered)");
|
||||
else
|
||||
madeWithTracker = UL_("MadTracker 2");
|
||||
} else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14) || !memcmp(fileHeader.trackerName, "Sk@le Tracker\0", 14))
|
||||
{
|
||||
m_playBehaviour.reset(kFT2ST3OffsetOutOfRange);
|
||||
|
@ -673,10 +701,8 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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;
|
||||
}
|
||||
if(m_SongFlags[SONG_EXFILTERRANGE] && madeWith[verNewModPlug])
|
||||
madeWith = verFT2Clone | verNewModPlug | verConfirmed | verEmptyOrders;
|
||||
|
||||
ReadOrderFromFile<uint8>(Order(), file, fileHeader.orders);
|
||||
if(fileHeader.orders == 0 && !madeWith[verEmptyOrders])
|
||||
|
@ -696,13 +722,20 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
// 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<SampleIO> sampleFlags;
|
||||
uint8 sampleReserved = 0;
|
||||
int instrType = -1;
|
||||
int16 lastInstrType = -1, lastSampleReserved = -1;
|
||||
int64 lastSampleHeaderSize = -1;
|
||||
bool unsupportedSamples = false;
|
||||
bool anyADPCM = false;
|
||||
bool instrumentWithSamplesEncountered = false;
|
||||
|
||||
// Reading instruments
|
||||
for(INSTRUMENTINDEX instr = 1; instr <= m_nInstruments; instr++)
|
||||
{
|
||||
if(!AllocateInstrument(instr))
|
||||
return false;
|
||||
if(!file.CanRead(4))
|
||||
continue;
|
||||
|
||||
// First, try to read instrument header length...
|
||||
uint32 headerSize = file.ReadUint32LE();
|
||||
if(headerSize == 0)
|
||||
|
@ -744,38 +777,61 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
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.
|
||||
// 4-mat's eternity.xm has an empty instruments with a header size of 29.
|
||||
// Another module using that size is funky_dumbass.xm. Mysterious!
|
||||
// Note: This may happen when the XM Commenter by Aka (XMC.EXE) adds empty instruments at the end of the list,
|
||||
// which would explain the latter case, but in eternity.xm the empty slots are not at the end of the list.
|
||||
madeWith = verUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
if(AllocateInstrument(instr) == nullptr)
|
||||
{
|
||||
continue;
|
||||
if(instrHeader.size != 33)
|
||||
{
|
||||
madeWith.reset(verPlayerPRO);
|
||||
} else if(instrHeader.sampleHeaderSize > sizeof(XMSample) && madeWith[verPlayerPRO])
|
||||
{
|
||||
// Older PlayerPRO versions appear to write garbage in the sampleHeaderSize field, and it's different for each sample.
|
||||
// Note: FT2 NORMALLY writes sampleHeaderSize=40 for all samples, but for any instruments before the first
|
||||
// instrument that has numSamples != 0, sampleHeaderSize will be uninitialized. It will always be the same
|
||||
// value, though.
|
||||
if(instrumentWithSamplesEncountered || (lastSampleHeaderSize != -1 && instrHeader.sampleHeaderSize != lastSampleHeaderSize))
|
||||
madeWith = verPlayerPRO | verConfirmed;
|
||||
lastSampleHeaderSize = instrHeader.sampleHeaderSize;
|
||||
}
|
||||
}
|
||||
|
||||
instrHeader.ConvertToMPT(*Instruments[instr]);
|
||||
|
||||
if(instrType == -1)
|
||||
if(lastInstrType == -1)
|
||||
{
|
||||
instrType = instrHeader.type;
|
||||
} else if(instrType != instrHeader.type && madeWith[verFT2Generic])
|
||||
lastInstrType = instrHeader.type;
|
||||
} else if(lastInstrType != 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.
|
||||
// Note: This may happen when running an FT2-made XM through PutInst and adding new instrument slots.
|
||||
madeWith.reset(verFT2Generic);
|
||||
madeWith.set(verFT2Clone);
|
||||
}
|
||||
|
||||
if(instrHeader.numSamples > 0)
|
||||
{
|
||||
instrumentWithSamplesEncountered = true;
|
||||
|
||||
// Yep, there are some samples associated with this instrument.
|
||||
|
||||
// If MIDI settings are present, this is definitely not an old MPT or PlayerPRO.
|
||||
if((instrHeader.instrument.midiEnabled | instrHeader.instrument.midiChannel | instrHeader.instrument.midiProgram | instrHeader.instrument.muteComputer) != 0)
|
||||
madeWith.reset(verOldModPlug | verNewModPlug | verPlayerPRO);
|
||||
if(instrHeader.size != 263 || instrHeader.type != 0)
|
||||
madeWith.reset(verPlayerPRO);
|
||||
if(!madeWith[verConfirmed] && madeWith[verPlayerPRO])
|
||||
{
|
||||
// Definitely not an old MPT.
|
||||
madeWith.reset(verOldModPlug | verNewModPlug);
|
||||
// Note: Earlier (?) PlayerPRO versions do not seem to set the loop points to 0xFF (george_megas_-_q.xm)
|
||||
if((!(instrHeader.instrument.volFlags & XMInstrument::envLoop) && instrHeader.instrument.volLoopStart == 0xFF && instrHeader.instrument.volLoopEnd == 0xFF)
|
||||
|| (!(instrHeader.instrument.panFlags & XMInstrument::envLoop) && instrHeader.instrument.panLoopStart == 0xFF && instrHeader.instrument.panLoopEnd == 0xFF))
|
||||
{
|
||||
madeWith.set(verConfirmed);
|
||||
madeWith.reset(verNewModPlug);
|
||||
}
|
||||
}
|
||||
|
||||
// Read sample headers
|
||||
|
@ -810,6 +866,18 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
sampleSize[sample] = sampleHeader.length;
|
||||
sampleReserved |= sampleHeader.reserved;
|
||||
|
||||
if(sampleHeader.reserved != 0 && sampleHeader.reserved != 0xAD)
|
||||
madeWith.reset(verOldModPlug | verNewModPlug | verOpenMPT);
|
||||
|
||||
if(lastSampleReserved == -1)
|
||||
lastSampleReserved = sampleHeader.reserved;
|
||||
else if(lastSampleReserved != sampleHeader.reserved)
|
||||
madeWith.reset(verPlayerPRO);
|
||||
if(sampleHeader.pan != 128)
|
||||
madeWith.reset(verPlayerPRO);
|
||||
if((sampleHeader.finetune & 0x0F) && sampleHeader.finetune != 127)
|
||||
madeWith.reset(verPlayerPRO);
|
||||
|
||||
if(sample < sampleSlots.size())
|
||||
{
|
||||
SAMPLEINDEX mptSample = sampleSlots[sample];
|
||||
|
@ -818,12 +886,16 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
instrHeader.instrument.ApplyAutoVibratoToMPT(Samples[mptSample]);
|
||||
|
||||
m_szNames[mptSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
|
||||
if(madeWith[verFT2Generic | verFT2Clone] && madeWith[verNewModPlug | verPlayerPRO] && !madeWith[verConfirmed]
|
||||
&& (sampleHeader.reserved > 22 || std::find_if(std::begin(sampleHeader.name) + sampleHeader.reserved, std::end(sampleHeader.name), [](char c) { return c != ' '; }) != std::end(sampleHeader.name)))
|
||||
{
|
||||
// FT2 stores the sample name length here (it just copies the entire Pascal string, but that string might have ended with spaces even before space-padding it in the file, so we cannot do an exact length comparison)
|
||||
madeWith.reset(verFT2Generic);
|
||||
madeWith.set(verFT2Clone | verConfirmed);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
madeWith.set(verModPlugBidiFlag);
|
||||
}
|
||||
if(sampleFlags.back().GetEncoding() == SampleIO::ADPCM)
|
||||
anyADPCM = true;
|
||||
|
@ -882,6 +954,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
{
|
||||
m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR);
|
||||
madeWith.set(verConfirmed);
|
||||
madeWith.reset(verPlayerPRO);
|
||||
}
|
||||
|
||||
// Read midi config: "MIDI"
|
||||
|
@ -892,6 +965,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
m_MidiCfg.Sanitize();
|
||||
hasMidiConfig = true;
|
||||
madeWith.set(verConfirmed);
|
||||
madeWith.reset(verPlayerPRO);
|
||||
}
|
||||
|
||||
// Read pattern names: "PNAM"
|
||||
|
@ -906,6 +980,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
Patterns[pat].SetName(patName);
|
||||
}
|
||||
madeWith.set(verConfirmed);
|
||||
madeWith.reset(verPlayerPRO);
|
||||
}
|
||||
|
||||
// Read channel names: "CNAM"
|
||||
|
@ -917,6 +992,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
file.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[chn].szName, MAX_CHANNELNAME);
|
||||
}
|
||||
madeWith.set(verConfirmed);
|
||||
madeWith.reset(verPlayerPRO);
|
||||
}
|
||||
|
||||
// Read mix plugins information
|
||||
|
@ -927,19 +1003,27 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
if(file.GetPosition() != oldPos)
|
||||
{
|
||||
madeWith.set(verConfirmed);
|
||||
madeWith.reset(verPlayerPRO);
|
||||
}
|
||||
}
|
||||
|
||||
if(madeWith[verConfirmed])
|
||||
{
|
||||
if(madeWith[verModPlug1_09])
|
||||
if(madeWith[verModPlugBidiFlag])
|
||||
{
|
||||
m_dwLastSavedWithVersion = MPT_V("1.09.00.00");
|
||||
madeWithTracker = U_("ModPlug Tracker 1.09");
|
||||
} else if(madeWith[verNewModPlug])
|
||||
m_dwLastSavedWithVersion = MPT_V("1.11");
|
||||
madeWithTracker = U_("ModPlug Tracker 1.0 - 1.11");
|
||||
} else if(madeWith[verNewModPlug] && !madeWith[verPlayerPRO])
|
||||
{
|
||||
m_dwLastSavedWithVersion = MPT_V("1.16.00.00");
|
||||
madeWithTracker = U_("ModPlug Tracker 1.10 - 1.16");
|
||||
madeWithTracker = U_("ModPlug Tracker 1.0 - 1.16");
|
||||
} else if(madeWith[verNewModPlug] && madeWith[verPlayerPRO])
|
||||
{
|
||||
m_dwLastSavedWithVersion = MPT_V("1.16");
|
||||
madeWithTracker = U_("ModPlug Tracker 1.0 - 1.16 / PlayerPRO");
|
||||
} else if(!madeWith[verNewModPlug] && madeWith[verPlayerPRO])
|
||||
{
|
||||
madeWithTracker = U_("PlayerPRO");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -986,7 +1070,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
|
||||
if(madeWithTracker.empty())
|
||||
{
|
||||
if(madeWith[verDigiTrakker] && sampleReserved == 0 && (instrType ? instrType : -1) == -1)
|
||||
if(madeWith[verDigiTrakker] && sampleReserved == 0 && (lastInstrType ? lastInstrType : -1) == -1)
|
||||
{
|
||||
madeWithTracker = U_("DigiTrakker");
|
||||
} else if(madeWith[verFT2Generic])
|
||||
|
@ -1051,6 +1135,17 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
|||
#ifndef MODPLUG_NO_FILESAVE
|
||||
|
||||
|
||||
#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(14, 1, 0)
|
||||
// work-around massively confused GCC 13 optimizer:
|
||||
// /usr/include/c++/13/bits/stl_algobase.h:437:30: warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' writing between 3 and 9223372036854775806 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
|
||||
template <typename Tcont2, typename Tcont1>
|
||||
static MPT_NOINLINE Tcont1 & gcc_append(Tcont1 & cont1, const Tcont2 & cont2) {
|
||||
cont1.insert(cont1.end(), cont2.begin(), cont2.end());
|
||||
return cont1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport)
|
||||
{
|
||||
|
||||
|
@ -1212,7 +1307,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport)
|
|||
if(!p->IsEmpty())
|
||||
emptyPattern = false;
|
||||
|
||||
// Apparently, completely empty patterns are loaded as empty 64-row patterns in FT2, regardless of their original size.
|
||||
// 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)
|
||||
{
|
||||
|
@ -1322,7 +1417,11 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport)
|
|||
}
|
||||
}
|
||||
|
||||
samples.insert(samples.end(), additionalSamples.begin(), additionalSamples.end());
|
||||
#if MPT_GCC_AT_LEAST(13, 0, 0) && MPT_GCC_BEFORE(14, 1, 0)
|
||||
gcc_append(samples, additionalSamples);
|
||||
#else
|
||||
mpt::append(samples, additionalSamples);
|
||||
#endif
|
||||
} else
|
||||
{
|
||||
MemsetZero(insHeader);
|
||||
|
|
|
@ -50,6 +50,8 @@ struct XMFSampleHeader
|
|||
return false;
|
||||
if((flags & smpEnableLoop) && !loopEnd.get())
|
||||
return false;
|
||||
if(loopStart.get() > loopEnd.get() || loopStart.get() > length)
|
||||
return false;
|
||||
if(loopEnd.get() != 0 && (loopEnd.get() >= length || loopStart.get() >= loopEnd.get()))
|
||||
return false;
|
||||
return true;
|
||||
|
|
|
@ -91,6 +91,8 @@ struct MIDIMacroConfigData
|
|||
struct Macro
|
||||
{
|
||||
public:
|
||||
Macro() = default;
|
||||
Macro(const Macro &other) = default;
|
||||
Macro &operator=(const Macro &other) = default;
|
||||
Macro &operator=(const std::string_view other) noexcept
|
||||
{
|
||||
|
|
|
@ -79,7 +79,7 @@ struct NoInterpolation
|
|||
// FilterFunc: Functor for applying the resonant filter
|
||||
// MixFunc: Functor for mixing the computed sample data into the output buffer
|
||||
template<class Traits, class InterpolationFunc, class FilterFunc, class MixFunc>
|
||||
static void SampleLoop(ModChannel &chn, const CResampler &resampler, typename Traits::output_t * MPT_RESTRICT outBuffer, unsigned int numSamples)
|
||||
inline 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<const typename Traits::input_t *>(c.pCurrentSample);
|
||||
|
|
|
@ -127,16 +127,15 @@ void ModChannel::UpdateInstrumentVolume(const ModSample *smp, const ModInstrumen
|
|||
}
|
||||
|
||||
|
||||
ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping, bool ignoreArpeggio) const
|
||||
ModCommand::NOTE ModChannel::GetPluginNote(bool ignoreArpeggio) const
|
||||
{
|
||||
if(nArpeggioLastNote != NOTE_NONE && !ignoreArpeggio)
|
||||
{
|
||||
// If an arpeggio is playing, this definitely the last playing note, which may be different from the arpeggio base note stored in nNote.
|
||||
// If an arpeggio is playing, this definitely the last playing note, which may be different from the arpeggio base note stored in nLastNote.
|
||||
return nArpeggioLastNote;
|
||||
}
|
||||
ModCommand::NOTE plugNote = mpt::saturate_cast<ModCommand::NOTE>(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))
|
||||
ModCommand::NOTE plugNote = nLastNote;
|
||||
if(pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN))
|
||||
{
|
||||
plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN];
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ struct ModChannel
|
|||
|
||||
uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; }
|
||||
|
||||
ModCommand::NOTE GetPluginNote(bool realNoteMapping, bool ignoreArpeggio = false) const;
|
||||
ModCommand::NOTE GetPluginNote(bool ignoreArpeggio = false) 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(); }
|
||||
|
@ -208,6 +208,10 @@ struct ModChannel
|
|||
void InstrumentControl(uint8 param, const CSoundFile &sndFile);
|
||||
|
||||
int32 GetMIDIPitchBend() const noexcept { return (static_cast<int32>(microTuning) + 0x8000) / 4; }
|
||||
void SetMIDIPitchBend(const uint8 high, const uint8 low) noexcept
|
||||
{
|
||||
microTuning = static_cast<int16>(((high << 9) | (low << 2)) - 0x8000);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ OPENMPT_NAMESPACE_BEGIN
|
|||
// Translate sample properties between two given formats.
|
||||
void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
|
||||
{
|
||||
uFlags.reset(CHN_REVERSE); // Not supported by any native formats yet
|
||||
|
||||
// Convert between frequency and transpose values if necessary.
|
||||
if((!(toType & (MOD_TYPE_MOD | MOD_TYPE_XM))) && (fromType & (MOD_TYPE_MOD | MOD_TYPE_XM)))
|
||||
{
|
||||
|
@ -134,7 +136,7 @@ void ModSample::Initialize(MODTYPE type)
|
|||
nPan = 128;
|
||||
nVolume = 256;
|
||||
nGlobalVol = 64;
|
||||
uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_ADLIB | SMP_MODIFIED | SMP_KEEPONDISK);
|
||||
uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_REVERSE | CHN_ADLIB | SMP_MODIFIED | SMP_KEEPONDISK);
|
||||
if(type == MOD_TYPE_XM)
|
||||
{
|
||||
uFlags.set(CHN_PANNING);
|
||||
|
|
|
@ -125,6 +125,18 @@ ORDERINDEX ModSequence::GetPreviousOrderIgnoringSkips(const ORDERINDEX start) co
|
|||
}
|
||||
|
||||
|
||||
ORDERINDEX ModSequence::GetFirstValidIndex() const noexcept
|
||||
{
|
||||
const ORDERINDEX length = GetLength();
|
||||
for(ORDERINDEX ord = 0; ord < length; ord++)
|
||||
{
|
||||
if(IsValidPat(ord))
|
||||
return ord;
|
||||
}
|
||||
return ORDERINDEX_INVALID;
|
||||
}
|
||||
|
||||
|
||||
void ModSequence::Remove(ORDERINDEX posBegin, ORDERINDEX posEnd) noexcept
|
||||
{
|
||||
if(posEnd < posBegin || posEnd >= size())
|
||||
|
@ -484,6 +496,7 @@ bool ModSequenceSet::MergeSequences()
|
|||
firstSeq.reserve(firstOrder + lengthTrimmed);
|
||||
firstSeq.push_back(); // Separator item
|
||||
RestartPosToPattern(seqNum);
|
||||
patternsFixed.resize(m_sndFile.Patterns.Size(), SEQUENCEINDEX_INVALID); // Previous line might have increased pattern count
|
||||
for(ORDERINDEX ord = 0; ord < lengthTrimmed; ord++)
|
||||
{
|
||||
PATTERNINDEX pat = seq[ord];
|
||||
|
@ -522,6 +535,7 @@ bool ModSequenceSet::MergeSequences()
|
|||
}
|
||||
}
|
||||
m_Sequences.erase(m_Sequences.begin() + 1, m_Sequences.end());
|
||||
m_currentSeq = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,9 @@ public:
|
|||
ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const noexcept;
|
||||
ORDERINDEX GetNextOrderIgnoringSkips(const ORDERINDEX start) const noexcept;
|
||||
|
||||
// Returns the first item that contains a pattern that actually exists, ORDERINDEX_INVALID if no such item exists.
|
||||
ORDERINDEX GetFirstValidIndex() const noexcept;
|
||||
|
||||
// Find an order item that contains a given pattern number.
|
||||
ORDERINDEX FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt = 0, bool searchForward = true) const noexcept;
|
||||
|
||||
|
|
|
@ -33,7 +33,9 @@ OPENMPT_NAMESPACE_BEGIN
|
|||
// 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
|
||||
#ifdef MPT_BUILD_FUZZER
|
||||
#define MPT_RESAMPLER_TABLES_CACHED_ONSTARTUP
|
||||
#endif
|
||||
|
||||
#endif // LIBOPENMPT_BUILD
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ OPENMPT_NAMESPACE_BEGIN
|
|||
|
||||
RowVisitor::LoopState::LoopState(const ChannelStates &chnState, const bool ignoreRow)
|
||||
{
|
||||
// Rather than storing the exact loop count vector, we compute a FNV-1a 64-bit hash of it.
|
||||
// Rather than storing the exact loop count vector, we compute an FNV-1a 64-bit hash of it.
|
||||
// This means we can store the loop state in a small and fixed amount of memory.
|
||||
// In theory there is the possibility of hash collisions for different loop states, but in practice,
|
||||
// the relevant inputs for the hashing algorithm are extremely unlikely to produce collisions.
|
||||
|
@ -84,10 +84,15 @@ void RowVisitor::Initialize(bool reset)
|
|||
{
|
||||
auto &order = Order();
|
||||
const ORDERINDEX endOrder = order.GetLengthTailTrimmed();
|
||||
bool reserveLoopStates = true;
|
||||
m_visitedRows.resize(endOrder);
|
||||
if(reset)
|
||||
{
|
||||
m_visitedLoopStates.clear();
|
||||
reserveLoopStates = m_visitedLoopStates.empty();
|
||||
for(auto &loopState : m_visitedLoopStates)
|
||||
{
|
||||
loopState.second.clear();
|
||||
}
|
||||
m_rowsSpentInLoops = 0;
|
||||
}
|
||||
|
||||
|
@ -104,7 +109,7 @@ void RowVisitor::Initialize(bool reset)
|
|||
else
|
||||
visitedRows.resize(numRows, false);
|
||||
|
||||
if(!order.IsValidPat(ord))
|
||||
if(!reserveLoopStates || !order.IsValidPat(ord))
|
||||
continue;
|
||||
|
||||
const ROWINDEX startRow = std::min(static_cast<ROWINDEX>(reset ? 0 : visitedRows.size()), numRows);
|
||||
|
|
|
@ -11,13 +11,14 @@
|
|||
#pragma once
|
||||
|
||||
#include "openmpt/all/BuildSettings.hpp"
|
||||
|
||||
#include "../soundlib/ModSample.h"
|
||||
#include "../soundlib/SampleIO.h"
|
||||
|
||||
#include "openmpt/base/Endian.hpp"
|
||||
#include "Snd_defs.h"
|
||||
|
||||
OPENMPT_NAMESPACE_BEGIN
|
||||
|
||||
struct ModSample;
|
||||
class SampleIO;
|
||||
|
||||
// S3M File Header
|
||||
struct S3MFileHeader
|
||||
{
|
||||
|
@ -45,13 +46,16 @@ struct S3MFileHeader
|
|||
|
||||
trkAkord = 0x0208,
|
||||
trkST3_00 = 0x1300,
|
||||
trkST3_20 = 0x1320,
|
||||
trkST3_01 = 0x1301,
|
||||
trkST3_20 = 0x1320,
|
||||
trkIT1_old = 0x3320,
|
||||
trkIT2_07 = 0x3207,
|
||||
trkIT2_14 = 0x3214,
|
||||
trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012
|
||||
trkGraoumfTracker = 0x5447,
|
||||
trkNESMusa = 0x5700,
|
||||
trkCamoto = 0xCA00,
|
||||
trkPlayerPRO = 0x2013, // PlayerPRO on Intel doesn't byte-swap the tracker ID bytes
|
||||
};
|
||||
|
||||
// Flags
|
||||
|
|
|
@ -168,7 +168,7 @@ struct SFZFlexEG
|
|||
break;
|
||||
}
|
||||
|
||||
if(sustain < env.size())
|
||||
if(sustain < env.size() && !(envType == ENV_VOLUME && sustain == env.size() - 1u && env.back().value == 0))
|
||||
{
|
||||
env.nSustainStart = env.nSustainEnd = sustain;
|
||||
env.dwFlags.set(ENV_SUSTAIN);
|
||||
|
@ -307,7 +307,7 @@ struct SFZRegion
|
|||
};
|
||||
|
||||
size_t filenameOffset = 0;
|
||||
std::string filename, name;
|
||||
std::string filename, globalName, regionName;
|
||||
SFZEnvelope ampEnv, pitchEnv, filterEnv;
|
||||
std::vector<SFZFlexEG> flexEGs;
|
||||
SmpLength loopStart = 0, loopEnd = 0;
|
||||
|
@ -398,8 +398,10 @@ struct SFZRegion
|
|||
filename = control.defaultPath + value;
|
||||
filenameOffset = control.defaultPath.size();
|
||||
}
|
||||
else if(key == "global_label")
|
||||
globalName = value;
|
||||
else if(key == "region_label")
|
||||
name = value;
|
||||
regionName = value;
|
||||
else if(key == "lokey")
|
||||
keyLo = ReadKey(value, control);
|
||||
else if(key == "hikey")
|
||||
|
@ -761,6 +763,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
|||
DestroyInstrument(nInstr, deleteAssociatedSamples);
|
||||
if(nInstr > m_nInstruments) m_nInstruments = nInstr;
|
||||
Instruments[nInstr] = pIns;
|
||||
pIns->name = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, globals.globalName);
|
||||
|
||||
SAMPLEINDEX prevSmp = 0;
|
||||
for(auto ®ion : regions)
|
||||
|
@ -841,8 +844,8 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
|||
sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData());
|
||||
}
|
||||
|
||||
if(!region.name.empty())
|
||||
m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.name);
|
||||
if(!region.regionName.empty())
|
||||
m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.regionName);
|
||||
if(!m_szNames[smp][0])
|
||||
m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::PathString::FromUTF8(region.filename).GetFilenameBase().ToUnicode());
|
||||
|
||||
|
@ -1070,6 +1073,19 @@ static void WriteSFZEnvelope(std::ostream &f, double tickDuration, int index, co
|
|||
f << "\n// Release Node: " << static_cast<uint32>(env.nReleaseNode);
|
||||
}
|
||||
|
||||
static std::string SanitizeSFZString(std::string s, mpt::Charset sourceCharset)
|
||||
{
|
||||
using namespace std::literals;
|
||||
// Remove characters could trip up the parser
|
||||
std::string::size_type pos = 0;
|
||||
while((pos = s.find_first_of("<=\r\n\t\0"sv, pos)) != std::string::npos)
|
||||
{
|
||||
s[pos++] = ' ';
|
||||
}
|
||||
return mpt::ToCharset(mpt::Charset::UTF8, sourceCharset, s);
|
||||
}
|
||||
|
||||
|
||||
bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const
|
||||
{
|
||||
#ifdef MODPLUG_TRACKER
|
||||
|
@ -1092,10 +1108,6 @@ bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons
|
|||
const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast<double>(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)
|
||||
|
@ -1114,8 +1126,12 @@ bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons
|
|||
break;
|
||||
}
|
||||
|
||||
f << "\n\n<control>\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n";
|
||||
f << "<group>";
|
||||
f << "\n\n<control>\ndefault_path=" << sampleDirName.ToUTF8();
|
||||
if(const auto globalName = SanitizeSFZString(ins->name, GetCharsetInternal()); !globalName.empty())
|
||||
{
|
||||
f << "\n\n<global>\nglobal_label=" << globalName;
|
||||
}
|
||||
f << "\n\n<group>";
|
||||
f << "\nbend_up=" << ins->midiPWD * 100;
|
||||
f << "\nbend_down=" << -ins->midiPWD * 100;
|
||||
const uint32 cutoff = ins->IsCutoffEnabled() ? ins->GetCutoff() : 127;
|
||||
|
@ -1215,9 +1231,9 @@ bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons
|
|||
|
||||
|
||||
f << "\n\n<region>";
|
||||
if(!m_szNames[ins->Keyboard[i]].empty())
|
||||
if(const auto regionName = SanitizeSFZString(m_szNames[ins->Keyboard[i]], GetCharsetInternal()); !regionName.empty())
|
||||
{
|
||||
f << "\nregion_label=" << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[ins->Keyboard[i]]);
|
||||
f << "\nregion_label=" << regionName;
|
||||
}
|
||||
f << "\nsample=" << sampleName.GetFilename().ToUTF8();
|
||||
f << "\nlokey=" << i;
|
||||
|
|
|
@ -9,32 +9,36 @@
|
|||
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Sndfile.h"
|
||||
#include "ITTools.h"
|
||||
#include "Loaders.h"
|
||||
#include "mod_specifications.h"
|
||||
#include "S3MTools.h"
|
||||
#include "Sndfile.h"
|
||||
#include "Tagging.h"
|
||||
#include "tuningcollection.h"
|
||||
#include "WAVTools.h"
|
||||
#include "XMTools.h"
|
||||
#include "../common/FileReader.h"
|
||||
#include "../common/misc_util.h"
|
||||
#include "../common/version.h"
|
||||
#include "../soundlib/AudioCriticalSection.h"
|
||||
#include "../soundlib/ModSampleCopy.h"
|
||||
#include "mpt/format/join.hpp"
|
||||
#include "mpt/string/utility.hpp"
|
||||
#include "openmpt/base/Endian.hpp"
|
||||
|
||||
#ifdef MODPLUG_TRACKER
|
||||
#include "../mptrack/Moddoc.h"
|
||||
#include "Dlsbank.h"
|
||||
#endif // MODPLUG_TRACKER
|
||||
#include "../soundlib/AudioCriticalSection.h"
|
||||
#include "mpt/format/join.hpp"
|
||||
|
||||
#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 "mpt/string/utility.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
|
@ -276,6 +280,15 @@ bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoun
|
|||
#endif
|
||||
pIns->Convert(srcSong.GetType(), GetType());
|
||||
|
||||
if(pIns->pTuning && this != &srcSong)
|
||||
{
|
||||
CTuning *existingTuning = m_pTuningsTuneSpecific->FindIdenticalTuning(*pIns->pTuning);
|
||||
if(existingTuning)
|
||||
pIns->pTuning = existingTuning;
|
||||
else
|
||||
pIns->pTuning = m_pTuningsTuneSpecific->AddTuning(std::make_unique<CTuning>(*pIns->pTuning));
|
||||
}
|
||||
|
||||
// Copy all referenced samples over
|
||||
for(size_t i = 0; i < targetSample.size(); i++)
|
||||
{
|
||||
|
@ -2519,7 +2532,7 @@ struct IFFChunk
|
|||
|
||||
size_t GetLength() const
|
||||
{
|
||||
if(length == 0) // Broken files
|
||||
if(length == 0 && id == idBODY) // Broken files
|
||||
return std::numeric_limits<size_t>::max();
|
||||
return length;
|
||||
}
|
||||
|
@ -2729,6 +2742,8 @@ bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file, bool allow
|
|||
|
||||
static uint32 WriteIFFStringChunk(std::ostream &f, IFFChunk::ChunkIdentifiers id, const std::string &str)
|
||||
{
|
||||
if(str.empty())
|
||||
return 0;
|
||||
IFFChunk chunk{};
|
||||
chunk.id = id;
|
||||
chunk.length = static_cast<uint32>(str.size());
|
||||
|
|
|
@ -56,7 +56,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
|
|||
restrictedSampleDataView = file.GetPinnedView(CalculateEncodedSize(sample.nLength));
|
||||
sourceBuf = restrictedSampleDataView.data();
|
||||
fileSize = restrictedSampleDataView.size();
|
||||
if(sourceBuf == nullptr)
|
||||
if(fileSize < 1)
|
||||
return 0;
|
||||
} else
|
||||
{
|
||||
|
@ -947,17 +947,20 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength
|
|||
}
|
||||
}
|
||||
|
||||
else if(GetBitDepth() == 16 && GetChannelFormat() == stereoSplit && GetEncoding() == signedPCM && GetEndianness() == bigEndian)
|
||||
else if(GetBitDepth() == 16 && (GetChannelFormat() == stereoSplit || GetChannelFormat() == mono)
|
||||
&& (GetEncoding() == signedPCM || GetEncoding() == unsignedPCM) && GetEndianness() == bigEndian)
|
||||
{
|
||||
// Stereo signed split, big-endian
|
||||
MPT_ASSERT(len == numSamples * 4);
|
||||
for(uint8 chn = 0; chn < 2; chn++)
|
||||
// Stereo split / mono signed 16-bit, big-endian
|
||||
const uint8 numChannels = GetNumChannels();
|
||||
const uint16 offset = (GetEncoding() == unsignedPCM) ? 0x8000 : 0;
|
||||
MPT_ASSERT(len == numSamples * numChannels * 2);
|
||||
for(uint8 chn = 0; chn < numChannels; chn++)
|
||||
{
|
||||
const int16 *p = sample.sample16() + chn;
|
||||
for(SmpLength j = 0; j < numSamples; j++)
|
||||
{
|
||||
mpt::IO::Write(fb, mpt::as_be(*p));
|
||||
p += 2;
|
||||
mpt::IO::Write(fb, mpt::as_be(static_cast<int16>(static_cast<uint16>(*p) + offset)));
|
||||
p += numChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -552,6 +552,7 @@ enum PlayBehaviour
|
|||
kST3TonePortaWithAdlibNote, // Adlib note next to tone portamento is delayed until next row
|
||||
kITResetFilterOnPortaSmpChange, // Filter is reset on portamento if sample is swapped
|
||||
kITInitialNoteMemory, // Initial "last note memory" for each channel is C-0 and not "no note"
|
||||
kPluginDefaultProgramAndBank1, // Default program and bank is set to 1 for plugins, so if an instrument is set to either of those, the program / bank change event is not sent to the plugin
|
||||
|
||||
// Add new play behaviours here.
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ public:
|
|||
|
||||
if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative()))
|
||||
{
|
||||
if(!chn.dwFlags[CHN_LOOP])
|
||||
if(!chn.dwFlags[CHN_LOOP] || !loopLength)
|
||||
{
|
||||
// Past sample end.
|
||||
stopNote = true;
|
||||
|
@ -326,7 +326,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
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()))
|
||||
|| (m->IsNote() && m->instr && !m->IsPortamento()))
|
||||
{
|
||||
memory.chnSettings[i].ticksToRender = GetLengthMemory::IGNORE_CHANNEL;
|
||||
}
|
||||
|
@ -979,7 +979,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
break;
|
||||
}
|
||||
|
||||
if(m_playBehaviour[kST3EffectMemory] && param != 0)
|
||||
if(m_playBehaviour[kST3EffectMemory] && command != CMD_NONE && param != 0)
|
||||
{
|
||||
UpdateS3MEffectMemory(chn, param);
|
||||
}
|
||||
|
@ -1064,7 +1064,8 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
{
|
||||
if(m.command == CMD_OFFSET)
|
||||
{
|
||||
ProcessSampleOffset(chn, nChn, playState);
|
||||
if(!porta || !(GetType() & (MOD_TYPE_XM | MOD_TYPE_DBM)))
|
||||
ProcessSampleOffset(chn, nChn, playState);
|
||||
} else if(m.command == CMD_OFFSETPERCENTAGE)
|
||||
{
|
||||
SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 256));
|
||||
|
@ -1365,7 +1366,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
|||
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 ModSample *pSmp = &Samples[instr <= GetNumSamples() ? instr : 0];
|
||||
const auto oldInsVol = chn.nInsVol;
|
||||
ModCommand::NOTE note = chn.nNewNote;
|
||||
|
||||
|
@ -2707,8 +2708,8 @@ bool CSoundFile::ProcessEffects()
|
|||
if(m_playBehaviour[kMODSampleSwap])
|
||||
{
|
||||
// ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it
|
||||
// Test case: PTSwapEmpty.mod, PTInstrVolume.mod, SampleSwap.s3m
|
||||
if(!chn.IsSamplePlaying() && (chn.pModSample == nullptr || !chn.pModSample->HasSampleData()))
|
||||
// Test cases: PTSwapEmpty.mod, PTInstrVolume.mod, PTStoppedSwap.mod
|
||||
if(!chn.IsSamplePlaying() && instr <= GetNumSamples() && Samples[instr].uFlags[CHN_LOOP])
|
||||
keepInstr = true;
|
||||
}
|
||||
|
||||
|
@ -2951,7 +2952,8 @@ bool CSoundFile::ProcessEffects()
|
|||
}
|
||||
|
||||
NoteChange(chn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)), false, nChn);
|
||||
HandleDigiSamplePlayDirection(m_PlayState, nChn);
|
||||
if(ModCommand::IsNote(note))
|
||||
HandleDigiSamplePlayDirection(m_PlayState, nChn);
|
||||
if ((bPorta) && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr))
|
||||
{
|
||||
chn.dwFlags.set(CHN_FASTVOLRAMP);
|
||||
|
@ -3229,7 +3231,7 @@ bool CSoundFile::ProcessEffects()
|
|||
{
|
||||
// FT2 compatibility: Portamento + Offset = Ignore offset
|
||||
// Test case: porta-offset.xm
|
||||
if(bPorta && GetType() == MOD_TYPE_XM)
|
||||
if(bPorta && (GetType() & (MOD_TYPE_XM | MOD_TYPE_DBM)))
|
||||
break;
|
||||
|
||||
ProcessSampleOffset(chn, nChn, m_PlayState);
|
||||
|
@ -3553,7 +3555,7 @@ bool CSoundFile::ProcessEffects()
|
|||
break;
|
||||
}
|
||||
|
||||
if(m_playBehaviour[kST3EffectMemory] && param != 0)
|
||||
if(m_playBehaviour[kST3EffectMemory] && cmd != CMD_NONE && param != 0)
|
||||
{
|
||||
UpdateS3MEffectMemory(chn, static_cast<ModCommand::PARAM>(param));
|
||||
}
|
||||
|
@ -4156,7 +4158,7 @@ void CSoundFile::TonePortamento(CHANNELINDEX nChn, uint16 param)
|
|||
IMixPlugin *plugin = GetChannelInstrumentPlugin(chn);
|
||||
if(plugin != nullptr)
|
||||
{
|
||||
plugin->MidiTonePortamento(delta, chn.GetPluginNote(m_playBehaviour[kITRealNoteMapping], true), chn.pModInstrument->midiPWD, nChn);
|
||||
plugin->MidiTonePortamento(delta, chn.GetPluginNote(true), chn.pModInstrument->midiPWD, nChn);
|
||||
}
|
||||
}
|
||||
#endif // NO_PLUGINS
|
||||
|
@ -5163,12 +5165,17 @@ void CSoundFile::ParseMIDIMacro(PlayState &playState, CHANNELINDEX nChn, bool is
|
|||
{
|
||||
// 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)
|
||||
{
|
||||
while(startPos > 0 && out[--startPos] != 0xF0)
|
||||
;
|
||||
|
||||
if(outPos - startPos < 3 || out[startPos] != 0xF0)
|
||||
continue;
|
||||
}
|
||||
for(auto p = startPos + 5u; p != outPos; p++)
|
||||
|
||||
uint8 checksumStart = out[startPos + 3] ? 5 : 6;
|
||||
if(outPos - startPos < checksumStart)
|
||||
continue;
|
||||
|
||||
for(auto p = startPos + checksumStart; p != outPos; p++)
|
||||
{
|
||||
data += out[p];
|
||||
}
|
||||
|
@ -6147,10 +6154,16 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
|
|||
note -= NOTE_MIN;
|
||||
if(!UseFinetuneAndTranspose())
|
||||
{
|
||||
if(GetType() & (MOD_TYPE_MDL | MOD_TYPE_DTM))
|
||||
if(GetType() == MOD_TYPE_MDL)
|
||||
{
|
||||
// MDL uses non-linear slides, but their effectiveness does not depend on the middle-C frequency.
|
||||
MPT_ASSERT(!PeriodsAreFrequencies());
|
||||
return (FreqS3MTable[note % 12u] << 4) >> (note / 12);
|
||||
} else if(GetType() == MOD_TYPE_DTM)
|
||||
{
|
||||
// Similar to MDL, but finetune is factored in and we don't transpose everything by an octave
|
||||
MPT_ASSERT(!PeriodsAreFrequencies());
|
||||
return (ProTrackerTunedPeriods[XM2MODFineTune(nFineTune) * 12u + note % 12u] << 5) >> (note / 12u);
|
||||
}
|
||||
if(!nC5Speed)
|
||||
nC5Speed = 8363;
|
||||
|
@ -6266,8 +6279,9 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPerio
|
|||
{
|
||||
// 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))
|
||||
} else if(GetType() == MOD_TYPE_MDL)
|
||||
{
|
||||
MPT_ASSERT(!PeriodsAreFrequencies());
|
||||
LimitMax(period, Util::MaxValueOfType(period) >> 8);
|
||||
if (!c5speed) c5speed = 8363;
|
||||
return Util::muldiv_unsigned(c5speed, (1712L << 7) << FREQ_FRACBITS, (period << 8) + nPeriodFrac);
|
||||
|
@ -6279,7 +6293,7 @@ uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPerio
|
|||
// 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])
|
||||
} else if(m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_DTM)
|
||||
{
|
||||
if(!c5speed)
|
||||
c5speed = 8363;
|
||||
|
|
|
@ -699,13 +699,13 @@ 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.
|
||||
loadPluginData = 0x04, // If unset, plugin data is not loaded (and as a consequence, plugins are not instantiated).
|
||||
loadPluginInstance = 0x08, // If unset, plugins are not instantiated.
|
||||
skipContainer = 0x10,
|
||||
skipModules = 0x20,
|
||||
onlyVerifyHeader = 0x40, // Do not combine with other flags!
|
||||
|
||||
// Shortcuts
|
||||
loadCompleteModule = loadSampleData | loadPatternData | loadPluginData | loadPluginInstance,
|
||||
|
@ -1285,7 +1285,7 @@ public:
|
|||
// 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);
|
||||
uint32 MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<32> drumChns);
|
||||
size_t ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers);
|
||||
std::pair<bool, bool> LoadMixPlugins(FileReader &file);
|
||||
#ifndef NO_PLUGINS
|
||||
|
|
|
@ -200,6 +200,7 @@ CSoundFile::samplecount_t CSoundFile::ReadOneTick()
|
|||
{
|
||||
const auto origMaxMixChannels = m_MixerSettings.m_nMaxMixChannels;
|
||||
m_MixerSettings.m_nMaxMixChannels = 0;
|
||||
ResetMixStat();
|
||||
while(m_PlayState.m_nBufferCount)
|
||||
{
|
||||
auto framesToRender = std::min(m_PlayState.m_nBufferCount, samplecount_t(MIXBUFFERSIZE));
|
||||
|
@ -2387,6 +2388,9 @@ bool CSoundFile::ReadNote()
|
|||
// 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);
|
||||
#ifndef MODPLUG_TRACKER
|
||||
milliHertz = Util::muldivr_unsigned(milliHertz, m_nFreqFactor, 65536);
|
||||
#endif // !MODPLUG_TRACKER
|
||||
|
||||
const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0);
|
||||
if(!m_playBehaviour[kOPLNoteStopWith0Hz] || !keyOff)
|
||||
|
|
|
@ -352,8 +352,21 @@ void CSoundFile::UpgradeModule()
|
|||
}
|
||||
}
|
||||
|
||||
bool hasAnyPlugins = false;
|
||||
if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM))
|
||||
{
|
||||
for(auto &plugin : m_MixPlugins)
|
||||
{
|
||||
if(plugin.IsValidPlugin())
|
||||
{
|
||||
hasAnyPlugins = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NO_PLUGINS
|
||||
if(m_dwLastSavedWithVersion < MPT_V("1.22.07.01"))
|
||||
if(m_dwLastSavedWithVersion < MPT_V("1.22.07.01") && hasAnyPlugins)
|
||||
{
|
||||
// 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)
|
||||
|
@ -724,7 +737,7 @@ void CSoundFile::UpgradeModule()
|
|||
}
|
||||
}
|
||||
|
||||
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)))
|
||||
if(m_dwLastSavedWithVersion >= MPT_V("1.27.00.42") && m_dwLastSavedWithVersion < MPT_V("1.30.00.46") && hasAnyPlugins)
|
||||
{
|
||||
// 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.
|
||||
|
@ -736,20 +749,26 @@ void CSoundFile::UpgradeModule()
|
|||
}
|
||||
}
|
||||
|
||||
if(m_dwLastSavedWithVersion < MPT_V("1.31.00.09") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)))
|
||||
if(m_dwLastSavedWithVersion < MPT_V("1.30.00.54") && hasAnyPlugins)
|
||||
{
|
||||
// Old-style plugin tone portamento
|
||||
for(auto &plugin : m_MixPlugins)
|
||||
// Currently active program and bank is assumed to be 1 when starting playback
|
||||
for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
|
||||
{
|
||||
if(plugin.IsValidPlugin())
|
||||
if(Instruments[i] && (Instruments[i]->nMidiProgram == 1 || Instruments[i]->wMidiBank == 1))
|
||||
{
|
||||
m_playBehaviour.set(kPluginIgnoreTonePortamento);
|
||||
m_playBehaviour.set(kPluginDefaultProgramAndBank1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_dwLastSavedWithVersion >= MPT_V("1.27") && m_dwLastSavedWithVersion < MPT_V("1.30.06.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)))
|
||||
if(m_dwLastSavedWithVersion < MPT_V("1.31.00.09") && hasAnyPlugins)
|
||||
{
|
||||
// Old-style plugin tone portamento
|
||||
m_playBehaviour.set(kPluginIgnoreTonePortamento);
|
||||
}
|
||||
|
||||
if(m_dwLastSavedWithVersion >= MPT_V("1.27") && m_dwLastSavedWithVersion < MPT_V("1.30.06.00") && hasAnyPlugins)
|
||||
{
|
||||
// Fix off-by-one delay length in older Echo DMO emulation
|
||||
for(auto &plugin : m_MixPlugins)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "openmpt/all/BuildSettings.hpp"
|
||||
#include "openmpt/base/Endian.hpp"
|
||||
#include "Snd_defs.h"
|
||||
|
||||
|
||||
OPENMPT_NAMESPACE_BEGIN
|
||||
|
@ -21,24 +23,24 @@ struct XMFileHeader
|
|||
{
|
||||
enum XMHeaderFlags
|
||||
{
|
||||
linearSlides = 0x01,
|
||||
extendedFilterRange = 0x1000,
|
||||
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
|
||||
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)
|
||||
|
@ -50,35 +52,35 @@ struct XMInstrument
|
|||
// Envelope Flags
|
||||
enum XMEnvelopeFlags
|
||||
{
|
||||
envEnabled = 0x01,
|
||||
envSustain = 0x02,
|
||||
envLoop = 0x04,
|
||||
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
|
||||
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
|
||||
{
|
||||
|
@ -109,11 +111,11 @@ 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
|
||||
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)
|
||||
|
@ -133,16 +135,16 @@ struct XIInstrumentHeader
|
|||
{
|
||||
enum
|
||||
{
|
||||
fileVersion = 0x102,
|
||||
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)
|
||||
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
|
||||
uint16le numSamples; // Number of embedded sample headers + samples
|
||||
|
||||
// Convert OpenMPT's internal sample representation to an XIInstrumentHeader.
|
||||
void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport);
|
||||
|
@ -158,24 +160,23 @@ struct XMSample
|
|||
{
|
||||
enum XMSampleFlags
|
||||
{
|
||||
sampleLoop = 0x01,
|
||||
sampleBidiLoop = 0x02,
|
||||
sample16Bit = 0x10,
|
||||
sampleStereo = 0x20,
|
||||
|
||||
sampleADPCM = 0xAD, // MODPlugin :(
|
||||
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)
|
||||
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);
|
||||
|
|
|
@ -1173,7 +1173,7 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd)
|
|||
CMD_TONEPORTAMENTO,
|
||||
CMD_TONEPORTAVOL,
|
||||
CMD_DBMECHO,
|
||||
CMD_GLOBALVOLSLIDE,
|
||||
CMD_CHANNELVOLSLIDE,
|
||||
CMD_CHANNELVOLUME,
|
||||
CMD_GLOBALVOLSLIDE,
|
||||
CMD_GLOBALVOLUME,
|
||||
|
@ -1192,6 +1192,7 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd)
|
|||
}
|
||||
}
|
||||
// Invalid / unknown command.
|
||||
MPT_ASSERT_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1347,6 +1348,41 @@ bool ModCommand::CombineEffects(EffectCommand &eff1, uint8 ¶m1, EffectComman
|
|||
|
||||
std::pair<EffectCommand, ModCommand::PARAM> ModCommand::FillInTwoCommands(EffectCommand effect1, uint8 param1, EffectCommand effect2, uint8 param2)
|
||||
{
|
||||
if(effect1 == effect2)
|
||||
{
|
||||
// For non-sliding, absolute effects, it doesn't make sense to keep both commands
|
||||
switch(effect1)
|
||||
{
|
||||
case CMD_ARPEGGIO:
|
||||
case CMD_PANNING8:
|
||||
case CMD_OFFSET:
|
||||
case CMD_POSITIONJUMP:
|
||||
case CMD_VOLUME:
|
||||
case CMD_PATTERNBREAK:
|
||||
case CMD_SPEED:
|
||||
case CMD_TEMPO:
|
||||
case CMD_CHANNELVOLUME:
|
||||
case CMD_GLOBALVOLUME:
|
||||
case CMD_KEYOFF:
|
||||
case CMD_SETENVPOSITION:
|
||||
case CMD_MIDI:
|
||||
case CMD_SMOOTHMIDI:
|
||||
case CMD_DELAYCUT:
|
||||
case CMD_FINETUNE:
|
||||
case CMD_FINETUNE_SMOOTH:
|
||||
case CMD_DUMMY:
|
||||
case CMD_REVERSEOFFSET:
|
||||
case CMD_DBMECHO:
|
||||
case CMD_OFFSETPERCENTAGE:
|
||||
case CMD_DIGIREVERSESAMPLE:
|
||||
case CMD_VOLUME8:
|
||||
effect2 = CMD_NONE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8 n = 0; n < 4; n++)
|
||||
{
|
||||
if(auto volCmd = ModCommand::ConvertToVolCommand(effect1, param1, (n > 1)); effect1 == CMD_NONE || volCmd.first != VOLCMD_NONE)
|
||||
|
|
|
@ -11,9 +11,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "openmpt/all/BuildSettings.hpp"
|
||||
|
||||
#include "Snd_defs.h"
|
||||
#include "../common/misc_util.h"
|
||||
#include "mpt/base/algorithm.hpp"
|
||||
|
||||
OPENMPT_NAMESPACE_BEGIN
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
|
||||
#include <cstdint>
|
||||
#include "../common/mptBaseUtils.h"
|
||||
|
||||
|
||||
|
||||
|
@ -408,7 +409,11 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
|||
|
||||
// The 4-op channels are 0, 1, 2, 9, 10, 11
|
||||
uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6);
|
||||
// cppcheck false-positive
|
||||
// cppcheck-suppress arrayIndexOutOfBounds
|
||||
Channel *primary = &Chan[chan];
|
||||
// cppcheck false-positive
|
||||
// cppcheck-suppress arrayIndexOutOfBounds
|
||||
Channel *secondary = &Chan[chan + 3];
|
||||
|
||||
if (val & mask) {
|
||||
|
@ -570,9 +575,9 @@ void Opal::Sample(int16_t *left, int16_t *right) {
|
|||
}
|
||||
|
||||
// Mix with the partial accumulation
|
||||
int32_t omblend = SampleRate - SampleAccum;
|
||||
*left = static_cast<uint16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
|
||||
*right = static_cast<uint16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate);
|
||||
const int32_t fract = Util::muldivr(SampleAccum, 65536, SampleRate);
|
||||
*left = static_cast<int16_t>(LastOutput[0] + ((fract * (CurrOutput[0] - LastOutput[0])) / 65536));
|
||||
*right = static_cast<int16_t>(LastOutput[1] + ((fract * (CurrOutput[1] - LastOutput[1])) / 65536));
|
||||
|
||||
SampleAccum += OPL3SampleRate;
|
||||
}
|
||||
|
@ -602,14 +607,14 @@ void Opal::Output(int16_t &left, int16_t &right) {
|
|||
else if (leftmix > 0x7FFF)
|
||||
left = 0x7FFF;
|
||||
else
|
||||
left = static_cast<uint16_t>(leftmix);
|
||||
left = static_cast<int16_t>(leftmix);
|
||||
|
||||
if (rightmix < -0x8000)
|
||||
right = -0x8000;
|
||||
else if (rightmix > 0x7FFF)
|
||||
right = 0x7FFF;
|
||||
else
|
||||
right = static_cast<uint16_t>(rightmix);
|
||||
right = static_cast<int16_t>(rightmix);
|
||||
|
||||
Clock++;
|
||||
|
||||
|
|
|
@ -739,7 +739,7 @@ IMidiPlugin::IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGI
|
|||
for(auto &chn : m_MidiCh)
|
||||
{
|
||||
chn.midiPitchBendPos = EncodePitchBendParam(MIDIEvents::pitchBendCentre); // centre pitch bend on all channels
|
||||
chn.ResetProgram();
|
||||
chn.ResetProgram(sndFile.m_playBehaviour[kPluginDefaultProgramAndBank1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -269,7 +269,7 @@ protected:
|
|||
uint8 lastNote = 0 /* NOTE_NONE */;
|
||||
uint8 noteOnMap[128][MAX_CHANNELS];
|
||||
|
||||
void ResetProgram() { currentProgram = uint16_max; currentBank = uint16_max; }
|
||||
void ResetProgram(bool oldBehaviour) { currentProgram = oldBehaviour ? 0 : uint16_max; currentBank = oldBehaviour ? 0 : uint16_max; }
|
||||
};
|
||||
|
||||
std::array<PlugInstrChannel, 16> m_MidiCh; // MIDI channel state
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue