Updated libOpenMPT to version 0.7.9
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
681cd18145
commit
e03d857f86
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
|
Copyright (c) 1997-2003, Olivier Lapicque
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
|
|
@ -585,32 +585,10 @@ endif
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OPTIMIZE_FASTMATH),2)
|
ifeq ($(MPT_COMPILER_NOIPARA),1)
|
||||||
CPPFLAGS += -DMPT_CHECK_CXX_IGNORE_WARNING_FASTMATH -DMPT_CHECK_CXX_IGNORE_WARNING_FINITEMATH
|
# See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||||
CXXFLAGS += -ffast-math
|
CXXFLAGS += -fno-ipa-ra
|
||||||
CFLAGS += -ffast-math
|
CFLAGS += -fno-ipa-ra
|
||||||
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
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CHECKED),1)
|
ifeq ($(CHECKED),1)
|
||||||
|
@ -707,10 +685,10 @@ endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(LOCAL_MPG123),1)
|
ifeq ($(LOCAL_MPG123),1)
|
||||||
CPPFLAGS_MPG123 := -DMPT_WITH_MPG123
|
CPPFLAGS_MPG123 := -DMPT_WITH_MPG123 -DMPG123_NO_LARGENAME
|
||||||
LDFLAGS_MPG123 :=
|
LDFLAGS_MPG123 :=
|
||||||
LDLIBS_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 :=
|
||||||
LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat.c
|
LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat.c
|
||||||
LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat_str.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/layer1.c
|
||||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer2.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/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/libmpg123.c
|
||||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/ntom.c
|
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/ntom.c
|
||||||
LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/optimize.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/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/%$(FLAVOUR_O).o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC
|
||||||
include/mpg123/src/libmpg123/%.test$(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
|
else
|
||||||
ifeq ($(NO_MPG123),1)
|
ifeq ($(NO_MPG123),1)
|
||||||
else
|
else
|
||||||
|
@ -795,7 +778,10 @@ ifeq ($(LOCAL_VORBIS),1)
|
||||||
CPPFLAGS_VORBIS := -DMPT_WITH_VORBIS
|
CPPFLAGS_VORBIS := -DMPT_WITH_VORBIS
|
||||||
LDFLAGS_VORBIS :=
|
LDFLAGS_VORBIS :=
|
||||||
LDLIBS_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 :=
|
||||||
LOCAL_VORBIS_SOURCES += include/vorbis/lib/analysis.c
|
LOCAL_VORBIS_SOURCES += include/vorbis/lib/analysis.c
|
||||||
LOCAL_VORBIS_SOURCES += include/vorbis/lib/bitrate.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 += --std=c11 --std=c++17
|
||||||
CPPCHECK_FLAGS += --quiet
|
CPPCHECK_FLAGS += --quiet
|
||||||
CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]'
|
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=missingIncludeSystem
|
||||||
CPPCHECK_FLAGS += --suppress=uninitMemberVar
|
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
|
||||||
mkdir -p bin/$(FLAVOUR_DIR)dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/$(FLAVOUR_DIR)all
|
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 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 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
|
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
|
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
|
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
|
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 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
|
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
|
.PHONY: bin/$(FLAVOUR_DIR)dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip
|
||||||
|
@ -2005,10 +1990,10 @@ ifeq ($(SHARED_LIB),1)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
contrib/fuzzing/fuzz$(FLAVOUR_O).o: contrib/fuzzing/fuzz.c
|
contrib/fuzzing/fuzz$(FLAVOUR_O).o: contrib/fuzzing/fuzz.cpp
|
||||||
$(INFO) [CC] $<
|
$(INFO) [CXX] $<
|
||||||
$(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*$(FLAVOUR_O).d
|
$(VERYSILENT)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*$(FLAVOUR_O).d
|
||||||
$(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $<
|
$(SILENT)$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||||
bin/$(FLAVOUR_DIR)fuzz$(EXESUFFIX): contrib/fuzzing/fuzz$(FLAVOUR_O).o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT)
|
bin/$(FLAVOUR_DIR)fuzz$(EXESUFFIX): contrib/fuzzing/fuzz$(FLAVOUR_O).o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT)
|
||||||
$(INFO) [LD] $@
|
$(INFO) [LD] $@
|
||||||
$(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) contrib/fuzzing/fuzz$(FLAVOUR_O).o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@
|
$(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
|
- Visual Studio 2017 XP targeting toolset
|
||||||
|
|
||||||
- OpenMPT requires the compile host system to be Windows 8.1 (or later) amd64,
|
- OpenMPT requires the compile host system to be Windows 8.1 (or later) on
|
||||||
or Windows 11 (or later) ARM64.
|
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
|
- 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
|
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_CPPFLAGS := -std=c++17 -fexceptions -frtti
|
||||||
APP_LDFLAGS :=
|
APP_LDFLAGS :=
|
||||||
APP_STL := c++_shared
|
APP_STL := c++_shared
|
||||||
|
|
||||||
|
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
|
||||||
MPT_SVNVERSION=19406
|
MPT_SVNVERSION=21223
|
||||||
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.2
|
MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9
|
||||||
MPT_SVNDATE=2023-06-18T13:08:13.199805Z
|
MPT_SVNDATE=2024-07-21T12:01:13.335584Z
|
||||||
|
|
|
@ -7,96 +7,83 @@ set -e
|
||||||
cd build 2>&1 > /dev/null || true
|
cd build 2>&1 > /dev/null || true
|
||||||
cd ..
|
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 () {
|
function download () {
|
||||||
set -e
|
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"
|
MPT_GET_FILE="$2"
|
||||||
if [ ! -f "$2" ]; then
|
MPT_GET_SUBDIR="$3"
|
||||||
wget "$1" -O "$2"
|
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
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -108,13 +95,16 @@ if [ ! -d "build/tools" ]; then
|
||||||
mkdir build/tools
|
mkdir build/tools
|
||||||
fi
|
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
|
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)
|
ifeq ($(HOST_FLAVOUR),MACOSX)
|
||||||
|
|
||||||
NO_PULSEAUDIO?=1
|
include build/make/config-macos.mk
|
||||||
include build/make/config-clang.mk
|
|
||||||
# Mac OS X overrides
|
|
||||||
DYNLINK=0
|
|
||||||
SHARED_SONAME=0
|
|
||||||
MPT_COMPILER_NOSECTIONS=1
|
|
||||||
MPT_COMPILER_NOGCSECTIONS=1
|
|
||||||
|
|
||||||
else ifeq ($(HOST_FLAVOUR),MSYS2)
|
else ifeq ($(HOST_FLAVOUR),MSYS2)
|
||||||
|
|
||||||
|
@ -38,11 +32,13 @@ include build/make/config-gcc.mk
|
||||||
else ifeq ($(HOST_FLAVOUR),NETBSD)
|
else ifeq ($(HOST_FLAVOUR),NETBSD)
|
||||||
|
|
||||||
include build/make/config-gcc.mk
|
include build/make/config-gcc.mk
|
||||||
|
MPT_COMPILER_NOALLOCAH=1
|
||||||
NO_PORTAUDIOCPP?=1
|
NO_PORTAUDIOCPP?=1
|
||||||
|
|
||||||
else ifeq ($(HOST_FLAVOUR),FREEBSD)
|
else ifeq ($(HOST_FLAVOUR),FREEBSD)
|
||||||
|
|
||||||
include build/make/config-clang.mk
|
include build/make/config-clang.mk
|
||||||
|
MPT_COMPILER_NOALLOCAH=1
|
||||||
NO_PORTAUDIOCPP?=1
|
NO_PORTAUDIOCPP?=1
|
||||||
|
|
||||||
else ifeq ($(HOST_FLAVOUR),OPENBSD)
|
else ifeq ($(HOST_FLAVOUR),OPENBSD)
|
||||||
|
@ -50,6 +46,7 @@ else ifeq ($(HOST_FLAVOUR),OPENBSD)
|
||||||
NO_PORTAUDIOCPP?=1
|
NO_PORTAUDIOCPP?=1
|
||||||
NO_PULSEAUDIO?=1
|
NO_PULSEAUDIO?=1
|
||||||
include build/make/config-clang.mk
|
include build/make/config-clang.mk
|
||||||
|
MPT_COMPILER_NOALLOCAH=1
|
||||||
|
|
||||||
else ifeq ($(HOST_FLAVOUR),HAIKU)
|
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/cx486slc := $(___) -march=i486 $(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/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/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)
|
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
|
OPTIMIZE_FASTMATH=1
|
||||||
|
|
||||||
|
# See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115049>.
|
||||||
|
MPT_COMPILER_NOIPARA=1
|
||||||
|
|
||||||
include build/make/warnings-gcc.mk
|
include build/make/warnings-gcc.mk
|
||||||
|
|
||||||
DYNLINK=0
|
DYNLINK=0
|
||||||
|
@ -393,6 +396,8 @@ MPT_COMPILER_NOVISIBILITY=1
|
||||||
# causes crashes on process shutdown with liballegro
|
# causes crashes on process shutdown with liballegro
|
||||||
MPT_COMPILER_NOGCSECTIONS=1
|
MPT_COMPILER_NOGCSECTIONS=1
|
||||||
|
|
||||||
|
MPT_COMPILER_NOALLOCAH=1
|
||||||
|
|
||||||
ifeq ($(OPTIMIZE_LTO),1)
|
ifeq ($(OPTIMIZE_LTO),1)
|
||||||
CXXFLAGS += -flto=auto -Wno-attributes
|
CXXFLAGS += -flto=auto -Wno-attributes
|
||||||
CFLAGS += -flto=auto -Wno-attributes
|
CFLAGS += -flto=auto -Wno-attributes
|
||||||
|
|
|
@ -70,6 +70,12 @@ CXXFLAGS += -flto
|
||||||
CFLAGS += -flto
|
CFLAGS += -flto
|
||||||
LDFLAGS += -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)
|
ifeq ($(EMSCRIPTEN_TARGET),default)
|
||||||
# emits whatever is emscripten's default, currently (1.38.8) this is the same as "wasm" below.
|
# emits whatever is emscripten's default, currently (1.38.8) this is the same as "wasm" below.
|
||||||
CPPFLAGS += -DMPT_BUILD_WASM
|
CPPFLAGS += -DMPT_BUILD_WASM
|
||||||
|
@ -130,6 +136,7 @@ endif
|
||||||
CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0
|
CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0
|
||||||
CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -fno-strict-aliasing
|
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'"
|
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
|
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)
|
$(error unknown WINDOWS_ARCH)
|
||||||
endif
|
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)
|
ifeq ($(origin CC),default)
|
||||||
CC = $(MINGW_ARCH)-w64-mingw32-gcc$(MINGW_FLAVOUR)
|
CC = $(MINGW_ARCH)-w64-$(MINGW_CRT)-gcc$(MINGW_FLAVOUR)
|
||||||
endif
|
endif
|
||||||
ifeq ($(origin CXX),default)
|
ifeq ($(origin CXX),default)
|
||||||
CXX = $(MINGW_ARCH)-w64-mingw32-g++$(MINGW_FLAVOUR)
|
CXX = $(MINGW_ARCH)-w64-$(MINGW_CRT)-g++$(MINGW_FLAVOUR)
|
||||||
endif
|
endif
|
||||||
ifeq ($(origin LD),default)
|
ifeq ($(origin LD),default)
|
||||||
LD = $(CXX)
|
LD = $(CXX)
|
||||||
endif
|
endif
|
||||||
ifeq ($(origin AR),default)
|
ifeq ($(origin AR),default)
|
||||||
AR = $(MINGW_ARCH)-w64-mingw32-ar$(MINGW_FLAVOUR)
|
AR = $(MINGW_ARCH)-w64-$(MINGW_CRT)-ar$(MINGW_FLAVOUR)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(STDCXX),)
|
ifneq ($(STDCXX),)
|
||||||
|
@ -107,6 +117,11 @@ else
|
||||||
$(error unknown WINDOWS_VERSION)
|
$(error unknown WINDOWS_VERSION)
|
||||||
endif
|
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)
|
ifeq ($(MINGW_COMPILER),clang)
|
||||||
include build/make/warnings-clang.mk
|
include build/make/warnings-clang.mk
|
||||||
else
|
else
|
||||||
|
|
|
@ -73,6 +73,9 @@ CFLAGS += -march=i586 -m80387 -mtune=pentium
|
||||||
|
|
||||||
PC_LIBS_PRIVATE += -lole32 -lrpcrt4
|
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
|
include build/make/warnings-gcc.mk
|
||||||
|
|
||||||
EXESUFFIX=.exe
|
EXESUFFIX=.exe
|
||||||
|
|
|
@ -73,6 +73,9 @@ CFLAGS += -march=i386 -m80387 -mtune=i486
|
||||||
|
|
||||||
PC_LIBS_PRIVATE += -lole32 -lrpcrt4
|
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
|
include build/make/warnings-gcc.mk
|
||||||
|
|
||||||
EXESUFFIX=.exe
|
EXESUFFIX=.exe
|
||||||
|
|
|
@ -13,7 +13,7 @@ CFLAGS_WARNINGS += -Wframe-larger-than=4000
|
||||||
ifeq ($(MODERN),1)
|
ifeq ($(MODERN),1)
|
||||||
CXXFLAGS_WARNINGS +=
|
CXXFLAGS_WARNINGS +=
|
||||||
CFLAGS_WARNINGS +=
|
CFLAGS_WARNINGS +=
|
||||||
LDFLAGS_WARNINGS += -Wl,-no-undefined -Wl,--detect-odr-violations
|
LDFLAGS_WARNINGS += -Wl,-no-undefined
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS_SILENT += -Wno-\#warnings
|
CFLAGS_SILENT += -Wno-\#warnings
|
||||||
|
@ -24,6 +24,7 @@ CFLAGS_SILENT += -Wno-float-conversion
|
||||||
CFLAGS_SILENT += -Wno-frame-larger-than
|
CFLAGS_SILENT += -Wno-frame-larger-than
|
||||||
CFLAGS_SILENT += -Wno-missing-prototypes
|
CFLAGS_SILENT += -Wno-missing-prototypes
|
||||||
CFLAGS_SILENT += -Wno-sign-compare
|
CFLAGS_SILENT += -Wno-sign-compare
|
||||||
|
CFLAGS_SILENT += -Wno-unused-but-set-variable
|
||||||
CFLAGS_SILENT += -Wno-unused-function
|
CFLAGS_SILENT += -Wno-unused-function
|
||||||
CFLAGS_SILENT += -Wno-unused-parameter
|
CFLAGS_SILENT += -Wno-unused-parameter
|
||||||
CFLAGS_SILENT += -Wno-unused-variable
|
CFLAGS_SILENT += -Wno-unused-variable
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#define OPENMPT_VERSION_SVNVERSION "19406"
|
#define OPENMPT_VERSION_SVNVERSION "21223"
|
||||||
#define OPENMPT_VERSION_REVISION 19406
|
#define OPENMPT_VERSION_REVISION 21223
|
||||||
#define OPENMPT_VERSION_DIRTY 0
|
#define OPENMPT_VERSION_DIRTY 0
|
||||||
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
#define OPENMPT_VERSION_MIXEDREVISIONS 0
|
||||||
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.2"
|
#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.7.9"
|
||||||
#define OPENMPT_VERSION_DATE "2023-06-18T13:08:13.199805Z"
|
#define OPENMPT_VERSION_DATE "2024-07-21T12:01:13.335584Z"
|
||||||
#define OPENMPT_VERSION_IS_PACKAGE 1
|
#define OPENMPT_VERSION_IS_PACKAGE 1
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; };
|
6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; };
|
||||||
82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; };
|
82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; };
|
||||||
8ADD039E1D6ACD90864371DE /* icy2utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = C3752BE686E0ED98C5849226 /* icy2utf8.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 */; };
|
9CA288B296644524D06346F2 /* index.c in Sources */ = {isa = PBXBuildFile; fileRef = 754AC0BABDBBA4EC025A76FA /* index.c */; };
|
||||||
BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; };
|
BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; };
|
||||||
C2065F0E06550480487C3D4E /* readers.c in Sources */ = {isa = PBXBuildFile; fileRef = 489101566A5CAE88A711D796 /* readers.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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
F56A100A0C7CAB3CFC98A64A /* id3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = id3.c; path = ../../../include/mpg123/src/libmpg123/id3.c; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
@ -112,6 +114,7 @@
|
||||||
748089C69CBB42788456D006 /* layer1.c */,
|
748089C69CBB42788456D006 /* layer1.c */,
|
||||||
9796B208BFD16ABAA76CF848 /* layer2.c */,
|
9796B208BFD16ABAA76CF848 /* layer2.c */,
|
||||||
BAACDA4AE2E792FCCA83208A /* layer3.c */,
|
BAACDA4AE2E792FCCA83208A /* layer3.c */,
|
||||||
|
E769BB26AAD57CD8E9792166 /* lfs_wrap.c */,
|
||||||
54E0A14CA243977E1862978C /* libmpg123.c */,
|
54E0A14CA243977E1862978C /* libmpg123.c */,
|
||||||
5B10DD6681370D18E83E03A6 /* ntom.c */,
|
5B10DD6681370D18E83E03A6 /* ntom.c */,
|
||||||
1B61528CDECD143E1D70B8CC /* optimize.c */,
|
1B61528CDECD143E1D70B8CC /* optimize.c */,
|
||||||
|
@ -211,6 +214,7 @@
|
||||||
26299D7E974C3E704D12EBBE /* layer1.c in Sources */,
|
26299D7E974C3E704D12EBBE /* layer1.c in Sources */,
|
||||||
142B46E0854DE7D23B149520 /* layer2.c in Sources */,
|
142B46E0854DE7D23B149520 /* layer2.c in Sources */,
|
||||||
022CF042734F913429163E82 /* layer3.c in Sources */,
|
022CF042734F913429163E82 /* layer3.c in Sources */,
|
||||||
|
8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */,
|
||||||
C39C716430C47FD632576FA4 /* libmpg123.c in Sources */,
|
C39C716430C47FD632576FA4 /* libmpg123.c in Sources */,
|
||||||
EAB0691E1B9461102EAC975E /* ntom.c in Sources */,
|
EAB0691E1B9461102EAC975E /* ntom.c in Sources */,
|
||||||
0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */,
|
0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */,
|
||||||
|
@ -254,9 +258,7 @@
|
||||||
SYMROOT = "../../../bin/debug/xcode4-ios/all";
|
SYMROOT = "../../../bin/debug/xcode4-ios/all";
|
||||||
USER_HEADER_SEARCH_PATHS = (
|
USER_HEADER_SEARCH_PATHS = (
|
||||||
../../../include/mpg123/ports/Xcode,
|
../../../include/mpg123/ports/Xcode,
|
||||||
../../../include/mpg123/src/libmpg123,
|
../../../include/mpg123/src/include,
|
||||||
../../../include/mpg123/src/compat,
|
|
||||||
../../../include/mpg123/src,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
@ -294,9 +296,7 @@
|
||||||
SYMROOT = "../../../bin/release/xcode4-ios/all";
|
SYMROOT = "../../../bin/release/xcode4-ios/all";
|
||||||
USER_HEADER_SEARCH_PATHS = (
|
USER_HEADER_SEARCH_PATHS = (
|
||||||
../../../include/mpg123/ports/Xcode,
|
../../../include/mpg123/ports/Xcode,
|
||||||
../../../include/mpg123/src/libmpg123,
|
../../../include/mpg123/src/include,
|
||||||
../../../include/mpg123/src/compat,
|
|
||||||
../../../include/mpg123/src,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Release;
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */,
|
4D1485535F373B4538387393 /* tests_base_arithmetic_shift.hpp */,
|
||||||
44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */,
|
44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */,
|
||||||
6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */,
|
6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */,
|
||||||
|
DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */,
|
||||||
32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */,
|
32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */,
|
||||||
A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */,
|
A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */,
|
||||||
82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */,
|
82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */,
|
||||||
|
@ -1024,6 +1027,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A235F524F99B6816ABA98364 /* BuildSettings.h */,
|
A235F524F99B6816ABA98364 /* BuildSettings.h */,
|
||||||
|
9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */,
|
||||||
13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */,
|
13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */,
|
||||||
0A20B0FECCE111702A15EF3E /* ComponentManager.h */,
|
0A20B0FECCE111702A15EF3E /* ComponentManager.h */,
|
||||||
EF78F5224ABA48941E149362 /* Dither.h */,
|
EF78F5224ABA48941E149362 /* Dither.h */,
|
||||||
|
@ -1749,8 +1753,7 @@
|
||||||
);
|
);
|
||||||
SYMROOT = "../../bin/release/xcode4-ios/all";
|
SYMROOT = "../../bin/release/xcode4-ios/all";
|
||||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||||
../../include/mpg123/ports/Xcode,
|
../../include/mpg123/src/include,
|
||||||
../../include/mpg123/src/libmpg123,
|
|
||||||
../../include/ogg/include,
|
../../include/ogg/include,
|
||||||
../../include/vorbis/include,
|
../../include/vorbis/include,
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -1795,8 +1798,7 @@
|
||||||
);
|
);
|
||||||
SYMROOT = "../../bin/debug/xcode4-ios/all";
|
SYMROOT = "../../bin/debug/xcode4-ios/all";
|
||||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||||
../../include/mpg123/ports/Xcode,
|
../../include/mpg123/src/include,
|
||||||
../../include/mpg123/src/libmpg123,
|
|
||||||
../../include/ogg/include,
|
../../include/ogg/include,
|
||||||
../../include/vorbis/include,
|
../../include/vorbis/include,
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; };
|
6C03B21AB052578CF279905A /* feature.c in Sources */ = {isa = PBXBuildFile; fileRef = A60D55A2C7D902D4048E2BE2 /* feature.c */; };
|
||||||
82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; };
|
82DB3C387C9CF8AAB69BFA78 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 440E80E08C7F6512D11E3720 /* parse.c */; };
|
||||||
8ADD039E1D6ACD90864371DE /* icy2utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = C3752BE686E0ED98C5849226 /* icy2utf8.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 */; };
|
9CA288B296644524D06346F2 /* index.c in Sources */ = {isa = PBXBuildFile; fileRef = 754AC0BABDBBA4EC025A76FA /* index.c */; };
|
||||||
BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; };
|
BCB343AA29DB521C2B6E41EA /* stringbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 0BDCDC32593FD264CF5ED272 /* stringbuf.c */; };
|
||||||
C2065F0E06550480487C3D4E /* readers.c in Sources */ = {isa = PBXBuildFile; fileRef = 489101566A5CAE88A711D796 /* readers.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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
F56A100A0C7CAB3CFC98A64A /* id3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = id3.c; path = ../../../include/mpg123/src/libmpg123/id3.c; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
@ -112,6 +114,7 @@
|
||||||
748089C69CBB42788456D006 /* layer1.c */,
|
748089C69CBB42788456D006 /* layer1.c */,
|
||||||
9796B208BFD16ABAA76CF848 /* layer2.c */,
|
9796B208BFD16ABAA76CF848 /* layer2.c */,
|
||||||
BAACDA4AE2E792FCCA83208A /* layer3.c */,
|
BAACDA4AE2E792FCCA83208A /* layer3.c */,
|
||||||
|
E769BB26AAD57CD8E9792166 /* lfs_wrap.c */,
|
||||||
54E0A14CA243977E1862978C /* libmpg123.c */,
|
54E0A14CA243977E1862978C /* libmpg123.c */,
|
||||||
5B10DD6681370D18E83E03A6 /* ntom.c */,
|
5B10DD6681370D18E83E03A6 /* ntom.c */,
|
||||||
1B61528CDECD143E1D70B8CC /* optimize.c */,
|
1B61528CDECD143E1D70B8CC /* optimize.c */,
|
||||||
|
@ -211,6 +214,7 @@
|
||||||
26299D7E974C3E704D12EBBE /* layer1.c in Sources */,
|
26299D7E974C3E704D12EBBE /* layer1.c in Sources */,
|
||||||
142B46E0854DE7D23B149520 /* layer2.c in Sources */,
|
142B46E0854DE7D23B149520 /* layer2.c in Sources */,
|
||||||
022CF042734F913429163E82 /* layer3.c in Sources */,
|
022CF042734F913429163E82 /* layer3.c in Sources */,
|
||||||
|
8EEA26DE2177F0D08A50951E /* lfs_wrap.c in Sources */,
|
||||||
C39C716430C47FD632576FA4 /* libmpg123.c in Sources */,
|
C39C716430C47FD632576FA4 /* libmpg123.c in Sources */,
|
||||||
EAB0691E1B9461102EAC975E /* ntom.c in Sources */,
|
EAB0691E1B9461102EAC975E /* ntom.c in Sources */,
|
||||||
0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */,
|
0DDEA7A4A06C7196094515E4 /* optimize.c in Sources */,
|
||||||
|
@ -254,9 +258,7 @@
|
||||||
SYMROOT = "../../../bin/debug/xcode4-macosx/all";
|
SYMROOT = "../../../bin/debug/xcode4-macosx/all";
|
||||||
USER_HEADER_SEARCH_PATHS = (
|
USER_HEADER_SEARCH_PATHS = (
|
||||||
../../../include/mpg123/ports/Xcode,
|
../../../include/mpg123/ports/Xcode,
|
||||||
../../../include/mpg123/src/libmpg123,
|
../../../include/mpg123/src/include,
|
||||||
../../../include/mpg123/src/compat,
|
|
||||||
../../../include/mpg123/src,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
@ -294,9 +296,7 @@
|
||||||
SYMROOT = "../../../bin/release/xcode4-macosx/all";
|
SYMROOT = "../../../bin/release/xcode4-macosx/all";
|
||||||
USER_HEADER_SEARCH_PATHS = (
|
USER_HEADER_SEARCH_PATHS = (
|
||||||
../../../include/mpg123/ports/Xcode,
|
../../../include/mpg123/ports/Xcode,
|
||||||
../../../include/mpg123/src/libmpg123,
|
../../../include/mpg123/src/include,
|
||||||
../../../include/mpg123/src/compat,
|
|
||||||
../../../include/mpg123/src,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Release;
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */,
|
4D1485535F373B4538387393 /* tests_base_arithmetic_shift.hpp */,
|
||||||
44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */,
|
44E6E1C323897D35D86B8003 /* tests_base_bit.hpp */,
|
||||||
6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */,
|
6C67B7D97E1EF7CBF38CE619 /* tests_base_math.hpp */,
|
||||||
|
DA432DAB72BB1B1D9EA20BEB /* tests_base_numeric.hpp */,
|
||||||
32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */,
|
32BAAF6B89D057DDB618EDAB /* tests_base_saturate_cast.hpp */,
|
||||||
A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */,
|
A9BD0A251CDE97177DA3D865 /* tests_base_saturate_round.hpp */,
|
||||||
82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */,
|
82EA4C5D44A2DDCFEBBDAA9D /* tests_base_wrapping_divide.hpp */,
|
||||||
|
@ -1024,6 +1027,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A235F524F99B6816ABA98364 /* BuildSettings.h */,
|
A235F524F99B6816ABA98364 /* BuildSettings.h */,
|
||||||
|
9F2E1C1AF92F330CC0762A5A /* BuildSettingsCompiler.h */,
|
||||||
13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */,
|
13F9789407F8C2068CE3D6D4 /* ComponentManager.cpp */,
|
||||||
0A20B0FECCE111702A15EF3E /* ComponentManager.h */,
|
0A20B0FECCE111702A15EF3E /* ComponentManager.h */,
|
||||||
EF78F5224ABA48941E149362 /* Dither.h */,
|
EF78F5224ABA48941E149362 /* Dither.h */,
|
||||||
|
@ -1749,8 +1753,7 @@
|
||||||
);
|
);
|
||||||
SYMROOT = "../../bin/release/xcode4-macosx/all";
|
SYMROOT = "../../bin/release/xcode4-macosx/all";
|
||||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||||
../../include/mpg123/ports/Xcode,
|
../../include/mpg123/src/include,
|
||||||
../../include/mpg123/src/libmpg123,
|
|
||||||
../../include/ogg/include,
|
../../include/ogg/include,
|
||||||
../../include/vorbis/include,
|
../../include/vorbis/include,
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -1795,8 +1798,7 @@
|
||||||
);
|
);
|
||||||
SYMROOT = "../../bin/debug/xcode4-macosx/all";
|
SYMROOT = "../../bin/debug/xcode4-macosx/all";
|
||||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||||
../../include/mpg123/ports/Xcode,
|
../../include/mpg123/src/include,
|
||||||
../../include/mpg123/src/libmpg123,
|
|
||||||
../../include/ogg/include,
|
../../include/ogg/include,
|
||||||
../../include/vorbis/include,
|
../../include/vorbis/include,
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include "BuildSettingsCompiler.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "mpt/base/detect_compiler.hpp"
|
#include "mpt/base/detect_compiler.hpp"
|
||||||
#include "mpt/base/detect_os.hpp"
|
#include "mpt/base/detect_os.hpp"
|
||||||
#include "mpt/base/detect_quirks.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
|
// third-party library configuration
|
||||||
|
|
||||||
#ifdef MPT_WITH_STBVORBIS
|
#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;
|
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
|
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
|
||||||
|
|
||||||
} // namespace mpt
|
} // namespace mpt
|
||||||
|
|
|
@ -128,6 +128,10 @@ using default_prng = mpt::good_prng;
|
||||||
mpt::random_device & global_random_device();
|
mpt::random_device & global_random_device();
|
||||||
mpt::thread_safe_prng<mpt::default_prng> & global_prng();
|
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)
|
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT)
|
||||||
void set_global_random_device(mpt::random_device *rd);
|
void set_global_random_device(mpt::random_device *rd);
|
||||||
void set_global_prng(mpt::thread_safe_prng<mpt::default_prng> *rng);
|
void set_global_prng(mpt::thread_safe_prng<mpt::default_prng> *rng);
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
#include "mptStringBuffer.h"
|
#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>
|
#include <chrono>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
#include "openmpt/all/BuildSettings.hpp"
|
#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 <chrono>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#endif
|
#endif
|
||||||
|
@ -175,7 +175,7 @@ Local UnixAsLocal(Unix tp);
|
||||||
|
|
||||||
} // namespace nochrono
|
} // 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;
|
using Unix = std::chrono::system_clock::time_point;
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "mpt/base/span.hpp"
|
|
||||||
#include "mpt/check/compiler.hpp"
|
#include "mpt/check/compiler.hpp"
|
||||||
#include "mpt/check/libc.hpp"
|
#include "mpt/check/libc.hpp"
|
||||||
#if defined(MPT_WITH_MFC)
|
#if defined(MPT_WITH_MFC)
|
||||||
|
@ -73,6 +72,8 @@
|
||||||
#if MPT_OS_WINDOWS
|
#if MPT_OS_WINDOWS
|
||||||
#include "mpt/check/windows.hpp"
|
#include "mpt/check/windows.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "mpt/base/span.hpp"
|
||||||
#include "mpt/exception/exception.hpp"
|
#include "mpt/exception/exception.hpp"
|
||||||
#include "mpt/exception/exception_text.hpp"
|
#include "mpt/exception/exception_text.hpp"
|
||||||
#include "mpt/out_of_memory/out_of_memory.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"
|
"libopenmpt (based on OpenMPT / Open ModPlug Tracker)\n"
|
||||||
#endif
|
#endif
|
||||||
"\n"
|
"\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"
|
"Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Developers:\n"
|
"Developers:\n"
|
||||||
"Johannes Schultz (2008-2023)\n"
|
"Johannes Schultz (2008-2024)\n"
|
||||||
"J\xC3\xB6rn Heusipp (2012-2023)\n"
|
"J\xC3\xB6rn Heusipp (2012-2024)\n"
|
||||||
"Ahti Lepp\xC3\xA4nen (2005-2011)\n"
|
"Ahti Lepp\xC3\xA4nen (2005-2011)\n"
|
||||||
"Robin Fernandes (2004-2007)\n"
|
"Robin Fernandes (2004-2007)\n"
|
||||||
"Sergiy Pylypenko (2007)\n"
|
"Sergiy Pylypenko (2007)\n"
|
||||||
|
@ -776,7 +776,7 @@ mpt::ustring GetFullCreditsString()
|
||||||
"\n"
|
"\n"
|
||||||
#endif
|
#endif
|
||||||
"Daniel Collin (emoon/TBL) for providing test infrastructure\n"
|
"Daniel Collin (emoon/TBL) for providing test infrastructure\n"
|
||||||
"https://twitter.com/daniel_collin\n"
|
"https://mastodon.gamedev.place/@daniel_collin\n"
|
||||||
"\n"
|
"\n"
|
||||||
"The people in the ModPlug community for crucial contribution\n"
|
"The people in the ModPlug community for crucial contribution\n"
|
||||||
"in the form of ideas, testing and support;\n"
|
"in the form of ideas, testing and support;\n"
|
||||||
|
@ -800,7 +800,7 @@ mpt::ustring GetFullCreditsString()
|
||||||
mpt::ustring GetLicenseString()
|
mpt::ustring GetLicenseString()
|
||||||
{
|
{
|
||||||
return MPT_UTF8(
|
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"
|
"Copyright (c) 1997-2003, Olivier Lapicque" "\n"
|
||||||
"All rights reserved." "\n"
|
"All rights reserved." "\n"
|
||||||
"" "\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);
|
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.
|
// Version definitions. The only thing that needs to be changed when changing version number.
|
||||||
#define VER_MAJORMAJOR 1
|
#define VER_MAJORMAJOR 1
|
||||||
#define VER_MAJOR 31
|
#define VER_MAJOR 31
|
||||||
#define VER_MINOR 03
|
#define VER_MINOR 09
|
||||||
#define VER_MINORMINOR 00
|
#define VER_MINORMINOR 00
|
||||||
|
|
||||||
OPENMPT_NAMESPACE_END
|
OPENMPT_NAMESPACE_END
|
||||||
|
|
|
@ -8,7 +8,7 @@ amf="AMF\x0A"
|
||||||
amf="DMF\x0E"
|
amf="DMF\x0E"
|
||||||
|
|
||||||
ams="Extreme"
|
ams="Extreme"
|
||||||
ams="AMShdr\x1A\x02\x02"
|
ams="AMShdr\x1A\x00\x02\x02"
|
||||||
|
|
||||||
#dbm="DBM0"
|
#dbm="DBM0"
|
||||||
dbm="NAME"
|
dbm="NAME"
|
||||||
|
@ -54,7 +54,7 @@ far="\x0D\x0A\x1A"
|
||||||
fmt="FMTracker\x01\x01"
|
fmt="FMTracker\x01\x01"
|
||||||
|
|
||||||
gdm="GDM\xFE"
|
gdm="GDM\xFE"
|
||||||
gdm="GMFS"
|
gdm="\x0D\x0A\x1AGMFS\x01\x00"
|
||||||
|
|
||||||
gtk="GTK\x04"
|
gtk="GTK\x04"
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ psm16="PSAH"
|
||||||
psm16="PPAT"
|
psm16="PPAT"
|
||||||
|
|
||||||
ptm="PTMF"
|
ptm="PTMF"
|
||||||
ptm="\x1A\x03\x02"
|
ptm="\x1A\x03\x02\x00"
|
||||||
|
|
||||||
s3m="SCRM"
|
s3m="SCRM"
|
||||||
#s3m="SCRS"
|
#s3m="SCRS"
|
||||||
|
|
|
@ -10,4 +10,4 @@ mkdir $FUZZING_TEMPDIR/bin
|
||||||
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
|
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
|
||||||
|
|
||||||
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
#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
|
. ./fuzz-settings.sh
|
||||||
|
|
||||||
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
#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
|
. ./fuzz-settings.sh
|
||||||
|
|
||||||
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
|
#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
|
tar -xzvf $AFL_FILENAME
|
||||||
rm $AFL_FILENAME
|
rm $AFL_FILENAME
|
||||||
cd AFLplusplus-*
|
cd AFLplusplus-*
|
||||||
make source-only || exit
|
make PERFORMANCE=1 source-only || exit
|
||||||
cd ..
|
cd ..
|
||||||
rm -rf afl
|
rm -rf afl
|
||||||
mv AFLplusplus-* 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
|
Copyright (c) 1997-2003, Olivier Lapicque
|
||||||
All rights reserved.
|
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
|
Copyright (c) 1997-2003, Olivier Lapicque
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ problems can happen at:
|
||||||
reports or bug fixes and feature development discussion
|
reports or bug fixes and feature development discussion
|
||||||
* [Forum](https://forum.openmpt.org/), preferred for long-term discussion of
|
* [Forum](https://forum.openmpt.org/), preferred for long-term discussion of
|
||||||
new features or specific questions about development
|
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
|
preferred for shorter questions
|
||||||
* [GitHub pull requests](https://github.com/OpenMPT/openmpt/pulls), please
|
* [GitHub pull requests](https://github.com/OpenMPT/openmpt/pulls), please
|
||||||
only use for rather tiny fixes, see below
|
only use for rather tiny fixes, see below
|
||||||
|
|
|
@ -51,12 +51,17 @@
|
||||||
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
|
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
|
||||||
#endif
|
#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 push
|
||||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||||
#endif
|
#endif
|
||||||
#include <portaudio.h>
|
#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
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,17 @@
|
||||||
|
|
||||||
#include <libopenmpt/libopenmpt.h>
|
#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 push
|
||||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||||
#endif
|
#endif
|
||||||
#include <portaudio.h>
|
#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
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,17 @@
|
||||||
#include <libopenmpt/libopenmpt.h>
|
#include <libopenmpt/libopenmpt.h>
|
||||||
#include <libopenmpt/libopenmpt_stream_callbacks_file.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 push
|
||||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||||
#endif
|
#endif
|
||||||
#include <portaudio.h>
|
#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
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#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.
|
- 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.
|
- 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.
|
- "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.
|
- "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.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.
|
- 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 ) {
|
static void about( HWND hwndParent ) {
|
||||||
std::ostringstream about;
|
std::ostringstream about;
|
||||||
about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
|
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 << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl;
|
||||||
about << std::endl;
|
about << std::endl;
|
||||||
about << openmpt::string::get( "contact" ) << 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.
|
* - 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.
|
* - 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.
|
* - "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.
|
* - "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.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.
|
* - 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.
|
- 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.
|
- 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.
|
- "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.
|
- "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.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.
|
- 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];
|
subsong = &subsongs[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
base_seconds += subsong->duration;
|
base_seconds += subsongs[i].duration;
|
||||||
}
|
}
|
||||||
seconds -= base_seconds;
|
seconds -= base_seconds;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
/*! \brief libopenmpt minor version number */
|
/*! \brief libopenmpt minor version number */
|
||||||
#define OPENMPT_API_VERSION_MINOR 7
|
#define OPENMPT_API_VERSION_MINOR 7
|
||||||
/*! \brief libopenmpt patch version number */
|
/*! \brief libopenmpt patch version number */
|
||||||
#define OPENMPT_API_VERSION_PATCH 2
|
#define OPENMPT_API_VERSION_PATCH 9
|
||||||
/*! \brief libopenmpt pre-release tag */
|
/*! \brief libopenmpt pre-release tag */
|
||||||
#define OPENMPT_API_VERSION_PREREL ""
|
#define OPENMPT_API_VERSION_PREREL ""
|
||||||
/*! \brief libopenmpt pre-release flag */
|
/*! \brief libopenmpt pre-release flag */
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
LIBOPENMPT_VERSION_MAJOR=0
|
LIBOPENMPT_VERSION_MAJOR=0
|
||||||
LIBOPENMPT_VERSION_MINOR=7
|
LIBOPENMPT_VERSION_MINOR=7
|
||||||
LIBOPENMPT_VERSION_PATCH=2
|
LIBOPENMPT_VERSION_PATCH=9
|
||||||
LIBOPENMPT_VERSION_PREREL=
|
LIBOPENMPT_VERSION_PREREL=
|
||||||
|
|
||||||
LIBOPENMPT_LTVER_CURRENT=4
|
LIBOPENMPT_LTVER_CURRENT=4
|
||||||
LIBOPENMPT_LTVER_REVISION=2
|
LIBOPENMPT_LTVER_REVISION=9
|
||||||
LIBOPENMPT_LTVER_AGE=4
|
LIBOPENMPT_LTVER_AGE=4
|
||||||
|
|
|
@ -192,7 +192,7 @@ BEGIN
|
||||||
VALUE "FileDescription", VER_FILEDESC_STR
|
VALUE "FileDescription", VER_FILEDESC_STR
|
||||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||||
VALUE "InternalName", VER_FILENAME_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 "OriginalFilename", VER_FILENAME_STR
|
||||||
VALUE "ProductName", "libopenmpt"
|
VALUE "ProductName", "libopenmpt"
|
||||||
VALUE "ProductVersion", VER_FILEVERSION_STR
|
VALUE "ProductVersion", VER_FILEVERSION_STR
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
|
|
||||||
|
@ -244,6 +245,9 @@ static std::string StringUpperCase( std::string str ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string seconds_to_string( double time ) {
|
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 time_ms = static_cast<std::int64_t>( time * 1000 );
|
||||||
std::int64_t seconds = ( time_ms / 1000 ) % 60;
|
std::int64_t seconds = ( time_ms / 1000 ) % 60;
|
||||||
std::int64_t minutes = ( time_ms / ( 1000 * 60 ) ) % 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 ) {
|
static void WINAPI openmpt_About( HWND win ) {
|
||||||
std::ostringstream about;
|
std::ostringstream about;
|
||||||
about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
|
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 << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl;
|
||||||
about << std::endl;
|
about << std::endl;
|
||||||
about << openmpt::string::get( "contact" ) << std::endl;
|
about << openmpt::string::get( "contact" ) << std::endl;
|
||||||
|
@ -551,10 +555,10 @@ std::streambuf::int_type xmplay_streambuf::underflow() {
|
||||||
start += put_back_count;
|
start += put_back_count;
|
||||||
}
|
}
|
||||||
std::size_t n = xmpffile->Read( file, start, buffer.size() - ( start - base ) );
|
std::size_t n = xmpffile->Read( file, start, buffer.size() - ( start - base ) );
|
||||||
|
setg( base, start, start + n );
|
||||||
if ( n == 0 ) {
|
if ( n == 0 ) {
|
||||||
return traits_type::eof();
|
return traits_type::eof();
|
||||||
}
|
}
|
||||||
setg( base, start, start + n );
|
|
||||||
return traits_type::to_int_type( *gptr() );
|
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() ) {
|
if ( subsong >= 0 && static_cast<size_t>( subsong ) < subsong_names.size() ) {
|
||||||
first_subsong += subsong;
|
first_subsong += subsong;
|
||||||
last_subsong = first_subsong + 1;
|
last_subsong = first_subsong + 1;
|
||||||
} else
|
|
||||||
{
|
|
||||||
last_subsong = first_subsong + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( auto subsong_name = first_subsong; subsong_name != last_subsong; subsong_name++ ) {
|
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, "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, "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, "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 ) ) );
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float * build_xmplay_length( const openmpt::module & /* mod */ ) {
|
static std::vector<double> build_subsong_lengths( openmpt::module & mod ) {
|
||||||
float * result = static_cast<float*>( xmpfmisc->Alloc( sizeof( float ) * self->subsong_lengths.size() ) );
|
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 ) {
|
if ( !result ) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
for ( std::size_t i = 0; i < self->subsong_lengths.size(); ++i ) {
|
for ( std::size_t i = 0; i < subsong_lengths.size(); ++i ) {
|
||||||
result[i] = static_cast<float>( self->subsong_lengths[i] );
|
result[i] = static_cast<float>( subsong_lengths[i] );
|
||||||
}
|
}
|
||||||
return result;
|
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 ) {
|
static void clear_xmplay_string( char * str ) {
|
||||||
if ( !str ) {
|
if ( !str ) {
|
||||||
return;
|
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 DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, float * * length, char * * tags ) {
|
||||||
static_cast<void>( filename );
|
static_cast<void>( filename );
|
||||||
|
DWORD subsongs = 0;
|
||||||
try {
|
try {
|
||||||
std::map< std::string, std::string > ctls
|
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:
|
case XMPFILE_TYPE_MEMORY:
|
||||||
{
|
{
|
||||||
openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls );
|
openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls );
|
||||||
if ( length ) {
|
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||||
*length = build_xmplay_length( mod );
|
|
||||||
}
|
|
||||||
if ( tags ) {
|
|
||||||
*tags = build_xmplay_tags( mod );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XMPFILE_TYPE_FILE:
|
case XMPFILE_TYPE_FILE:
|
||||||
|
@ -878,50 +896,30 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl
|
||||||
{
|
{
|
||||||
xmplay_istream s( file );
|
xmplay_istream s( file );
|
||||||
openmpt::module mod( s, std::clog, ctls );
|
openmpt::module mod( s, std::clog, ctls );
|
||||||
if ( length ) {
|
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||||
*length = build_xmplay_length( mod );
|
|
||||||
}
|
|
||||||
if ( tags ) {
|
|
||||||
*tags = build_xmplay_tags( mod );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if ( xmpffile->GetType( file ) == XMPFILE_TYPE_MEMORY ) {
|
if ( xmpffile->GetType( file ) == XMPFILE_TYPE_MEMORY ) {
|
||||||
openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls );
|
openmpt::module mod( xmpffile->GetMemory( file ), xmpffile->GetSize( file ), std::clog, ctls );
|
||||||
if ( length ) {
|
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||||
*length = build_xmplay_length( mod );
|
|
||||||
}
|
|
||||||
if ( tags ) {
|
|
||||||
*tags = build_xmplay_tags( mod );
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
openmpt::module mod( read_XMPFILE_vector( file ), std::clog, ctls );
|
openmpt::module mod( read_XMPFILE_vector( file ), std::clog, ctls );
|
||||||
if ( length ) {
|
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||||
*length = build_xmplay_length( mod );
|
|
||||||
}
|
|
||||||
if ( tags ) {
|
|
||||||
*tags = build_xmplay_tags( mod );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
std::ifstream s( filename, std::ios_base::binary );
|
std::ifstream s( filename, std::ios_base::binary );
|
||||||
openmpt::module mod( s, std::clog, ctls );
|
openmpt::module mod( s, std::clog, ctls );
|
||||||
if ( length ) {
|
subsongs = build_xmplay_file_info( mod, length, tags );
|
||||||
*length = build_xmplay_length( mod );
|
#endif
|
||||||
}
|
|
||||||
if ( tags ) {
|
|
||||||
*tags = build_xmplay_tags( mod );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
if ( length ) *length = nullptr;
|
if ( length ) *length = nullptr;
|
||||||
if ( tags ) *tags = nullptr;
|
if ( tags ) *tags = nullptr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return self->subsong_lengths.size() + XMPIN_INFO_NOSUBTAGS;
|
return subsongs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// open a file for playback
|
// open a file for playback
|
||||||
|
@ -967,12 +965,7 @@ static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) {
|
||||||
reset_timeinfos();
|
reset_timeinfos();
|
||||||
apply_options();
|
apply_options();
|
||||||
|
|
||||||
std::int32_t num_subsongs = self->mod->get_num_subsongs();
|
self->subsong_lengths = build_subsong_lengths( *self->mod );
|
||||||
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_names = self->mod->get_subsong_names();
|
self->subsong_names = self->mod->get_subsong_names();
|
||||||
self->mod->select_subsong( 0 );
|
self->mod->select_subsong( 0 );
|
||||||
self->tempo_factor = 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";
|
str << title << " Names:" << "\r";
|
||||||
for ( std::size_t i = 0; i < names.size(); i++ ) {
|
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";
|
str << "\r";
|
||||||
}
|
}
|
||||||
|
@ -1261,11 +1254,7 @@ static void WINAPI openmpt_GetSamples( char * buf ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD WINAPI openmpt_GetSubSongs( float * length ) {
|
static DWORD WINAPI openmpt_GetSubSongs( float * length ) {
|
||||||
double tmp = 0.0;
|
*length = static_cast<float>( std::accumulate( self->subsong_lengths.cbegin(), self->subsong_lengths.cend(), 0.0 ) );
|
||||||
for ( auto sub_length : self->subsong_lengths ) {
|
|
||||||
tmp += sub_length;
|
|
||||||
}
|
|
||||||
*length = static_cast<float>( tmp );
|
|
||||||
return static_cast<DWORD>( self->subsong_lengths.size() );
|
return static_cast<DWORD>( self->subsong_lengths.size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char * const license =
|
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"
|
"Copyright (c) 1997-2003, Olivier Lapicque" "\n"
|
||||||
"All rights reserved." "\n"
|
"All rights reserved." "\n"
|
||||||
"" "\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("Banner: ") << flags.banner << lf;
|
||||||
s << MPT_USTRING("Verbose: ") << flags.verbose << lf;
|
s << MPT_USTRING("Verbose: ") << flags.verbose << lf;
|
||||||
s << MPT_USTRING("Mode : ") << mode_to_string( flags.mode ) << 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 progress: ") << flags.show_progress << lf;
|
||||||
s << MPT_USTRING("Show peak meters: ") << flags.show_meters << lf;
|
s << MPT_USTRING("Show peak meters: ") << flags.show_meters << lf;
|
||||||
s << MPT_USTRING("Show channel peak meters: ") << flags.show_channel_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;
|
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("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 ) {
|
if ( banner == verbosity_normal ) {
|
||||||
log << lf;
|
log << lf;
|
||||||
return;
|
return;
|
||||||
|
@ -461,7 +462,7 @@ static void show_banner( concat_stream<mpt::ustring> & log, verbosity banner ) {
|
||||||
static void show_man_version( textout & log ) {
|
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 << MPT_USTRING("openmpt123") << MPT_USTRING(" v") << mpt::transcode<mpt::ustring>( mpt::source_encoding, OPENMPT123_VERSION_STRING ) << lf;
|
||||||
log << 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 ) {
|
static void show_short_version( textout & log ) {
|
||||||
|
@ -970,7 +971,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
||||||
meter_type meter;
|
meter_type meter;
|
||||||
|
|
||||||
const bool multiline = flags.show_ui;
|
const bool multiline = flags.show_ui;
|
||||||
|
|
||||||
|
const bool narrow = (flags.terminal_width < 72) && (flags.terminal_height > 25);
|
||||||
|
|
||||||
int lines = 0;
|
int lines = 0;
|
||||||
|
|
||||||
int pattern_lines = 0;
|
int pattern_lines = 0;
|
||||||
|
@ -980,10 +983,17 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
||||||
// cppcheck-suppress identicalInnerCondition
|
// cppcheck-suppress identicalInnerCondition
|
||||||
if ( flags.show_ui ) {
|
if ( flags.show_ui ) {
|
||||||
lines += 1;
|
lines += 1;
|
||||||
|
if ( narrow ) {
|
||||||
|
lines += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( flags.show_meters ) {
|
if ( flags.show_meters ) {
|
||||||
for ( int channel = 0; channel < flags.channels; ++channel ) {
|
if ( narrow ) {
|
||||||
lines += 1;
|
lines += 1;
|
||||||
|
} else {
|
||||||
|
for ( int channel = 0; channel < flags.channels; ++channel ) {
|
||||||
|
lines += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( flags.show_channel_meters ) {
|
if ( flags.show_channel_meters ) {
|
||||||
|
@ -993,6 +1003,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
||||||
lines += 1;
|
lines += 1;
|
||||||
if ( flags.show_progress ) {
|
if ( flags.show_progress ) {
|
||||||
lines += 1;
|
lines += 1;
|
||||||
|
if ( narrow ) {
|
||||||
|
lines += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( flags.show_progress ) {
|
if ( flags.show_progress ) {
|
||||||
|
@ -1120,7 +1133,13 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
||||||
log.cursor_up( lines );
|
log.cursor_up( lines );
|
||||||
log << lf;
|
log << lf;
|
||||||
if ( flags.show_meters ) {
|
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 ) {
|
if ( flags.show_channel_meters ) {
|
||||||
int width = ( flags.terminal_width - 3 ) / mod.get_num_channels();
|
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 ) {
|
if ( flags.show_ui ) {
|
||||||
log << MPT_USTRING("Settings...: ");
|
if ( narrow ) {
|
||||||
log << MPT_USTRING("Gain: ") << static_cast<float>( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" ");
|
log << MPT_USTRING("Settings...: ");
|
||||||
log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" ");
|
log << MPT_USTRING("Gain: ") << static_cast<float>( flags.gain ) * 0.01f << MPT_USTRING(" dB") << MPT_USTRING(" ");
|
||||||
log << MPT_USTRING("Filter: ") << flags.filtertaps << MPT_USTRING(" taps") << MPT_USTRING(" ");
|
log << MPT_USTRING("Stereo: ") << flags.separation << MPT_USTRING(" %") << MPT_USTRING(" ");
|
||||||
log << MPT_USTRING("Ramping: ") << flags.ramping << MPT_USTRING(" ");
|
log << lf;
|
||||||
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 ) {
|
if ( flags.show_details ) {
|
||||||
log << MPT_USTRING("Mixer......: ");
|
log << MPT_USTRING("Mixer......: ");
|
||||||
|
@ -1198,18 +1228,37 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto
|
||||||
log << MPT_USTRING(" ");
|
log << MPT_USTRING(" ");
|
||||||
log << lf;
|
log << lf;
|
||||||
if ( flags.show_progress ) {
|
if ( flags.show_progress ) {
|
||||||
log << MPT_USTRING("Player.....: ");
|
if ( narrow ) {
|
||||||
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("Player.....: ");
|
||||||
log << MPT_USTRING(" ");
|
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("Pat:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_pattern() );
|
log << MPT_USTRING(" ");
|
||||||
log << MPT_USTRING(" ");
|
log << lf;
|
||||||
log << MPT_USTRING("Row:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_row() );
|
log << MPT_USTRING("Pattern....: ");
|
||||||
log << MPT_USTRING(" ");
|
log << MPT_USTRING("Pat:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_pattern() );
|
||||||
log << MPT_USTRING("Spd:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 2, mod.get_current_speed() );
|
log << MPT_USTRING(" ");
|
||||||
log << MPT_USTRING(" ");
|
log << MPT_USTRING("Row:") << align_right<mpt::ustring>( MPT_UCHAR(':'), 3, mod.get_current_row() );
|
||||||
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 << MPT_USTRING(" ");
|
log << lf;
|
||||||
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 ) {
|
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;
|
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!
|
// 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;
|
SmpLength lookaheadOffset = 3 * InterpolationLookaheadBufferSize + chn.pModSample->nLength - chn.nLoopEnd;
|
||||||
if(inSustainLoop)
|
if(inSustainLoop)
|
||||||
|
|
|
@ -11,13 +11,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "openmpt/all/BuildSettings.hpp"
|
#include "openmpt/all/BuildSettings.hpp"
|
||||||
|
#include "openmpt/base/Endian.hpp"
|
||||||
#include "../soundlib/ModInstrument.h"
|
#include "Snd_defs.h"
|
||||||
#include "../soundlib/ModSample.h"
|
|
||||||
#include "../soundlib/SampleIO.h"
|
|
||||||
|
|
||||||
OPENMPT_NAMESPACE_BEGIN
|
OPENMPT_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct InstrumentEnvelope;
|
||||||
|
struct ModInstrument;
|
||||||
|
struct ModSample;
|
||||||
|
class CSoundFile;
|
||||||
|
class SampleIO;
|
||||||
|
|
||||||
struct ITFileHeader
|
struct ITFileHeader
|
||||||
{
|
{
|
||||||
// Header Flags
|
// Header Flags
|
||||||
|
|
|
@ -401,6 +401,7 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeGlobals(MOD_TYPE_AMS);
|
InitializeGlobals(MOD_TYPE_AMS);
|
||||||
|
InitializeChannels();
|
||||||
|
|
||||||
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
|
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
|
||||||
m_nChannels = (fileHeader.channelConfig & 0x1F) + 1;
|
m_nChannels = (fileHeader.channelConfig & 0x1F) + 1;
|
||||||
|
@ -435,7 +436,6 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
// Read channel names
|
// Read channel names
|
||||||
for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
|
for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
|
||||||
{
|
{
|
||||||
ChnSettings[chn].Reset();
|
|
||||||
file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
|
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 numSamples = file.ReadUint8();
|
||||||
uint8 sampleAssignment[120];
|
std::array<uint8, 120> sampleAssignment;
|
||||||
MemsetZero(sampleAssignment); // Only really needed for v2.0, where the lowest and highest octave aren't cleared.
|
sampleAssignment.fill(0); // Only really needed for v2.0, where the lowest and highest octave aren't cleared.
|
||||||
|
|
||||||
if(numSamples == 0
|
if(numSamples == 0
|
||||||
|| (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment)) // v2.01+: 120 Notes
|
|| (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.ReadRaw(mpt::as_span(sampleAssignment).subspan(12, 96)).size())) // v2.0: 96 Notes
|
||||||
{
|
{
|
||||||
continue;
|
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,
|
// 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.
|
// as it is possible that shadow samples use different sample settings like base frequency or panning.
|
||||||
const SAMPLEINDEX firstSmp = GetNumSamples() + 1;
|
const SAMPLEINDEX firstSmp = GetNumSamples() + 1;
|
||||||
|
std::string sampleName;
|
||||||
for(SAMPLEINDEX smp = 0; smp < numSamples; smp++)
|
for(SAMPLEINDEX smp = 0; smp < numSamples; smp++)
|
||||||
{
|
{
|
||||||
if(firstSmp + smp >= MAX_SAMPLES)
|
file.ReadSizedString<uint8le, mpt::String::spacePadded>(sampleName);
|
||||||
{
|
|
||||||
file.Skip(sizeof(AMS2SampleHeader));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[firstSmp + smp]);
|
|
||||||
|
|
||||||
AMS2SampleHeader sampleHeader;
|
AMS2SampleHeader sampleHeader;
|
||||||
file.ReadStruct(sampleHeader);
|
file.ReadStruct(sampleHeader);
|
||||||
|
if(firstSmp + smp >= MAX_SAMPLES)
|
||||||
|
continue;
|
||||||
|
|
||||||
sampleHeader.ConvertToMPT(Samples[firstSmp + smp]);
|
sampleHeader.ConvertToMPT(Samples[firstSmp + smp]);
|
||||||
|
m_szNames[firstSmp + smp] = sampleName;
|
||||||
|
|
||||||
uint16 settings = (instrHeader.shadowInstr & instrIndexMask)
|
uint16 settings = (instrHeader.shadowInstr & instrIndexMask)
|
||||||
| ((smp << sampleIndexShift) & sampleIndexMask)
|
| ((smp << sampleIndexShift) & sampleIndexMask)
|
||||||
|
|
|
@ -38,17 +38,17 @@ struct DBMChunk
|
||||||
// 32-Bit chunk identifiers
|
// 32-Bit chunk identifiers
|
||||||
enum ChunkIdentifiers
|
enum ChunkIdentifiers
|
||||||
{
|
{
|
||||||
idNAME = MagicBE("NAME"),
|
idNAME = MagicBE("NAME"),
|
||||||
idINFO = MagicBE("INFO"),
|
idINFO = MagicBE("INFO"),
|
||||||
idSONG = MagicBE("SONG"),
|
idSONG = MagicBE("SONG"),
|
||||||
idINST = MagicBE("INST"),
|
idINST = MagicBE("INST"),
|
||||||
idVENV = MagicBE("VENV"),
|
idVENV = MagicBE("VENV"),
|
||||||
idPENV = MagicBE("PENV"),
|
idPENV = MagicBE("PENV"),
|
||||||
idPATT = MagicBE("PATT"),
|
idPATT = MagicBE("PATT"),
|
||||||
idPNAM = MagicBE("PNAM"),
|
idPNAM = MagicBE("PNAM"),
|
||||||
idSMPL = MagicBE("SMPL"),
|
idSMPL = MagicBE("SMPL"),
|
||||||
idDSPE = MagicBE("DSPE"),
|
idDSPE = MagicBE("DSPE"),
|
||||||
idMPEG = MagicBE("MPEG"),
|
idMPEG = MagicBE("MPEG"),
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32be id;
|
uint32be id;
|
||||||
|
@ -85,18 +85,43 @@ struct DBMInstrument
|
||||||
{
|
{
|
||||||
enum DBMInstrFlags
|
enum DBMInstrFlags
|
||||||
{
|
{
|
||||||
smpLoop = 0x01,
|
smpLoop = 0x01,
|
||||||
smpPingPongLoop = 0x02,
|
smpPingPongLoop = 0x02,
|
||||||
};
|
};
|
||||||
|
|
||||||
char name[30];
|
char name[30];
|
||||||
uint16be sample; // Sample reference
|
uint16be sample; // Sample reference
|
||||||
uint16be volume; // 0...64
|
uint16be volume; // 0...64
|
||||||
uint32be sampleRate;
|
uint32be sampleRate;
|
||||||
uint32be loopStart;
|
uint32be loopStart;
|
||||||
uint32be loopLength;
|
uint32be loopLength;
|
||||||
int16be panning; // -128...128
|
int16be panning; // -128...128
|
||||||
uint16be flags; // See DBMInstrFlags
|
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)
|
MPT_BINARY_STRUCT(DBMInstrument, 50)
|
||||||
|
@ -107,19 +132,55 @@ struct DBMEnvelope
|
||||||
{
|
{
|
||||||
enum DBMEnvelopeFlags
|
enum DBMEnvelopeFlags
|
||||||
{
|
{
|
||||||
envEnabled = 0x01,
|
envEnabled = 0x01,
|
||||||
envSustain = 0x02,
|
envSustain1 = 0x02,
|
||||||
envLoop = 0x04,
|
envLoop = 0x04,
|
||||||
|
envSustain2 = 0x08,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16be instrument;
|
uint16be instrument;
|
||||||
uint8be flags; // See DBMEnvelopeFlags
|
uint8be flags; // See DBMEnvelopeFlags
|
||||||
uint8be numSegments; // Number of envelope points - 1
|
uint8be numSegments; // Number of envelope points - 1
|
||||||
uint8be sustain1;
|
uint8be sustain1;
|
||||||
uint8be loopBegin;
|
uint8be loopBegin;
|
||||||
uint8be loopEnd;
|
uint8be loopEnd;
|
||||||
uint8be sustain2; // Second sustain point
|
uint8be sustain2; // Second sustain point
|
||||||
uint16be data[2 * 32];
|
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)
|
MPT_BINARY_STRUCT(DBMEnvelope, 136)
|
||||||
|
@ -137,11 +198,11 @@ static constexpr EffectCommand dbmEffects[] =
|
||||||
CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE,
|
CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE,
|
||||||
CMD_NONE, CMD_NONE, CMD_NONE,
|
CMD_NONE, CMD_NONE, CMD_NONE,
|
||||||
#ifndef NO_PLUGINS
|
#ifndef NO_PLUGINS
|
||||||
CMD_DBMECHO, // Toggle DSP
|
CMD_DBMECHO, // Toggle DSP
|
||||||
CMD_MIDI, // Wxx Echo Delay
|
CMD_MIDI, // Wxx Echo Delay
|
||||||
CMD_MIDI, // Xxx Echo Feedback
|
CMD_MIDI, // Xxx Echo Feedback
|
||||||
CMD_MIDI, // Yxx Echo Mix
|
CMD_MIDI, // Yxx Echo Mix
|
||||||
CMD_MIDI, // Zxx Echo Cross
|
CMD_MIDI, // Zxx Echo Cross
|
||||||
#endif // NO_PLUGINS
|
#endif // NO_PLUGINS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -197,15 +258,15 @@ static std::pair<EffectCommand, uint8> ConvertDBMEffect(const uint8 cmd, uint8 p
|
||||||
case CMD_MODCMDEX:
|
case CMD_MODCMDEX:
|
||||||
switch(param & 0xF0)
|
switch(param & 0xF0)
|
||||||
{
|
{
|
||||||
case 0x30: // Play backwards
|
case 0x30: // Play backwards
|
||||||
command = CMD_S3MCMDEX;
|
command = CMD_S3MCMDEX;
|
||||||
param = 0x9F;
|
param = 0x9F;
|
||||||
break;
|
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;
|
command = CMD_S3MCMDEX;
|
||||||
param = 0xC0;
|
param = 0xC0;
|
||||||
break;
|
break;
|
||||||
case 0x50: // Turn on/off channel
|
case 0x50: // Turn on/off channel
|
||||||
// TODO: Apparently this should also kill the playing note.
|
// TODO: Apparently this should also kill the playing note.
|
||||||
if((param & 0x0F) <= 0x01)
|
if((param & 0x0F) <= 0x01)
|
||||||
{
|
{
|
||||||
|
@ -213,7 +274,7 @@ static std::pair<EffectCommand, uint8> ConvertDBMEffect(const uint8 cmd, uint8 p
|
||||||
param = (param == 0x50) ? 0x00 : 0x40;
|
param = (param == 0x50) ? 0x00 : 0x40;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x70: // Coarse offset
|
case 0x70: // Coarse offset
|
||||||
command = CMD_S3MCMDEX;
|
command = CMD_S3MCMDEX;
|
||||||
param = 0xA0 | (param & 0x0F);
|
param = 0xA0 | (param & 0x0F);
|
||||||
break;
|
break;
|
||||||
|
@ -258,35 +319,7 @@ static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundF
|
||||||
uint16 dbmIns = dbmEnv.instrument;
|
uint16 dbmIns = dbmEnv.instrument;
|
||||||
if(dbmIns > 0 && dbmIns <= sndFile.GetNumInstruments() && (sndFile.Instruments[dbmIns] != nullptr))
|
if(dbmIns > 0 && dbmIns <= sndFile.GetNumInstruments() && (sndFile.Instruments[dbmIns] != nullptr))
|
||||||
{
|
{
|
||||||
ModInstrument *mptIns = sndFile.Instruments[dbmIns];
|
dbmEnv.ConvertToMPT(sndFile.Instruments[dbmIns]->GetEnvelope(envType), scaleEnv);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,6 +389,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_playBehaviour.set(kSlidesAtSpeed1);
|
m_playBehaviour.set(kSlidesAtSpeed1);
|
||||||
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
||||||
m_playBehaviour.reset(kITArpeggio);
|
m_playBehaviour.reset(kITArpeggio);
|
||||||
|
m_playBehaviour.reset(kITInstrWithNoteOff);
|
||||||
|
m_playBehaviour.reset(kITInstrWithNoteOffOldEffects);
|
||||||
|
|
||||||
m_modFormat.formatName = U_("DigiBooster Pro");
|
m_modFormat.formatName = U_("DigiBooster Pro");
|
||||||
m_modFormat.type = U_("dbm");
|
m_modFormat.type = U_("dbm");
|
||||||
|
@ -407,41 +442,41 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
#endif // MPT_DBM_USE_REAL_SUBSONGS
|
#endif // MPT_DBM_USE_REAL_SUBSONGS
|
||||||
|
|
||||||
// Read instruments
|
// Read instruments
|
||||||
|
std::map<SAMPLEINDEX, SAMPLEINDEX> copySample;
|
||||||
if(FileReader instChunk = chunks.GetChunk(DBMChunk::idINST))
|
if(FileReader instChunk = chunks.GetChunk(DBMChunk::idINST))
|
||||||
{
|
{
|
||||||
|
std::set<SAMPLEINDEX> sampleUsed;
|
||||||
for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
|
for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
|
||||||
{
|
{
|
||||||
DBMInstrument instrHeader;
|
DBMInstrument instrHeader;
|
||||||
instChunk.ReadStruct(instrHeader);
|
instChunk.ReadStruct(instrHeader);
|
||||||
|
|
||||||
ModInstrument *mptIns = AllocateInstrument(i, instrHeader.sample);
|
SAMPLEINDEX mappedSample = instrHeader.sample;
|
||||||
if(mptIns == nullptr || instrHeader.sample >= MAX_SAMPLES)
|
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;
|
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
|
// Sample Info
|
||||||
ModSample &mptSmp = Samples[instrHeader.sample];
|
instrHeader.ConvertToMPT(Samples[mappedSample]);
|
||||||
mptSmp.Initialize();
|
m_szNames[mappedSample] = mptIns->name;
|
||||||
mptSmp.nVolume = std::min(static_cast<uint16>(instrHeader.volume), uint16(64)) * 4u;
|
sampleUsed.insert(mappedSample);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read envelopes
|
// Read envelopes
|
||||||
|
@ -466,7 +501,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(patternChunk.IsValid() && (loadFlags & loadPatternData))
|
if(patternChunk.IsValid() && (loadFlags & loadPatternData))
|
||||||
{
|
{
|
||||||
FileReader patternNameChunk = chunks.GetChunk(DBMChunk::idPNAM);
|
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);
|
Patterns.ResizeArray(infoData.patterns);
|
||||||
std::vector<std::pair<EffectCommand, ModCommand::PARAM>> lostGlobalCommands;
|
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(cmd1, cmd2);
|
||||||
std::swap(param1, param2);
|
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);
|
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++)
|
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 sampleFlags = sampleChunk.ReadUint32BE();
|
||||||
uint32 sampleLength = 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)
|
// Counters for channel packing (including global track)
|
||||||
std::vector<uint8> channelCounter(numChannels + 1, 0);
|
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
|
// Global track info counter reached 0 => read global track data
|
||||||
if(channelCounter[0] == 0)
|
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).
|
// 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.
|
// 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)
|
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)});
|
chunks.chunks.push_back(ChunkReader::Item<DMFChunk>{chunkHeader, file.ReadChunk(chunkLength)});
|
||||||
file.Skip(chunkSkip);
|
file.Skip(chunkSkip);
|
||||||
}
|
}
|
||||||
|
@ -970,6 +970,8 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
const uint8 headerSize = fileHeader.version < 3 ? 9 : 8;
|
const uint8 headerSize = fileHeader.version < 3 ? 9 : 8;
|
||||||
chunk.Skip(headerSize - sizeof(uint32le));
|
chunk.Skip(headerSize - sizeof(uint32le));
|
||||||
const uint32 patLength = chunk.ReadUint32LE();
|
const uint32 patLength = chunk.ReadUint32LE();
|
||||||
|
if(!chunk.CanRead(patLength))
|
||||||
|
return false;
|
||||||
chunk.SkipBack(headerSize);
|
chunk.SkipBack(headerSize);
|
||||||
patternChunk = chunk.ReadChunk(headerSize + patLength);
|
patternChunk = chunk.ReadChunk(headerSize + patLength);
|
||||||
}
|
}
|
||||||
|
|
|
@ -401,7 +401,7 @@ bool CSoundFile::ReadDSm(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
InitializeGlobals(MOD_TYPE_MOD);
|
InitializeGlobals(MOD_TYPE_MOD);
|
||||||
m_SongFlags.set(SONG_IMPORTED);
|
m_SongFlags = SONG_IMPORTED;
|
||||||
m_nChannels = fileHeader.numChannels;
|
m_nChannels = fileHeader.numChannels;
|
||||||
static_assert(MAX_BASECHANNELS >= 32 && MAX_SAMPLES > 255);
|
static_assert(MAX_BASECHANNELS >= 32 && MAX_SAMPLES > 255);
|
||||||
m_nSamples = fileHeader.numSamples;
|
m_nSamples = fileHeader.numSamples;
|
||||||
|
@ -511,7 +511,7 @@ bool CSoundFile::ReadDSm(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
if(loadFlags & loadSampleData)
|
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(Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
|
||||||
SampleIO::mono,
|
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
|
// 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
|
// 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;
|
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)
|
if(formatVersion == DTM_206_PATTERN_FORMAT && transpose > 0 && transpose != 48)
|
||||||
{
|
{
|
||||||
// Digital Home Studio applies this unconditionally, but some old songs sound wrong then (delirium.dtm).
|
// Digital 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);
|
InitializeGlobals(MOD_TYPE_DTM);
|
||||||
InitializeChannels();
|
InitializeChannels();
|
||||||
m_SongFlags.set(SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS);
|
m_SongFlags.set(SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS);
|
||||||
|
m_playBehaviour.reset(kPeriodsAreHertz);
|
||||||
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
||||||
// Various files have a default speed or tempo of 0
|
// Various files have a default speed or tempo of 0
|
||||||
if(fileHeader.tempo)
|
if(fileHeader.tempo)
|
||||||
|
@ -436,26 +442,18 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
{
|
{
|
||||||
m->note = note + NOTE_MIN + 12;
|
m->note = note + NOTE_MIN + 12;
|
||||||
if(position.rem)
|
if(position.rem)
|
||||||
{
|
m->SetEffectCommand(CMD_MODCMDEX, static_cast<ModCommand::PARAM>(0xD0 | std::min(position.rem, 15)));
|
||||||
m->command = CMD_MODCMDEX;
|
|
||||||
m->param = 0xD0 | static_cast<ModCommand::PARAM>(std::min(position.rem, 15));
|
|
||||||
}
|
|
||||||
} else if(note & 0x80)
|
} else if(note & 0x80)
|
||||||
{
|
{
|
||||||
// Lower 7 bits contain note, probably intended for MIDI-like note-on/note-off events
|
// Lower 7 bits contain note, probably intended for MIDI-like note-on/note-off events
|
||||||
if(position.rem)
|
if(position.rem)
|
||||||
{
|
m->SetEffectCommand(CMD_MODCMDEX, static_cast<ModCommand::PARAM>(0xC0 |std::min(position.rem, 15)));
|
||||||
m->command = CMD_MODCMDEX;
|
else
|
||||||
m->param = 0xC0 | static_cast<ModCommand::PARAM>(std::min(position.rem, 15));
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
m->note = NOTE_NOTECUT;
|
m->note = NOTE_NOTECUT;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(volume)
|
if(volume)
|
||||||
{
|
{
|
||||||
m->volcmd = VOLCMD_VOLUME;
|
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.
|
||||||
m->vol = std::min(volume, uint8(64)); // Volume can go up to 255, but we do not support over-amplification at the moment.
|
|
||||||
}
|
}
|
||||||
if(instr)
|
if(instr)
|
||||||
{
|
{
|
||||||
|
@ -578,6 +576,9 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(patternFormat == DTM_206_PATTERN_FORMAT)
|
if(patternFormat == DTM_206_PATTERN_FORMAT)
|
||||||
{
|
{
|
||||||
tracker = U_("Digital Home Studio");
|
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))
|
} else if(FileReader chunk = chunks.GetChunk(DTMChunk::idVERS))
|
||||||
{
|
{
|
||||||
uint32 version = chunk.ReadUint32BE();
|
uint32 version = chunk.ReadUint32BE();
|
||||||
|
|
|
@ -172,7 +172,7 @@ struct IMFSample
|
||||||
uint8le unused3[5];
|
uint8le unused3[5];
|
||||||
uint16le ems; // Reserved for internal usage
|
uint16le ems; // Reserved for internal usage
|
||||||
uint32le dram; // 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.
|
// Convert an IMFSample to OpenMPT's internal sample representation.
|
||||||
void ConvertToMPT(ModSample &mptSmp) const
|
void ConvertToMPT(ModSample &mptSmp) const
|
||||||
|
@ -596,8 +596,9 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.ReadStruct(sampleHeader);
|
file.ReadStruct(sampleHeader);
|
||||||
|
|
||||||
const SAMPLEINDEX smpID = firstSample + smp;
|
const SAMPLEINDEX smpID = firstSample + smp;
|
||||||
if(memcmp(sampleHeader.is10, "IS10", 4) || smpID >= MAX_SAMPLES)
|
if(smpID >= MAX_SAMPLES)
|
||||||
{
|
{
|
||||||
|
file.Skip(sampleHeader.length);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,7 +208,7 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, mpt::Charset
|
||||||
CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str);
|
CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str);
|
||||||
if(localTuning)
|
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));
|
CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning));
|
||||||
if(pT)
|
if(pT)
|
||||||
{
|
{
|
||||||
|
@ -687,12 +687,6 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_MidiCfg.Sanitize();
|
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;
|
bool hasModPlugExtensions = false;
|
||||||
|
|
||||||
// Read pattern names: "PNAM"
|
// Read pattern names: "PNAM"
|
||||||
|
@ -1224,9 +1218,10 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
{
|
{
|
||||||
madeWithTracker = U_("ChibiTracker");
|
madeWithTracker = U_("ChibiTracker");
|
||||||
m_playBehaviour.reset(kITShortSampleRetrig);
|
m_playBehaviour.reset(kITShortSampleRetrig);
|
||||||
|
m_nSamplePreAmp /= 2;
|
||||||
} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && fileHeader.special <= 1 && fileHeader.pwd == 0 && fileHeader.reserved == 0
|
} 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
|
&& (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");
|
madeWithTracker = U_("CheeseTracker");
|
||||||
} else if(fileHeader.cwtv == 0 && madeWithTracker.empty())
|
} 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
|
// 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)
|
if(schismDateVersion < SchismVersionFromDate<2023, 03, 9>::date)
|
||||||
m_playBehaviour.reset(kITInitialNoteMemory);
|
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;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
madeWithTracker = MPT_UFORMAT("pyIT {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF));
|
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);
|
madeWithTracker = MPT_UFORMAT("ITMCK {}.{}.{}")((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0xD:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1313,6 +1333,17 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(anyADPCM)
|
if(anyADPCM)
|
||||||
madeWithTracker += U_(" (ADPCM packed)");
|
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)
|
if(GetType() == MOD_TYPE_MPT)
|
||||||
{
|
{
|
||||||
// START - mpt specific:
|
// START - mpt specific:
|
||||||
|
|
|
@ -168,7 +168,7 @@ static constexpr EffectCommand MDLEffTrans[] =
|
||||||
/* Either column */
|
/* Either column */
|
||||||
/* 7 */ CMD_TEMPO,
|
/* 7 */ CMD_TEMPO,
|
||||||
/* 8 */ CMD_PANNING8,
|
/* 8 */ CMD_PANNING8,
|
||||||
/* 9 */ CMD_SETENVPOSITION,
|
/* 9 */ CMD_S3MCMDEX,
|
||||||
/* A */ CMD_NONE,
|
/* A */ CMD_NONE,
|
||||||
/* B */ CMD_POSITIONJUMP,
|
/* B */ CMD_POSITIONJUMP,
|
||||||
/* C */ CMD_GLOBALVOLUME,
|
/* C */ CMD_GLOBALVOLUME,
|
||||||
|
@ -203,6 +203,16 @@ static std::pair<EffectCommand, uint8> ConvertMDLCommand(const uint8 command, ui
|
||||||
case 0x08: // Panning
|
case 0x08: // Panning
|
||||||
param = (param & 0x7F) * 2u;
|
param = (param & 0x7F) * 2u;
|
||||||
break;
|
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
|
case 0x0C: // Global volume
|
||||||
param = (param + 1) / 2u;
|
param = (param + 1) / 2u;
|
||||||
break;
|
break;
|
||||||
|
@ -468,6 +478,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_SongFlags = SONG_ITCOMPATGXX;
|
m_SongFlags = SONG_ITCOMPATGXX;
|
||||||
m_playBehaviour.set(kPerChannelGlobalVolSlide);
|
m_playBehaviour.set(kPerChannelGlobalVolSlide);
|
||||||
m_playBehaviour.set(kApplyOffsetWithoutNote);
|
m_playBehaviour.set(kApplyOffsetWithoutNote);
|
||||||
|
m_playBehaviour.reset(kPeriodsAreHertz);
|
||||||
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
m_playBehaviour.reset(kITVibratoTremoloPanbrello);
|
||||||
m_playBehaviour.reset(kITSCxStopsSample); // Gate effect in underbeat.mdl
|
m_playBehaviour.reset(kITSCxStopsSample); // Gate effect in underbeat.mdl
|
||||||
|
|
||||||
|
|
|
@ -84,10 +84,10 @@ struct MMD2Song
|
||||||
uint32be flags3;
|
uint32be flags3;
|
||||||
uint16be volAdjust; // Volume adjust (%)
|
uint16be volAdjust; // Volume adjust (%)
|
||||||
uint16be mixChannels; // Mixing channels, 0 means 4
|
uint16be mixChannels; // Mixing channels, 0 means 4
|
||||||
uint8be mixEchoType; // 0 = nothing, 1 = normal, 2 = cross
|
uint8 mixEchoType; // 0 = nothing, 1 = normal, 2 = cross
|
||||||
uint8be mixEchoDepth; // 1 - 6, 0 = default
|
uint8 mixEchoDepth; // 1 - 6, 0 = default
|
||||||
uint16be mixEchoLength; // Echo length in milliseconds
|
uint16be mixEchoLength; // Echo length in milliseconds
|
||||||
int8be mixStereoSep; // Stereo separation
|
int8 mixStereoSep; // Stereo separation
|
||||||
char pad0[223];
|
char pad0[223];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ struct MMDSong
|
||||||
{
|
{
|
||||||
FLAG_FILTERON = 0x01, // The hardware audio filter is on
|
FLAG_FILTERON = 0x01, // The hardware audio filter is on
|
||||||
FLAG_JUMPINGON = 0x02, // Mouse pointer jumping 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_INSTRSATT = 0x08, // sng+samples indicator (not useful in MMDs)
|
||||||
FLAG_VOLHEX = 0x10, // volumes are HEX
|
FLAG_VOLHEX = 0x10, // volumes are HEX
|
||||||
FLAG_STSLIDE = 0x20, // use ST/NT/PT compatible sliding
|
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)
|
switch(command)
|
||||||
{
|
{
|
||||||
case 0x04: // Vibrato (twice as deep as in ProTracker)
|
case 0x04: // Vibrato (twice as deep as in ProTracker)
|
||||||
m.command = CMD_VIBRATO;
|
m.SetEffectCommand(CMD_VIBRATO, (std::min<uint8>(param >> 3, 0x0F) << 4) | std::min<uint8>((param & 0x0F) * 2, 0x0F));
|
||||||
m.param = (std::min<uint8>(m.param >> 3, 0x0F) << 4) | std::min<uint8>((m.param & 0x0F) * 2, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x08: // Hold and decay
|
case 0x08: // Hold and decay
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
case 0x09: // Set secondary speed
|
case 0x09: // Set secondary speed
|
||||||
if(m.param > 0 && m.param <= 20)
|
if(param > 0 && param <= 20)
|
||||||
m.command = CMD_SPEED;
|
m.SetEffectCommand(CMD_SPEED, param);
|
||||||
else
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
case 0x0C: // Set Volume
|
case 0x0C: // Set Volume (note: parameters >= 0x80 (only in hex mode?) should set the default instrument volume, which we don't support)
|
||||||
m.command = CMD_VOLUME;
|
if(!ctx.volHex && param < 0x99)
|
||||||
if(!volHex && m.param < 0x99)
|
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>((param >> 4) * 10 + (param & 0x0F)));
|
||||||
m.param = (m.param >> 4) * 10 + (m.param & 0x0F);
|
else if(ctx.volHex && ctx.version < 3)
|
||||||
else if(volHex)
|
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>(std::min(param & 0x7F, 64)));
|
||||||
m.param = ((m.param & 0x7F) + 1) / 2;
|
else if(ctx.volHex)
|
||||||
else
|
m.SetEffectCommand(CMD_VOLUME, static_cast<ModCommand::PARAM>(((param & 0x7F) + 1) / 2));
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
case 0x0D:
|
case 0x0D:
|
||||||
m.command = CMD_VOLUMESLIDE;
|
m.SetEffectCommand(CMD_VOLUMESLIDE, param);
|
||||||
break;
|
break;
|
||||||
case 0x0E: // Synth jump
|
case 0x0E: // Synth jump
|
||||||
m.command = CMD_NONE;
|
m.command = CMD_NONE;
|
||||||
break;
|
break;
|
||||||
case 0x0F: // Misc
|
case 0x0F: // Misc
|
||||||
if(m.param == 0)
|
if(param == 0)
|
||||||
{
|
{
|
||||||
m.command = CMD_PATTERNBREAK;
|
m.SetEffectCommand(CMD_PATTERNBREAK, param);
|
||||||
} else if(m.param <= 0xF0)
|
} else if(param <= 0xF0)
|
||||||
{
|
{
|
||||||
m.command = CMD_TEMPO;
|
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.
|
// This appears to be a bug in OctaMED which is not emulated in MED Soundstudio on Windows.
|
||||||
m.param = 0x70;
|
m.param = 0x70;
|
||||||
} else
|
} 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))
|
if(tempo <= Util::MaxValueOfType(m.param))
|
||||||
{
|
{
|
||||||
m.param = static_cast<ModCommand::PARAM>(tempo);
|
m.param = static_cast<ModCommand::PARAM>(tempo);
|
||||||
|
@ -452,156 +460,183 @@ static std::pair<EffectCommand, ModCommand::PARAM> ConvertMEDEffect(ModCommand &
|
||||||
if(m.param < 0x20)
|
if(m.param < 0x20)
|
||||||
m.param = 0x20;
|
m.param = 0x20;
|
||||||
#endif // MODPLUG_TRACKER
|
#endif // MODPLUG_TRACKER
|
||||||
} else switch(command)
|
} else switch(param)
|
||||||
{
|
{
|
||||||
case 0xF1: // Play note twice
|
case 0xF1: // Play note twice
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0x93);
|
||||||
m.param = 0x93;
|
|
||||||
break;
|
break;
|
||||||
case 0xF2: // Delay note
|
case 0xF2: // Delay note
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xD3);
|
||||||
m.param = 0xD3;
|
|
||||||
break;
|
break;
|
||||||
case 0xF3: // Play note three times
|
case 0xF3: // Play note three times
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0x92);
|
||||||
m.param = 0x92;
|
|
||||||
break;
|
break;
|
||||||
case 0xF8: // Turn filter off
|
case 0xF8: // Turn filter off
|
||||||
case 0xF9: // Turn filter on
|
case 0xF9: // Turn filter on
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xF9 - param);
|
||||||
m.param = 0xF9 - m.param;
|
|
||||||
break;
|
break;
|
||||||
case 0xFA: // MIDI pedal on
|
case 0xFA: // MIDI pedal on
|
||||||
case 0xFB: // MIDI pedal off
|
case 0xFB: // MIDI pedal off
|
||||||
case 0xFD: // Set pitch
|
case 0xFD: // Set pitch
|
||||||
case 0xFE: // End of song
|
case 0xFE: // End of song
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
case 0xFF: // Turn note off
|
case 0xFF: // Turn note off
|
||||||
m.note = NOTE_NOTECUT;
|
m.note = NOTE_NOTECUT;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x10: // MIDI message
|
case 0x10: // MIDI message
|
||||||
m.command = CMD_MIDI;
|
m.SetEffectCommand(CMD_MIDI, 0x80 | param);
|
||||||
m.param |= 0x80;
|
|
||||||
break;
|
break;
|
||||||
case 0x11: // Slide pitch up
|
case 0x11: // Slide pitch up
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0x10 | nibbleLo);
|
||||||
m.param = 0x10 | std::min<uint8>(m.param, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x12: // Slide pitch down
|
case 0x12: // Slide pitch down
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0x20 | nibbleLo);
|
||||||
m.param = 0x20 | std::min<uint8>(m.param, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x14: // Vibrato (ProTracker compatible depth, but faster)
|
case 0x14: // Vibrato (ProTracker compatible depth, but faster)
|
||||||
m.command = CMD_VIBRATO;
|
m.SetEffectCommand(CMD_VIBRATO, (std::min<uint8>((param >> 4) + 1, 0x0F) << 4) | (param & 0x0F));
|
||||||
m.param = (std::min<uint8>((m.param >> 4) + 1, 0x0F) << 4) | (m.param & 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x15: // Set finetune
|
case 0x15: // Set finetune
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0x50 | (param & 0x0F));
|
||||||
m.param = 0x50 | (m.param & 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x16: // Loop
|
case 0x16: // Loop
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0x60 | nibbleLo);
|
||||||
m.param = 0x60 | std::min<uint8>(m.param, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x18: // Stop note
|
case 0x18: // Stop note
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xC0 | nibbleLo);
|
||||||
m.param = 0xC0 | std::min<uint8>(m.param, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x19: // Sample Offset
|
case 0x19: // Sample Offset
|
||||||
m.command = CMD_OFFSET;
|
m.SetEffectCommand(CMD_OFFSET, param);
|
||||||
break;
|
break;
|
||||||
case 0x1A: // Slide volume up once
|
case 0x1A: // Slide volume up once
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xA0 | nibbleLo);
|
||||||
m.param = 0xA0 | std::min<uint8>(m.param, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x1B: // Slide volume down once
|
case 0x1B: // Slide volume down once
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xB0 | nibbleLo);
|
||||||
m.param = 0xB0 | std::min<uint8>(m.param, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x1C: // MIDI program
|
case 0x1C: // MIDI program
|
||||||
if(m.param > 0 && m.param <= 128)
|
if(param > 0 && param <= 128)
|
||||||
{
|
m.SetEffectCommand(CMD_MIDI, param - 1);
|
||||||
m.command = CMD_MIDI;
|
|
||||||
m.param--;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 0x1D: // Pattern break (in hex)
|
case 0x1D: // Pattern break (in hex)
|
||||||
m.command = CMD_PATTERNBREAK;
|
m.SetEffectCommand(CMD_PATTERNBREAK, param);
|
||||||
break;
|
break;
|
||||||
case 0x1E: // Repeat row
|
case 0x1E: // Repeat row
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xE0 | std::min<uint8>(param, 0x0F));
|
||||||
m.param = 0xE0 | std::min<uint8>(m.param, 0x0F);
|
|
||||||
break;
|
break;
|
||||||
case 0x1F: // Note delay and retrigger
|
case 0x1F: // Note delay and retrigger
|
||||||
{
|
{
|
||||||
if(m.param & 0xF0)
|
if(param & 0xF0)
|
||||||
{
|
m.SetEffectCommand(CMD_MODCMDEX, 0xD0 | (param >> 4));
|
||||||
m.command = CMD_MODCMDEX;
|
else if(param & 0x0F)
|
||||||
m.param = 0xD0 | (m.param >> 4);
|
m.SetEffectCommand(CMD_MODCMDEX, 0x90 | param);
|
||||||
} else if(m.param & 0x0F)
|
|
||||||
{
|
|
||||||
m.command = CMD_MODCMDEX;
|
|
||||||
m.param = 0x90 | m.param;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x20: // Reverse sample + skip samples
|
case 0x20: // Reverse sample + skip samples
|
||||||
if(m.param == 0 && m.vol == 0)
|
if(param == 0 && param2 == 0)
|
||||||
{
|
{
|
||||||
if(m.IsNote())
|
if(m.IsNote())
|
||||||
{
|
{
|
||||||
m.command = CMD_S3MCMDEX;
|
m.SetEffectCommand(CMD_XFINEPORTAUPDOWN, 0x9F);
|
||||||
m.param = 0x9F;
|
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// Skip given number of samples
|
// Skip given number of samples
|
||||||
m.command = CMD_NONE;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x29: // Relative sample offset
|
case 0x29: // Relative sample offset
|
||||||
if(m.vol > 0)
|
if(param2 > 0)
|
||||||
{
|
m.SetEffectCommand(CMD_OFFSETPERCENTAGE, mpt::saturate_cast<ModCommand::PARAM>(Util::muldiv_unsigned(param, 0x100, param2)));
|
||||||
m.command = CMD_OFFSETPERCENTAGE;
|
|
||||||
m.param = mpt::saturate_cast<ModCommand::PARAM>(Util::muldiv_unsigned(m.param, 0x100, m.vol));
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 0x2E: // Set panning
|
case 0x2E: // Set panning
|
||||||
if(m.param <= 0x10 || m.param >= 0xF0)
|
if(param <= 0x10 || param >= 0xF0)
|
||||||
{
|
m.SetEffectCommand(CMD_PANNING8, mpt::saturate_cast<ModCommand::PARAM>(((param ^ 0x80) - 0x70) * 8));
|
||||||
m.command = CMD_PANNING8;
|
|
||||||
m.param = mpt::saturate_cast<ModCommand::PARAM>(((m.param ^ 0x80) - 0x70) * 8);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if(command < 0x10)
|
if((command > 0 || param) && command < 0x10)
|
||||||
CSoundFile::ConvertModCommand(m, command, m.param);
|
CSoundFile::ConvertModCommand(m, command, param);
|
||||||
else
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return std::make_pair(CMD_NONE, ModCommand::PARAM(0));
|
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
|
#ifdef MPT_WITH_VST
|
||||||
static std::wstring ReadMEDStringUTF16BE(FileReader &file)
|
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
|
// - starkelsesirap.mmd0 (synth instruments) on the other hand don't need it
|
||||||
// In MMD2 / MMD3, the mix flag is used instead.
|
// In MMD2 / MMD3, the mix flag is used instead.
|
||||||
const bool hardwareMixSamples = (version < 2) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX));
|
const bool hardwareMixSamples = (version < 2) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX));
|
||||||
|
m_nMinPeriod = hardwareMixSamples ? (113 * 4) : (55 * 4);
|
||||||
|
|
||||||
bool needInstruments = false;
|
bool needInstruments = false;
|
||||||
bool anySynthInstrs = false;
|
bool anySynthInstrs = false;
|
||||||
#ifdef MPT_WITH_VST
|
#ifndef NO_PLUGINS
|
||||||
PLUGINDEX numPlugins = 0;
|
PLUGINDEX numPlugins = 0;
|
||||||
#endif // MPT_WITH_VST
|
#endif // !NO_PLUGINS
|
||||||
for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++)
|
for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++)
|
||||||
{
|
{
|
||||||
if(!AllocateInstrument(ins, smp))
|
if(!AllocateInstrument(ins, smp))
|
||||||
|
@ -796,7 +832,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(type == L"VST")
|
if(type == L"VST")
|
||||||
{
|
{
|
||||||
auto &mixPlug = m_MixPlugins[numPlugins];
|
auto &mixPlug = m_MixPlugins[numPlugins];
|
||||||
mixPlug = {};
|
mpt::reconstruct(mixPlug);
|
||||||
mixPlug.Info.dwPluginId1 = Vst::kEffectMagic;
|
mixPlug.Info.dwPluginId1 = Vst::kEffectMagic;
|
||||||
mixPlug.Info.gain = 10;
|
mixPlug.Info.gain = 10;
|
||||||
mixPlug.Info.szName = mpt::ToCharset(mpt::Charset::Locale, name);
|
mixPlug.Info.szName = mpt::ToCharset(mpt::Charset::Locale, name);
|
||||||
|
@ -818,6 +854,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
instr.AssignSample(0);
|
instr.AssignSample(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MMD0Sample sampleHeader;
|
||||||
|
sampleHeaderChunk.ReadStruct(sampleHeader);
|
||||||
|
|
||||||
uint8 numSamples = 1;
|
uint8 numSamples = 1;
|
||||||
static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7};
|
static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7};
|
||||||
if(!isSynth && maskedType < std::size(SamplesPerType))
|
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)
|
// 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 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.Keyboard[i] = smp + OctSampleMap[numSamples - 2][octave - 4];
|
||||||
instr.NoteMap[12 * octave + note] += OctTransposeMap[numSamples - 2][octave - 4];
|
instr.NoteMap[i] = static_cast<uint8>(instr.NoteMap[i] + OctTransposeMap[numSamples - 2][octave - 4]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(maskedType == MMDInstrHeader::EXTSAMPLE)
|
} else if(maskedType == MMDInstrHeader::EXTSAMPLE)
|
||||||
|
@ -862,18 +901,14 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
instr.Transpose(-24);
|
instr.Transpose(-24);
|
||||||
} else if(!isSynth && hardwareMixSamples)
|
} else if(!isSynth && hardwareMixSamples)
|
||||||
{
|
{
|
||||||
for(int octave = 7; octave < 10; octave++)
|
for(auto ¬e : instr.NoteMap)
|
||||||
{
|
{
|
||||||
for(int note = 0; note < 12; note++)
|
int realNote = note + sampleHeader.sampleTranspose;
|
||||||
{
|
if(realNote >= NOTE_MIDDLEC + 24)
|
||||||
instr.NoteMap[12 * octave + note] -= static_cast<uint8>((octave - 6) * 12);
|
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?
|
// midiChannel = 0xFF == midi instrument but with invalid channel, midiChannel = 0x00 == sample-based instrument?
|
||||||
if(sampleHeader.midiChannel > 0 && sampleHeader.midiChannel <= 16)
|
if(sampleHeader.midiChannel > 0 && sampleHeader.midiChannel <= 16)
|
||||||
{
|
{
|
||||||
|
@ -884,7 +919,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(!isSynth)
|
if(!isSynth)
|
||||||
{
|
{
|
||||||
auto &mixPlug = m_MixPlugins[numPlugins];
|
auto &mixPlug = m_MixPlugins[numPlugins];
|
||||||
mixPlug = {};
|
mpt::reconstruct(mixPlug);
|
||||||
mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P');
|
mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P');
|
||||||
mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D');
|
mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D');
|
||||||
mixPlug.Info.gain = 10;
|
mixPlug.Info.gain = 10;
|
||||||
|
@ -1121,7 +1156,7 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
ChnSettings[chn].nVolume = std::min<uint8>(file.ReadUint8(), 64);
|
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++)
|
for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
|
||||||
{
|
{
|
||||||
|
@ -1132,6 +1167,35 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
SetupMODPanning(true);
|
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;
|
std::vector<uint16be> sections;
|
||||||
if(!file.Seek(header.sectionTableOffset)
|
if(!file.Seek(header.sectionTableOffset)
|
||||||
|| !file.CanRead(songHeader.songLength * 2)
|
|| !file.CanRead(songHeader.songLength * 2)
|
||||||
|
@ -1153,12 +1217,12 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(!order.empty())
|
if(!order.empty())
|
||||||
order.push_back(order.GetIgnoreIndex());
|
order.push_back(order.GetIgnoreIndex());
|
||||||
|
|
||||||
|
const size_t orderStart = order.size();
|
||||||
size_t readOrders = playSeq.length;
|
size_t readOrders = playSeq.length;
|
||||||
if(!file.CanRead(readOrders))
|
if(!file.CanRead(readOrders))
|
||||||
LimitMax(readOrders, file.BytesLeft());
|
LimitMax(readOrders, file.BytesLeft());
|
||||||
LimitMax(readOrders, ORDERINDEX_MAX);
|
LimitMax(readOrders, ORDERINDEX_MAX);
|
||||||
|
|
||||||
size_t orderStart = order.size();
|
|
||||||
order.reserve(orderStart + readOrders);
|
order.reserve(orderStart + readOrders);
|
||||||
for(size_t ord = 0; ord < readOrders; ord++)
|
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 is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0;
|
||||||
const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0;
|
const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0;
|
||||||
const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK);
|
const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK);
|
||||||
m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat);
|
if(song == 0)
|
||||||
m_nDefaultSpeed = Clamp<uint8, uint8>(songHeader.tempo2, 1, 32);
|
|
||||||
if(bpmMode)
|
|
||||||
{
|
{
|
||||||
m_nDefaultRowsPerBeat = rowsPerBeat;
|
m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat);
|
||||||
m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u;
|
m_nDefaultSpeed = Clamp<uint8, uint8>(songHeader.tempo2, 1, 32);
|
||||||
|
if(bpmMode)
|
||||||
|
{
|
||||||
|
m_nDefaultRowsPerBeat = rowsPerBeat;
|
||||||
|
m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(songHeader.masterVol)
|
if(songHeader.masterVol)
|
||||||
|
@ -1316,8 +1383,9 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
CHANNELINDEX numTracks;
|
CHANNELINDEX numTracks;
|
||||||
ROWINDEX numRows;
|
ROWINDEX numRows;
|
||||||
std::string patName;
|
std::string patName;
|
||||||
int transpose = NOTE_MIN + 47 + songHeader.playTranspose;
|
int16 transpose = NOTE_MIN + 47 + songHeader.playTranspose;
|
||||||
FileReader cmdExt;
|
uint16 numPages = 0;
|
||||||
|
FileReader cmdExt, commandPages;
|
||||||
|
|
||||||
if(version < 1)
|
if(version < 1)
|
||||||
{
|
{
|
||||||
|
@ -1339,16 +1407,27 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.Seek(patHeader.blockInfoOffset);
|
file.Seek(patHeader.blockInfoOffset);
|
||||||
MMDBlockInfo blockInfo;
|
MMDBlockInfo blockInfo;
|
||||||
file.ReadStruct(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.
|
// We have now chased four pointers to get this far... lovely format.
|
||||||
file.ReadString<mpt::String::maybeNullTerminated>(patName, blockInfo.nameLength);
|
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
|
if(blockInfo.cmdExtTableOffset
|
||||||
&& file.Seek(blockInfo.cmdExtTableOffset)
|
&& file.Seek(blockInfo.cmdExtTableOffset)
|
||||||
&& file.Seek(file.ReadUint32BE()))
|
&& file.Seek(file.ReadUint32BE()))
|
||||||
{
|
{
|
||||||
cmdExt = file.ReadChunk(numTracks * numRows);
|
cmdExt = file.ReadChunk(numTracks * numRows * (1 + numPages));
|
||||||
}
|
}
|
||||||
|
|
||||||
file.Seek(offset);
|
file.Seek(offset);
|
||||||
|
@ -1362,68 +1441,20 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
pattern.SetName(patName);
|
pattern.SetName(patName);
|
||||||
LimitMax(numTracks, m_nChannels);
|
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);
|
const uint32 pageOffset = commandPages.ReadUint32BE();
|
||||||
for(CHANNELINDEX chn = 0; chn < numTracks; chn++, m++)
|
if(!pageOffset || !file.Seek(pageOffset))
|
||||||
{
|
continue;
|
||||||
const auto oldCmd = std::make_pair(m->command, m->param);
|
TranslateMEDPattern(file, cmdExt, pattern, context, true);
|
||||||
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 :(
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix jump order commands
|
// Fix jump order commands
|
||||||
for(const auto & [from, to] : jumpTargets)
|
for(const auto &[from, to] : jumpTargets)
|
||||||
{
|
{
|
||||||
PATTERNINDEX pat;
|
PATTERNINDEX pat;
|
||||||
if(from > 0 && order.IsValidPat(from - 1))
|
if(from > 0 && order.IsValidPat(from - 1))
|
||||||
|
@ -1443,6 +1474,21 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
numPatterns = pat + 1;
|
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;
|
basePattern += numPatterns;
|
||||||
|
|
||||||
if(!expData.nextModOffset || !file.Seek(expData.nextModOffset))
|
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
|
// 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;
|
ModInstrument *pIns;
|
||||||
program &= 0x7F;
|
program &= 0x7F;
|
||||||
|
@ -361,6 +364,7 @@ struct TrackState
|
||||||
FileReader track;
|
FileReader track;
|
||||||
tick_t nextEvent = 0;
|
tick_t nextEvent = 0;
|
||||||
uint8 command = 0;
|
uint8 command = 0;
|
||||||
|
uint8 midiBaseChannel = 0;
|
||||||
bool finished = false;
|
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];
|
CHANNELINDEX chn = midiChn.noteOn[note];
|
||||||
if(chn == CHANNELINDEX_INVALID)
|
if(chn == CHANNELINDEX_INVALID)
|
||||||
|
@ -666,13 +670,14 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
ppqn = 96;
|
ppqn = 96;
|
||||||
Order().clear();
|
Order().clear();
|
||||||
|
|
||||||
MidiChannelState midiChnStatus[16];
|
std::array<MidiChannelState, NUM_MIDI_CHANNELS> midiChnStatus;
|
||||||
const CHANNELINDEX tempoChannel = m_nChannels - 2, globalVolChannel = m_nChannels - 1;
|
const CHANNELINDEX tempoChannel = m_nChannels - 2, globalVolChannel = m_nChannels - 1;
|
||||||
const uint16 numTracks = fileHeader.numTracks;
|
const uint16 numTracks = fileHeader.numTracks;
|
||||||
std::vector<TrackState> tracks(numTracks);
|
std::vector<TrackState> tracks(numTracks);
|
||||||
std::vector<ModChannelState> modChnStatus(m_nChannels);
|
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 - 1);
|
||||||
|
drumChns.set(MIDI_DRUMCHANNEL + 15);
|
||||||
|
|
||||||
tick_t timeShift = 0;
|
tick_t timeShift = 0;
|
||||||
for(auto &track : tracks)
|
for(auto &track : tracks)
|
||||||
|
@ -820,6 +825,9 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
case 8: // Patch name
|
case 8: // Patch name
|
||||||
case 9: // Port name
|
case 9: // Port name
|
||||||
break;
|
break;
|
||||||
|
case 0x21: // MIDI port
|
||||||
|
tracks[t].midiBaseChannel = chunk.ReadUint8() * 16u;
|
||||||
|
break;
|
||||||
case 0x2F: // End Of Track
|
case 0x2F: // End Of Track
|
||||||
tracks[t].finished = true;
|
tracks[t].finished = true;
|
||||||
break;
|
break;
|
||||||
|
@ -840,6 +848,14 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
tempo = newTempo;
|
tempo = newTempo;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -857,7 +873,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
data1 = track.ReadUint8();
|
data1 = track.ReadUint8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint8 midiCh = command & 0x0F;
|
const uint8 midiCh = ((command & 0x0F) + tracks[t].midiBaseChannel) % NUM_MIDI_CHANNELS;
|
||||||
|
|
||||||
switch(command & 0xF0)
|
switch(command & 0xF0)
|
||||||
{
|
{
|
||||||
|
@ -1079,7 +1095,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
midiChnStatus[midiCh].SetPitchbend(data1 | (track.ReadUint8() << 7));
|
midiChnStatus[midiCh].SetPitchbend(data1 | (track.ReadUint8() << 7));
|
||||||
break;
|
break;
|
||||||
case 0xF0: // General / Immediate
|
case 0xF0: // General / Immediate
|
||||||
switch(midiCh)
|
switch(command & 0x0F)
|
||||||
{
|
{
|
||||||
case MIDIEvents::sysExStart: // SysEx
|
case MIDIEvents::sysExStart: // SysEx
|
||||||
case MIDIEvents::sysExEnd: // SysEx (continued)
|
case MIDIEvents::sysExEnd: // SysEx (continued)
|
||||||
|
|
|
@ -740,7 +740,9 @@ static bool ValidateHeader(const MO3ContainerHeader &containerHeader)
|
||||||
{
|
{
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1322,6 +1324,8 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
sample.uFlags.set(CHN_STEREO);
|
sample.uFlags.set(CHN_STEREO);
|
||||||
|
|
||||||
FileReader sampleData = file.ReadChunk(smpHeader.compressedSize);
|
FileReader sampleData = file.ReadChunk(smpHeader.compressedSize);
|
||||||
|
if(!smpHeader.length)
|
||||||
|
continue;
|
||||||
const uint8 numChannels = sample.GetNumChannels();
|
const uint8 numChannels = sample.GetNumChannels();
|
||||||
|
|
||||||
if(compression == MO3Sample::smpDeltaCompression || compression == MO3Sample::smpDeltaPrediction)
|
if(compression == MO3Sample::smpDeltaCompression || compression == MO3Sample::smpDeltaPrediction)
|
||||||
|
|
|
@ -322,6 +322,11 @@ struct MODSampleHeader
|
||||||
+ ((loopStart > length * 2) ? 1 : 0);
|
+ ((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
|
// Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore
|
||||||
static constexpr uint32 INVALID_BYTE_THRESHOLD = 40;
|
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
|
// 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;
|
uint32 malformedBytes = 0;
|
||||||
for(const auto &row : patternData)
|
for(const auto &row : patternData)
|
||||||
{
|
{
|
||||||
|
@ -553,6 +558,18 @@ static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, co
|
||||||
{
|
{
|
||||||
if(data[0] & mask)
|
if(data[0] & mask)
|
||||||
malformedBytes++;
|
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;
|
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
|
// Check if number of malformed bytes in MOD pattern data exceeds some threshold
|
||||||
template <typename TFileReader>
|
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;
|
MODPatternData patternData;
|
||||||
if(!file.Read(patternData))
|
if(!file.Read(patternData))
|
||||||
return false;
|
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;
|
SmpLength totalSampleLen = 0, wowSampleLen = 0;
|
||||||
m_nSamples = 31;
|
m_nSamples = 31;
|
||||||
uint32 invalidBytes = 0;
|
uint32 invalidBytes = 0;
|
||||||
|
bool hasLongSamples = false;
|
||||||
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
||||||
{
|
{
|
||||||
MODSampleHeader sampleHeader = ReadAndSwap<MODSampleHeader>(file, modMagicResult.swapBytes);
|
MODSampleHeader sampleHeader = ReadAndSwap<MODSampleHeader>(file, modMagicResult.swapBytes);
|
||||||
|
@ -894,7 +912,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(isHMNT)
|
if(isHMNT)
|
||||||
Samples[smp].nFineTune = -static_cast<int8>(sampleHeader.finetune << 3);
|
Samples[smp].nFineTune = -static_cast<int8>(sampleHeader.finetune << 3);
|
||||||
else if(Samples[smp].nLength > 65535)
|
else if(Samples[smp].nLength > 65535)
|
||||||
isNoiseTracker = false;
|
hasLongSamples = true;
|
||||||
|
|
||||||
if(sampleHeader.length && !sampleHeader.loopLength)
|
if(sampleHeader.length && !sampleHeader.loopLength)
|
||||||
hasRepLen0 = true;
|
hasRepLen0 = true;
|
||||||
|
@ -994,7 +1012,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
// Before loading patterns, apply some heuristics:
|
// Before loading patterns, apply some heuristics:
|
||||||
// - Scan patterns to check if file could be a NoiseTracker file in disguise.
|
// - 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.
|
// - 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.
|
// - Detect 7-bit panning and whether 8xx / E8x commands should be interpreted as panning at all.
|
||||||
bool onlyAmigaNotes = true;
|
bool onlyAmigaNotes = true;
|
||||||
|
@ -1003,13 +1021,13 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
const uint8 ENABLE_MOD_PANNING_THRESHOLD = 0x30;
|
const uint8 ENABLE_MOD_PANNING_THRESHOLD = 0x30;
|
||||||
if(!isNoiseTracker)
|
if(!isNoiseTracker)
|
||||||
{
|
{
|
||||||
|
const uint32 patternLength = m_nChannels * 64;
|
||||||
bool leftPanning = false, extendedPanning = false; // For detecting 800-880 panning
|
bool leftPanning = false, extendedPanning = false; // For detecting 800-880 panning
|
||||||
isNoiseTracker = isMdKd;
|
isNoiseTracker = isMdKd && !hasEmptySampleWithVolume && !hasLongSamples;
|
||||||
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
|
||||||
{
|
{
|
||||||
uint16 patternBreaks = 0;
|
uint16 patternBreaks = 0;
|
||||||
|
for(uint32 i = 0; i < patternLength; i++)
|
||||||
for(uint32 i = 0; i < 256; i++)
|
|
||||||
{
|
{
|
||||||
ModCommand m;
|
ModCommand m;
|
||||||
const auto data = ReadAndSwap<std::array<uint8, 4>>(file, modMagicResult.swapBytes && pat == 0);
|
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);
|
file.Seek(modMagicResult.patternDataOffset);
|
||||||
|
|
||||||
const CHANNELINDEX readChannels = (isFLT8 ? 4 : m_nChannels); // 4 channels per pattern in FLT8 format.
|
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.
|
if(isFLT8)
|
||||||
bool hasTempoCommands = false, definitelyCIA = false; // for detecting VBlank MODs
|
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
|
// 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;
|
bool filterState = false;
|
||||||
int filterTransitions = 0;
|
int filterTransitions = 0;
|
||||||
|
@ -1245,6 +1264,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.Seek(nextSample);
|
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)
|
#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)
|
// Check if a name string is valid (i.e. doesn't contain binary garbage data)
|
||||||
template <size_t N>
|
static uint32 CountInvalidChars(const mpt::span<const char> name) noexcept
|
||||||
static uint32 CountInvalidChars(const char (&name)[N])
|
|
||||||
{
|
{
|
||||||
uint32 invalidChars = 0;
|
uint32 invalidChars = 0;
|
||||||
for(int8 c : name) // char can be signed or unsigned
|
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
|
// 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.
|
// 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)
|
// 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
|
// 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
|
// 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
|
// files with *too* many bogus characters. Arbitrary threshold: 48 bogus characters in total
|
||||||
// or more than 5 invalid characters just in the title alone.
|
// or more than 5 invalid characters just in the title alone
|
||||||
uint32 invalidChars = CountInvalidChars(fileHeaders.songname);
|
uint32 invalidCharsInTitle = CountInvalidChars(fileHeaders.songname);
|
||||||
if(invalidChars > 5)
|
uint32 invalidChars = invalidCharsInTitle;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SmpLength totalSampleLen = 0;
|
SmpLength totalSampleLen = 0;
|
||||||
uint8 allVolumes = 0;
|
uint8 allVolumes = 0;
|
||||||
|
uint8 validNameCount = 0;
|
||||||
|
bool invalidNames = false;
|
||||||
|
|
||||||
for(SAMPLEINDEX smp = 0; smp < 15; smp++)
|
for(SAMPLEINDEX smp = 0; smp < 15; smp++)
|
||||||
{
|
{
|
||||||
|
@ -1423,11 +1470,19 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
||||||
|
|
||||||
invalidChars += CountInvalidChars(sampleHeader.name);
|
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)
|
// 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
|
if(invalidChars > 48
|
||||||
|| sampleHeader.volume > 64
|
|| sampleHeader.volume > 64
|
||||||
|| sampleHeader.finetune != 0
|
|| sampleHeader.length > 37000)
|
||||||
|| sampleHeader.length > 32768)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1436,6 +1491,12 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders)
|
||||||
allVolumes |= sampleHeader.volume;
|
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)
|
// 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)
|
if(totalSampleLen == 0 || allVolumes == 0)
|
||||||
{
|
{
|
||||||
|
@ -1534,13 +1595,15 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.Seek(20);
|
file.Seek(20);
|
||||||
for(SAMPLEINDEX smp = 1; smp <= 15; smp++)
|
for(SAMPLEINDEX smp = 1; smp <= 15; smp++)
|
||||||
{
|
{
|
||||||
|
ModSample &mptSmp = Samples[smp];
|
||||||
MODSampleHeader sampleHeader;
|
MODSampleHeader sampleHeader;
|
||||||
file.ReadStruct(sampleHeader);
|
file.ReadStruct(sampleHeader);
|
||||||
ReadSample(sampleHeader, Samples[smp], m_szNames[smp], true);
|
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.
|
// Ultimate Soundtracker 1.8 and D.O.C. SoundTracker IX always have sample names containing disk names.
|
||||||
hasDiskNames = false;
|
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)
|
// 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)
|
if(sampleHeader.loopLength > 1)
|
||||||
{
|
{
|
||||||
Samples[smp].nLoopStart = sampleHeader.loopStart;
|
mptSmp.nLoopStart = sampleHeader.loopStart;
|
||||||
Samples[smp].nLoopEnd = sampleHeader.loopStart + sampleHeader.loopLength * 2;
|
mptSmp.nLoopEnd = sampleHeader.loopStart + sampleHeader.loopLength * 2;
|
||||||
Samples[smp].SanitizeLoops();
|
mptSmp.SanitizeLoops();
|
||||||
}
|
}
|
||||||
|
|
||||||
// UST only handles samples up to 9999 bytes. Master Soundtracker 1.0 and SoundTracker 2.0 introduce 32KB samples.
|
// 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.
|
// "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.
|
// Apart from those hidden patterns, the files play fine.
|
||||||
// Example: operation wolf - wolf1.mod (MD5 739acdbdacd247fbefcac7bc2d8abe6b, SHA1 e6b4813daacbf95f41ce9ec3b22520a2ae07eed8)
|
// Example: operation wolf - wolf1.mod (MD5 739acdbdacd247fbefcac7bc2d8abe6b, SHA1 e6b4813daacbf95f41ce9ec3b22520a2ae07eed8)
|
||||||
if(illegalBytes > 512)
|
if(illegalBytes > std::max(512u, numPatterns * 128u))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for(ROWINDEX row = 0; row < 64; row++)
|
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)
|
switch(minVersion)
|
||||||
{
|
{
|
||||||
case UST1_00:
|
case UST1_00:
|
||||||
|
|
|
@ -29,8 +29,8 @@ struct OktIffChunk
|
||||||
idSBOD = MagicBE("SBOD"),
|
idSBOD = MagicBE("SBOD"),
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32be signature; // IFF chunk name
|
uint32be signature; // IFF chunk name
|
||||||
uint32be chunksize; // chunk size without header
|
uint32be chunksize; // chunk size without header
|
||||||
};
|
};
|
||||||
|
|
||||||
MPT_BINARY_STRUCT(OktIffChunk, 8)
|
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
|
// Parse a pattern block
|
||||||
static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndFile, const std::array<int8, 8> pairedChn)
|
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++)
|
for(CHANNELINDEX chn = 0; chn < chns; chn++)
|
||||||
{
|
{
|
||||||
ModCommand &m = rowCmd[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>();
|
const auto [note, instr, effect, param] = chunk.ReadArray<uint8, 4>();
|
||||||
|
|
||||||
if(note > 0 && note <= 36)
|
if(note > 0 && note <= 36)
|
||||||
|
@ -147,18 +162,22 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* these aren't like regular arpeggio: "down" means to *subtract* the offset from the note.
|
|
||||||
For now I'm going to leave these unimplemented. */
|
|
||||||
case 10: // A Arpeggio 1 (down, orig, up)
|
case 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)
|
case 11: // B Arpeggio 2 (orig, up, orig, down)
|
||||||
if(param)
|
if(param)
|
||||||
{
|
{
|
||||||
m.command = CMD_ARPEGGIO;
|
m.command = CMD_ARPEGGIO;
|
||||||
m.param = param;
|
m.param = (param & 0xF0) | InvertArpeggioParam(param & 0x0F);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
// This one is close enough to "standard" arpeggio -- I think!
|
// This one is close enough to "standard" arpeggio -- I think!
|
||||||
case 12: // C Arpeggio 3 (up, up, orig)
|
case 12: // C Arpeggio 3 (up, up, orig)
|
||||||
if(param)
|
if(param)
|
||||||
|
@ -286,9 +305,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
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;
|
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);
|
MPT_UNREFERENCED_PARAMETER(pfilesize);
|
||||||
return ProbeSuccess;
|
return ProbeSuccess;
|
||||||
}
|
}
|
||||||
|
@ -330,7 +341,6 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare some arrays to store offsets etc.
|
|
||||||
std::vector<FileReader> patternChunks;
|
std::vector<FileReader> patternChunks;
|
||||||
std::vector<FileReader> sampleChunks;
|
std::vector<FileReader> sampleChunks;
|
||||||
std::array<int8, 8> pairedChn{{}};
|
std::array<int8, 8> pairedChn{{}};
|
||||||
|
@ -347,15 +357,11 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
{
|
{
|
||||||
OktIffChunk iffHead;
|
OktIffChunk iffHead;
|
||||||
if(!file.ReadStruct(iffHead))
|
if(!file.ReadStruct(iffHead))
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
FileReader chunk = file.ReadChunk(iffHead.chunksize);
|
FileReader chunk = file.ReadChunk(iffHead.chunksize);
|
||||||
if(!chunk.IsValid())
|
if(!chunk.IsValid())
|
||||||
{
|
continue;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(iffHead.signature)
|
switch(iffHead.signature)
|
||||||
{
|
{
|
||||||
|
@ -433,12 +439,6 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
sampleChunks.push_back(chunk);
|
sampleChunks.push_back(chunk);
|
||||||
}
|
}
|
||||||
break;
|
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;
|
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);
|
MPT_UNREFERENCED_PARAMETER(pfilesize);
|
||||||
return ProbeSuccess;
|
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 '@': m.command = (m.param ? CMD_DUMMY : CMD_NONE); break;
|
||||||
case 'A': m.command = CMD_SPEED; break;
|
case 'A': m.command = CMD_SPEED; break;
|
||||||
case 'B': m.command = CMD_POSITIONJUMP; 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 'D': m.command = CMD_VOLUMESLIDE; break;
|
||||||
case 'E': m.command = CMD_PORTAMENTODOWN; break;
|
case 'E': m.command = CMD_PORTAMENTODOWN; break;
|
||||||
case 'F': m.command = CMD_PORTAMENTOUP; 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_POSITIONJUMP: command = 'B'; break;
|
||||||
case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break;
|
case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break;
|
||||||
case CMD_VOLUMESLIDE: command = 'D'; 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_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_PORTAMENTOUP: command = 'F'; if(param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break;
|
||||||
case CMD_TONEPORTAMENTO: command = 'G'; break;
|
case CMD_TONEPORTAMENTO: command = 'G'; break;
|
||||||
case CMD_VIBRATO: command = 'H'; break;
|
case CMD_VIBRATO: command = 'H'; break;
|
||||||
case CMD_TREMOR: command = 'I'; break;
|
case CMD_TREMOR: command = 'I'; break;
|
||||||
|
@ -110,11 +110,11 @@ void CSoundFile::S3MSaveConvert(const ModCommand &source, uint8 &command, uint8
|
||||||
command = 'X';
|
command = 'X';
|
||||||
if(toIT && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD)))
|
if(toIT && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD)))
|
||||||
{
|
{
|
||||||
if (param == 0xA4) { command = 'S'; param = 0x91; }
|
if(param == 0xA4) { command = 'S'; param = 0x91; }
|
||||||
else if (param == 0x80) { param = 0xFF; }
|
else if(param == 0x80) { param = 0xFF; }
|
||||||
else if (param < 0x80) { param <<= 1; }
|
else if(param < 0x80) { param <<= 1; }
|
||||||
else command = 0;
|
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;
|
param >>= 1;
|
||||||
}
|
}
|
||||||
|
@ -240,54 +240,70 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
bool nonCompatTracker = false;
|
bool nonCompatTracker = false;
|
||||||
bool isST3 = false;
|
bool isST3 = false;
|
||||||
bool isSchism = false;
|
bool isSchism = false;
|
||||||
|
const bool usePanningTable = fileHeader.usePanningTable == S3MFileHeader::idPanning;
|
||||||
const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x4FFF) ? fileHeader.reserved2 : (fileHeader.cwtv - 0x4050));
|
const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x4FFF) ? fileHeader.reserved2 : (fileHeader.cwtv - 0x4050));
|
||||||
switch(fileHeader.cwtv & S3MFileHeader::trackerMask)
|
switch(fileHeader.cwtv & S3MFileHeader::trackerMask)
|
||||||
{
|
{
|
||||||
case S3MFileHeader::trkAkord & S3MFileHeader::trackerMask:
|
case S3MFileHeader::trkAkord & S3MFileHeader::trackerMask:
|
||||||
if(fileHeader.cwtv == S3MFileHeader::trkAkord)
|
if(fileHeader.cwtv == S3MFileHeader::trkAkord)
|
||||||
madeWithTracker = U_("Akord");
|
madeWithTracker = UL_("Akord");
|
||||||
break;
|
break;
|
||||||
case S3MFileHeader::trkScreamTracker:
|
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
|
// MPT and OpenMPT before 1.17.03.02 - Simply keep default (filter) MIDI macros
|
||||||
if((fileHeader.masterVolume & 0x80) != 0)
|
if((fileHeader.masterVolume & 0x80) != 0)
|
||||||
{
|
{
|
||||||
m_dwLastSavedWithVersion = MPT_V("1.16.00.00");
|
m_dwLastSavedWithVersion = MPT_V("1.16");
|
||||||
madeWithTracker = U_("ModPlug Tracker / OpenMPT 1.17");
|
madeWithTracker = UL_("ModPlug Tracker / OpenMPT 1.17");
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 beta1 does.
|
// MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 alpha6 does.
|
||||||
m_dwLastSavedWithVersion = MPT_V("1.00.00.00");
|
m_dwLastSavedWithVersion = MPT_V("1.00.00.A0");
|
||||||
madeWithTracker = U_("ModPlug Tracker 1.0 alpha");
|
madeWithTracker = UL_("ModPlug Tracker 1.0 alpha");
|
||||||
}
|
}
|
||||||
keepMidiMacros = true;
|
keepMidiMacros = true;
|
||||||
nonCompatTracker = true;
|
nonCompatTracker = true;
|
||||||
m_playBehaviour.set(kST3LimitPeriod);
|
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
|
} 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,
|
// 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.
|
// 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.
|
// 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.
|
// 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;
|
formatTrackerStr = true;
|
||||||
isST3 = true;
|
isST3 = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case S3MFileHeader::trkImagoOrpheus:
|
case S3MFileHeader::trkImagoOrpheus:
|
||||||
madeWithTracker = U_("Imago Orpheus");
|
formatTrackerStr = (fileHeader.cwtv != S3MFileHeader::trkPlayerPRO);
|
||||||
formatTrackerStr = true;
|
if(formatTrackerStr)
|
||||||
|
madeWithTracker = UL_("Imago Orpheus");
|
||||||
|
else
|
||||||
|
madeWithTracker = UL_("PlayerPRO");
|
||||||
nonCompatTracker = true;
|
nonCompatTracker = true;
|
||||||
break;
|
break;
|
||||||
case S3MFileHeader::trkImpulseTracker:
|
case S3MFileHeader::trkImpulseTracker:
|
||||||
if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14)
|
if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14)
|
||||||
{
|
{
|
||||||
madeWithTracker = U_("Impulse Tracker");
|
madeWithTracker = UL_("Impulse Tracker");
|
||||||
formatTrackerStr = true;
|
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
|
} else
|
||||||
{
|
{
|
||||||
madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - S3MFileHeader::trkIT2_14);
|
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:
|
case S3MFileHeader::trkSchismTracker:
|
||||||
if(fileHeader.cwtv == S3MFileHeader::trkBeRoTrackerOld)
|
if(fileHeader.cwtv == S3MFileHeader::trkBeRoTrackerOld)
|
||||||
{
|
{
|
||||||
madeWithTracker = U_("BeRoTracker");
|
madeWithTracker = UL_("BeRoTracker");
|
||||||
m_playBehaviour.set(kST3LimitPeriod);
|
m_playBehaviour.set(kST3LimitPeriod);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
|
@ -327,28 +343,40 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
nonCompatTracker = true;
|
nonCompatTracker = true;
|
||||||
break;
|
break;
|
||||||
case S3MFileHeader::trkOpenMPT:
|
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;
|
uint32 mptVersion = (fileHeader.cwtv & S3MFileHeader::versionMask) << 16;
|
||||||
if(mptVersion >= 0x01'29'00'00)
|
if(mptVersion >= 0x01'29'00'00)
|
||||||
mptVersion |= fileHeader.reserved2;
|
mptVersion |= fileHeader.reserved2;
|
||||||
m_dwLastSavedWithVersion = Version(mptVersion);
|
m_dwLastSavedWithVersion = Version(mptVersion);
|
||||||
madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion);
|
madeWithTracker = UL_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
madeWithTracker = U_("Graoumf Tracker");
|
madeWithTracker = UL_("Graoumf Tracker");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case S3MFileHeader::trkBeRoTracker:
|
case S3MFileHeader::trkBeRoTracker:
|
||||||
madeWithTracker = U_("BeRoTracker");
|
madeWithTracker = UL_("BeRoTracker");
|
||||||
m_playBehaviour.set(kST3LimitPeriod);
|
m_playBehaviour.set(kST3LimitPeriod);
|
||||||
break;
|
break;
|
||||||
case S3MFileHeader::trkCreamTracker:
|
case S3MFileHeader::trkCreamTracker:
|
||||||
madeWithTracker = U_("CreamTracker");
|
madeWithTracker = UL_("CreamTracker");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if(fileHeader.cwtv == S3MFileHeader::trkCamoto)
|
if(fileHeader.cwtv == S3MFileHeader::trkCamoto)
|
||||||
madeWithTracker = U_("Camoto");
|
madeWithTracker = UL_("Camoto");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(formatTrackerStr)
|
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));
|
madeWithTracker = MPT_UFORMAT("{} {}.{}")(madeWithTracker, (fileHeader.cwtv & 0xF00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_modFormat.formatName = U_("Scream Tracker 3");
|
m_modFormat.formatName = UL_("Scream Tracker 3");
|
||||||
m_modFormat.type = U_("s3m");
|
m_modFormat.type = UL_("s3m");
|
||||||
m_modFormat.madeWithTracker = std::move(madeWithTracker);
|
m_modFormat.madeWithTracker = std::move(madeWithTracker);
|
||||||
m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437;
|
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(kST3OffsetWithoutInstrument);
|
||||||
m_playBehaviour.reset(kApplyUpperPeriodLimit);
|
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
|
// This broken behaviour is not present in ST3.01
|
||||||
m_playBehaviour.reset(kST3TonePortaWithAdlibNote);
|
m_playBehaviour.reset(kST3TonePortaWithAdlibNote);
|
||||||
|
@ -439,15 +467,18 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
else
|
else
|
||||||
m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); // Bit 7 = Stereo (we always use stereo)
|
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
|
// Approximately as loud as in DOSBox and a real SoundBlaster 16
|
||||||
m_nVSTiVolume = 36;
|
m_nVSTiVolume = 36;
|
||||||
if(isSchism && schismDateVersion < SchismVersionFromDate<2018, 11, 12>::date)
|
if(isSchism && schismDateVersion < SchismVersionFromDate<2018, 11, 12>::date)
|
||||||
m_nVSTiVolume = 64;
|
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
|
// Channel setup
|
||||||
m_nChannels = 4;
|
m_nChannels = 4;
|
||||||
std::bitset<32> isAdlibChannel;
|
std::bitset<32> isAdlibChannel;
|
||||||
|
@ -489,17 +520,28 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
file.ReadVector(patternOffsets, fileHeader.patNum);
|
file.ReadVector(patternOffsets, fileHeader.patNum);
|
||||||
|
|
||||||
// Read extended channel panning
|
// Read extended channel panning
|
||||||
if(fileHeader.usePanningTable == S3MFileHeader::idPanning)
|
if(usePanningTable)
|
||||||
{
|
{
|
||||||
uint8 pan[32];
|
bool hasChannelsWithoutPanning = false;
|
||||||
file.ReadArray(pan);
|
const auto pan = file.ReadArray<uint8, 32>();
|
||||||
for(CHANNELINDEX i = 0; i < 32; i++)
|
for(CHANNELINDEX i = 0; i < 32; i++)
|
||||||
{
|
{
|
||||||
if((pan[i] & 0x20) != 0 && (!isST3 || !isAdlibChannel[i]))
|
if((pan[i] & 0x20) != 0 && (!isST3 || !isAdlibChannel[i]))
|
||||||
{
|
{
|
||||||
ChnSettings[i].nPan = (static_cast<uint16>(pan[i] & 0x0F) * 256 + 8) / 15;
|
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
|
// Reading sample headers
|
||||||
|
@ -520,11 +562,11 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
|
|
||||||
if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
|
if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
|
||||||
{
|
{
|
||||||
const uint32 sampleOffset = sampleHeader.GetSampleOffset();
|
if(sampleHeader.length != 0)
|
||||||
if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset))
|
|
||||||
{
|
{
|
||||||
SampleIO sampleIO = sampleHeader.GetSampleFormat((fileHeader.formatVersion == S3MFileHeader::oldVersion));
|
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;
|
anySamples = true;
|
||||||
if(sampleIO.GetEncoding() == SampleIO::ADPCM)
|
if(sampleIO.GetEncoding() == SampleIO::ADPCM)
|
||||||
anyADPCM = true;
|
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)
|
// 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;
|
isST3 = false;
|
||||||
MPT_UNUSED(isST3);
|
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)
|
} 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).
|
// 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)
|
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).
|
// 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
|
// 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
|
// Write patterns
|
||||||
enum class S3MChannelType : uint8 { kUnused = 0, kPCM = 1, kAdlib = 2 };
|
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;
|
bool globalCmdOnMutedChn = false;
|
||||||
for(PATTERNINDEX pat = 0; pat < writePatterns; pat++)
|
for(PATTERNINDEX pat = 0; pat < writePatterns; pat++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -207,6 +207,8 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
SFXSampleHeader sampleHeader;
|
SFXSampleHeader sampleHeader;
|
||||||
|
|
||||||
file.ReadStruct(sampleHeader);
|
file.ReadStruct(sampleHeader);
|
||||||
|
// cppcheck false-positive
|
||||||
|
// cppcheck-suppress uninitvar
|
||||||
sampleHeader.ConvertToMPT(Samples[smp], sampleLen[smp - 1]);
|
sampleHeader.ConvertToMPT(Samples[smp], sampleLen[smp - 1]);
|
||||||
|
|
||||||
// Get rid of weird characters in sample names.
|
// Get rid of weird characters in sample names.
|
||||||
|
|
|
@ -230,6 +230,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
InitializeGlobals(MOD_TYPE_STM);
|
InitializeGlobals(MOD_TYPE_STM);
|
||||||
|
InitializeChannels();
|
||||||
|
|
||||||
m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname);
|
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)
|
if(fileHeader.verMinor > 10)
|
||||||
m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u;
|
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
|
// Read samples
|
||||||
uint16 sampleOffsets[31];
|
uint16 sampleOffsets[31];
|
||||||
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
|
||||||
|
@ -461,6 +455,7 @@ bool CSoundFile::ReadSTX(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
InitializeGlobals(MOD_TYPE_STM);
|
InitializeGlobals(MOD_TYPE_STM);
|
||||||
|
InitializeChannels();
|
||||||
|
|
||||||
m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName);
|
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_nDefaultSpeed = initTempo >> 4;
|
||||||
m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u;
|
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;
|
std::vector<uint16le> patternOffsets, sampleOffsets;
|
||||||
file.Seek(fileHeader.patTableOffset << 4);
|
file.Seek(fileHeader.patTableOffset << 4);
|
||||||
file.ReadVector(patternOffsets, fileHeader.numPatterns);
|
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>();
|
const auto [instr, note, command, param] = file.ReadArray<uint8, 4>();
|
||||||
|
|
||||||
m.instr = instr;
|
m.instr = instr;
|
||||||
m.note = note;
|
|
||||||
m.param = param;
|
m.param = param;
|
||||||
|
if(note)
|
||||||
if(m.note)
|
|
||||||
{
|
{
|
||||||
m.note += 24 + NOTE_MIN;
|
m.note = NOTE_MIDDLEC - 36 + note;
|
||||||
chnMem = ChannelMemory();
|
chnMem = ChannelMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a nibble-swapped param value used for auto fine volside
|
// Volume slides not only have their nibbles swapped, but the up and down parameters also add up
|
||||||
// and auto global fine volside
|
const int totalSlide = -static_cast<int>(m.param >> 4) + (m.param & 0x0F);
|
||||||
uint8 swapped = (m.param >> 4) | (m.param << 4);
|
const uint8 slideParam = static_cast<uint8>((totalSlide > 0) ? totalSlide << 4 : -totalSlide);
|
||||||
|
|
||||||
if((command & 0xF0) == 0xF0)
|
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.param = mpt::saturate_round<ModCommand::PARAM>(ConvertTempo(ciaTempo).ToDouble());
|
||||||
m.command = CMD_TEMPO;
|
m.command = CMD_TEMPO;
|
||||||
} else
|
|
||||||
{
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
}
|
}
|
||||||
} else switch(command)
|
} else switch(command)
|
||||||
{
|
{
|
||||||
case 0x00: // arpeggio
|
case 0x00: // arpeggio
|
||||||
if(m.param)
|
if(m.param)
|
||||||
m.command = CMD_ARPEGGIO;
|
m.command = CMD_ARPEGGIO;
|
||||||
else
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x01: // portamento up
|
case 0x01: // portamento up
|
||||||
m.command = CMD_PORTAMENTOUP;
|
m.command = CMD_PORTAMENTOUP;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x02: // portamento down
|
case 0x02: // portamento down
|
||||||
m.command = CMD_PORTAMENTODOWN;
|
m.command = CMD_PORTAMENTODOWN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x03: // auto fine portamento up
|
case 0x03: // auto fine portamento up
|
||||||
chnMem.autoFinePorta = 0x10 | std::min(m.param, ModCommand::PARAM(15));
|
chnMem.autoFinePorta = 0x10 | std::min(m.param, ModCommand::PARAM(15));
|
||||||
chnMem.autoPortaUp = 0;
|
chnMem.autoPortaUp = 0;
|
||||||
chnMem.autoPortaDown = 0;
|
chnMem.autoPortaDown = 0;
|
||||||
chnMem.autoTonePorta = 0;
|
chnMem.autoTonePorta = 0;
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x04: // auto fine portamento down
|
case 0x04: // auto fine portamento down
|
||||||
chnMem.autoFinePorta = 0x20 | std::min(m.param, ModCommand::PARAM(15));
|
chnMem.autoFinePorta = 0x20 | std::min(m.param, ModCommand::PARAM(15));
|
||||||
chnMem.autoPortaUp = 0;
|
chnMem.autoPortaUp = 0;
|
||||||
chnMem.autoPortaDown = 0;
|
chnMem.autoPortaDown = 0;
|
||||||
chnMem.autoTonePorta = 0;
|
chnMem.autoTonePorta = 0;
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x05: // auto portamento up
|
case 0x05: // auto portamento up
|
||||||
chnMem.autoFinePorta = 0;
|
chnMem.autoFinePorta = 0;
|
||||||
chnMem.autoPortaUp = m.param;
|
chnMem.autoPortaUp = m.param;
|
||||||
chnMem.autoPortaDown = 0;
|
chnMem.autoPortaDown = 0;
|
||||||
chnMem.autoTonePorta = 0;
|
chnMem.autoTonePorta = 0;
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x06: // auto portamento down
|
case 0x06: // auto portamento down
|
||||||
chnMem.autoFinePorta = 0;
|
chnMem.autoFinePorta = 0;
|
||||||
chnMem.autoPortaUp = 0;
|
chnMem.autoPortaUp = 0;
|
||||||
chnMem.autoPortaDown = m.param;
|
chnMem.autoPortaDown = m.param;
|
||||||
chnMem.autoTonePorta = 0;
|
chnMem.autoTonePorta = 0;
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x07: // set global volume
|
case 0x07: // set global volume
|
||||||
m.command = CMD_GLOBALVOLUME;
|
m.command = CMD_GLOBALVOLUME;
|
||||||
globalVolSlide = 0;
|
globalVolSlide = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x08: // auto global fine volume slide
|
case 0x08: // auto global fine volume slide
|
||||||
globalVolSlide = swapped;
|
globalVolSlide = slideParam;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x09: // fine portamento up
|
case 0x09: // fine portamento up
|
||||||
m.command = CMD_MODCMDEX;
|
m.command = CMD_MODCMDEX;
|
||||||
m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15));
|
m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0A: // fine portamento down
|
case 0x0A: // fine portamento down
|
||||||
m.command = CMD_MODCMDEX;
|
m.command = CMD_MODCMDEX;
|
||||||
m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15));
|
m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0B: // auto fine volume slide
|
case 0x0B: // auto fine volume slide
|
||||||
chnMem.autoVolSlide = swapped;
|
chnMem.autoVolSlide = slideParam;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0C: // set volume
|
case 0x0C: // set volume
|
||||||
m.volcmd = VOLCMD_VOLUME;
|
m.volcmd = VOLCMD_VOLUME;
|
||||||
m.vol = m.param;
|
m.vol = m.param;
|
||||||
chnMem.autoVolSlide = 0;
|
chnMem.autoVolSlide = 0;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0D: // volume slide (param is swapped compared to .mod)
|
case 0x0D: // volume slide (param is swapped compared to .mod)
|
||||||
if(m.param & 0xF0)
|
if(totalSlide < 0)
|
||||||
{
|
m.SetVolumeCommand(VOLCMD_VOLSLIDEDOWN, slideParam & 0x0F);
|
||||||
m.volcmd = VOLCMD_VOLSLIDEDOWN;
|
else if(totalSlide > 0)
|
||||||
m.vol = m.param >> 4;
|
m.SetVolumeCommand(VOLCMD_VOLSLIDEUP, slideParam >> 4);
|
||||||
} else if(m.param & 0x0F)
|
|
||||||
{
|
|
||||||
m.volcmd = VOLCMD_VOLSLIDEUP;
|
|
||||||
m.vol = m.param & 0xF;
|
|
||||||
}
|
|
||||||
chnMem.autoVolSlide = 0;
|
chnMem.autoVolSlide = 0;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0E: // set filter (also uses opposite value compared to .mod)
|
case 0x0E: // set filter (also uses opposite value compared to .mod)
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 1 ^ (m.param ? 1 : 0));
|
||||||
m.param = 1 ^ (m.param ? 1 : 0);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0F: // set speed
|
case 0x0F: // set speed
|
||||||
m.command = CMD_SPEED;
|
|
||||||
speedFrac = m.param & 0x0F;
|
speedFrac = m.param & 0x0F;
|
||||||
m.param >>= 4;
|
m.SetEffectCommand(CMD_SPEED, m.param >> 4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x10: // auto vibrato
|
case 0x10: // auto vibrato
|
||||||
chnMem.autoVibrato = m.param;
|
chnMem.autoVibrato = m.param;
|
||||||
chnMem.vibratoMem = 0;
|
chnMem.vibratoMem = 0;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x11: // auto tremolo
|
case 0x11: // auto tremolo
|
||||||
if(m.param & 0xF)
|
if(m.param & 0xF)
|
||||||
chnMem.autoTremolo = m.param;
|
chnMem.autoTremolo = m.param;
|
||||||
else
|
else
|
||||||
chnMem.autoTremolo = 0;
|
chnMem.autoTremolo = 0;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x12: // pattern break
|
case 0x12: // pattern break
|
||||||
m.command = CMD_PATTERNBREAK;
|
m.command = CMD_PATTERNBREAK;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x13: // auto tone portamento
|
case 0x13: // auto tone portamento
|
||||||
chnMem.autoFinePorta = 0;
|
chnMem.autoFinePorta = 0;
|
||||||
chnMem.autoPortaUp = 0;
|
chnMem.autoPortaUp = 0;
|
||||||
|
@ -601,13 +554,10 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
chnMem.autoTonePorta = m.param;
|
chnMem.autoTonePorta = m.param;
|
||||||
|
|
||||||
chnMem.tonePortaMem = 0;
|
chnMem.tonePortaMem = 0;
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x14: // position jump
|
case 0x14: // position jump
|
||||||
m.command = CMD_POSITIONJUMP;
|
m.command = CMD_POSITIONJUMP;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x16: // start loop sequence
|
case 0x16: // start loop sequence
|
||||||
if(m.instr && m.instr <= loopInfo.size())
|
if(m.instr && m.instr <= loopInfo.size())
|
||||||
{
|
{
|
||||||
|
@ -620,10 +570,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m.vol = m.param;
|
m.vol = m.param;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x17: // play only loop nn
|
case 0x17: // play only loop nn
|
||||||
if(m.instr && m.instr <= loopInfo.size())
|
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.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x18: // play sequence without loop
|
case 0x18: // play sequence without loop
|
||||||
if(m.instr && m.instr <= loopInfo.size())
|
if(m.instr && m.instr <= loopInfo.size())
|
||||||
{
|
{
|
||||||
|
@ -657,10 +601,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
nonLooped[m.instr - 1] = ++m_nSamples;
|
nonLooped[m.instr - 1] = ++m_nSamples;
|
||||||
m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
|
m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x19: // play only loop nn without loop
|
case 0x19: // play only loop nn without loop
|
||||||
if(m.instr && m.instr <= loopInfo.size())
|
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.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x1D: // fine volume slide (nibble order also swapped)
|
case 0x1D: // fine volume slide (nibble order also swapped)
|
||||||
m.command = CMD_VOLUMESLIDE;
|
if(totalSlide < 0) // slide down
|
||||||
m.param = swapped;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xB0 | (slideParam & 0x0F));
|
||||||
if(m.param & 0xF0) // slide down
|
else if(totalSlide > 0)
|
||||||
m.param |= 0x0F;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xA0 | (slideParam >> 4));
|
||||||
else if(m.param & 0x0F)
|
|
||||||
m.param |= 0xF0;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x20: // "delayed fade"
|
case 0x20: // "delayed fade"
|
||||||
// just behave like either a normal fade or a notecut
|
// just behave like either a normal fade or a notecut
|
||||||
// depending on the speed
|
// depending on the speed
|
||||||
if(m.param & 0xF0)
|
if(m.param & 0xF0)
|
||||||
{
|
{
|
||||||
chnMem.autoVolSlide = m.param >> 4;
|
chnMem.autoVolSlide = m.param >> 4;
|
||||||
m.command = CMD_NONE;
|
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xC0 | (m.param & 0xF));
|
||||||
m.param = 0xC0 | (m.param & 0xF);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x21: // note delay
|
case 0x21: // note delay
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0xD0 | std::min(m.param, ModCommand::PARAM(15)));
|
||||||
m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x22: // retrigger note
|
case 0x22: // retrigger note
|
||||||
m.command = CMD_MODCMDEX;
|
m.SetEffectCommand(CMD_MODCMDEX, 0x90 | std::min(m.param, ModCommand::PARAM(15)));
|
||||||
m.param = 0x90 | std::min(m.param, ModCommand::PARAM(15));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x49: // set sample offset
|
case 0x49: // set sample offset
|
||||||
m.command = CMD_OFFSET;
|
m.command = CMD_OFFSET;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x4E: // other protracker commands (pattern loop / delay)
|
case 0x4E: // other protracker commands (pattern loop / delay)
|
||||||
if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
|
if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
|
||||||
m.command = CMD_MODCMDEX;
|
m.command = CMD_MODCMDEX;
|
||||||
else
|
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x4F: // set speed/tempo
|
case 0x4F: // set speed/tempo
|
||||||
if(m.param < 0x20)
|
if(m.param < 0x20)
|
||||||
{
|
{
|
||||||
|
@ -732,9 +656,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m.command = CMD_TEMPO;
|
m.command = CMD_TEMPO;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m.command = CMD_NONE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -665,7 +665,7 @@ struct SymInstrument
|
||||||
loopLen = (loopLen << 16) + loopLenFine;
|
loopLen = (loopLen << 16) + loopLenFine;
|
||||||
|
|
||||||
const double loopScale = static_cast<double>(mptSmp.nLength) / (100 << 16);
|
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));
|
loopLen = std::min(mptSmp.nLength - loopStart, mpt::saturate_cast<SmpLength>(loopLen * loopScale));
|
||||||
} else if(mptSmp.HasSampleData())
|
} else if(mptSmp.HasSampleData())
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,7 @@ struct UltFileHeader
|
||||||
{
|
{
|
||||||
char signature[14]; // "MAS_UTrack_V00"
|
char signature[14]; // "MAS_UTrack_V00"
|
||||||
uint8 version; // '1'...'4'
|
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
|
uint8 messageLength; // Number of Lines
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ struct UltSample
|
||||||
mptSmp.Initialize();
|
mptSmp.Initialize();
|
||||||
mptSmp.Set16BitCuePoints();
|
mptSmp.Set16BitCuePoints();
|
||||||
|
|
||||||
mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename);
|
mptSmp.filename = mpt::String::ReadBuf(mpt::String::spacePadded, filename);
|
||||||
|
|
||||||
if(sizeEnd <= sizeStart)
|
if(sizeEnd <= sizeStart)
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,7 @@ struct UltSample
|
||||||
mptSmp.nSustainEnd = std::min(static_cast<SmpLength>(loopEnd), mptSmp.nLength);
|
mptSmp.nSustainEnd = std::min(static_cast<SmpLength>(loopEnd), mptSmp.nLength);
|
||||||
mptSmp.nVolume = volume;
|
mptSmp.nVolume = volume;
|
||||||
|
|
||||||
mptSmp.nC5Speed = speed;
|
mptSmp.nC5Speed = speed * 2; // Doubled to fit the note range
|
||||||
if(finetune)
|
if(finetune)
|
||||||
{
|
{
|
||||||
mptSmp.Transpose(finetune / (12.0 * 32768.0));
|
mptSmp.Transpose(finetune / (12.0 * 32768.0));
|
||||||
|
@ -207,7 +207,7 @@ static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
|
||||||
b = file.ReadUint8();
|
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>();
|
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);
|
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")};
|
const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")};
|
||||||
m_modFormat.formatName = U_("UltraTracker");
|
m_modFormat.formatName = U_("UltraTracker");
|
||||||
|
@ -419,7 +419,7 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleHeader.ConvertToMPT(Samples[smp]);
|
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);
|
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++)
|
for(PATTERNINDEX pat = 0; pat < fileHeader.patterns; pat++)
|
||||||
{
|
{
|
||||||
FileReader::off_t curPos = file.GetPosition();
|
FileReader::off_t curPos = file.GetPosition();
|
||||||
uint32 headerSize = file.ReadUint32LE();
|
const uint32 headerSize = file.ReadUint32LE();
|
||||||
file.Skip(1); // Pack method (= 0)
|
if(headerSize < 8 || !file.CanRead(headerSize - 4))
|
||||||
|
break;
|
||||||
ROWINDEX numRows = 64;
|
file.Skip(1); // Pack method (= 0)
|
||||||
|
|
||||||
|
ROWINDEX numRows;
|
||||||
if(fileHeader.version == 0x0102)
|
if(fileHeader.version == 0x0102)
|
||||||
{
|
|
||||||
numRows = file.ReadUint8() + 1;
|
numRows = file.ReadUint8() + 1;
|
||||||
} else
|
else
|
||||||
{
|
|
||||||
numRows = file.ReadUint16LE();
|
numRows = file.ReadUint16LE();
|
||||||
}
|
|
||||||
|
|
||||||
// A packed size of 0 indicates a completely empty pattern.
|
// A packed size of 0 indicates a completely empty pattern.
|
||||||
const uint16 packedSize = file.ReadUint16LE();
|
const uint16 packedSize = file.ReadUint16LE();
|
||||||
|
@ -268,10 +266,8 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo
|
||||||
file.Seek(curPos + headerSize);
|
file.Seek(curPos + headerSize);
|
||||||
FileReader patternChunk = file.ReadChunk(packedSize);
|
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;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
enum PatternFlags
|
enum PatternFlags
|
||||||
{
|
{
|
||||||
|
@ -287,6 +283,9 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo
|
||||||
|
|
||||||
for(auto &m : sndFile.Patterns[pat])
|
for(auto &m : sndFile.Patterns[pat])
|
||||||
{
|
{
|
||||||
|
if(!file.CanRead(1))
|
||||||
|
break;
|
||||||
|
|
||||||
uint8 info = patternChunk.ReadUint8();
|
uint8 info = patternChunk.ReadUint8();
|
||||||
|
|
||||||
uint8 vol = 0, command = 0;
|
uint8 vol = 0, command = 0;
|
||||||
|
@ -359,19 +358,20 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo
|
||||||
|
|
||||||
enum TrackerVersions
|
enum TrackerVersions
|
||||||
{
|
{
|
||||||
verUnknown = 0x00, // Probably not made with MPT
|
verUnknown = 0x00, // Probably not made with MPT
|
||||||
verOldModPlug = 0x01, // Made with MPT Alpha / Beta
|
verOldModPlug = 0x01, // Made with MPT Alpha / Beta
|
||||||
verNewModPlug = 0x02, // Made with MPT (not Alpha / Beta)
|
verNewModPlug = 0x02, // Made with MPT (not Alpha / Beta)
|
||||||
verModPlug1_09 = 0x04, // Made with MPT 1.09 or possibly other version
|
verModPlugBidiFlag = 0x04, // MPT up to v1.11 sets both normal loop and pingpong loop flags
|
||||||
verOpenMPT = 0x08, // Made with OpenMPT
|
verOpenMPT = 0x08, // Made with OpenMPT
|
||||||
verConfirmed = 0x10, // We are very sure that we found the correct tracker version.
|
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
|
verFT2Generic = 0x20, // "FastTracker v2.00", but FastTracker has NOT been ruled out
|
||||||
verOther = 0x40, // Something we don't know, testing for DigiTrakker.
|
verOther = 0x40, // Something we don't know, testing for DigiTrakker.
|
||||||
verFT2Clone = 0x80, // NOT FT2: itype changed between instruments, or \0 found in song title
|
verFT2Clone = 0x80, // NOT FT2: itype changed between instruments, or \0 found in song title
|
||||||
verDigiTrakker = 0x100, // Probably DigiTrakker
|
verPlayerPRO = 0x100, // Could be PlayerPRO
|
||||||
verUNMO3 = 0x200, // TODO: UNMO3-ed XMs are detected as MPT 1.16
|
verDigiTrakker = 0x200, // Probably DigiTrakker
|
||||||
verEmptyOrders = 0x400, // Allow empty order list like in OpenMPT (FT2 just plays pattern 0 if the order list is empty according to the header)
|
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)
|
DECLARE_FLAGSET(TrackerVersions)
|
||||||
|
|
||||||
|
@ -480,6 +480,13 @@ static bool ReadSampleData(ModSample &sample, SampleIO sampleFlags, FileReader &
|
||||||
{
|
{
|
||||||
decodedSamples = ret;
|
decodedSamples = ret;
|
||||||
LimitMax(decodedSamples, mpt::saturate_cast<long>(sample.nLength - offset));
|
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(decodedSamples > 0 && channels == sample.GetNumChannels())
|
||||||
{
|
{
|
||||||
if(sample.uFlags[CHN_16BIT])
|
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)
|
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)
|
if(fileHeader.version < 0x0104)
|
||||||
|
{
|
||||||
madeWith = verFT2Generic | verConfirmed;
|
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
|
// FT2 pads the song title with spaces, some other trackers use null chars
|
||||||
madeWith = verFT2Clone | verNewModPlug | verEmptyOrders;
|
// PlayerPRO filles the remaining buffer after the null terminator with space characters.
|
||||||
else
|
// PlayerPRO does not support song restart position.
|
||||||
madeWith = verFT2Generic | verNewModPlug;
|
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))
|
} else if(!memcmp(fileHeader.trackerName, "FastTracker v 2.00 ", 20))
|
||||||
{
|
{
|
||||||
// MPT 1.0 (exact version to be determined later)
|
// 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
|
// Fix arpeggios in kragle_-_happy_day.xm
|
||||||
m_playBehaviour.reset(kFT2Arpeggio);
|
m_playBehaviour.reset(kFT2Arpeggio);
|
||||||
isMadTracker = true;
|
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))
|
} else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14) || !memcmp(fileHeader.trackerName, "Sk@le Tracker\0", 14))
|
||||||
{
|
{
|
||||||
m_playBehaviour.reset(kFT2ST3OffsetOutOfRange);
|
m_playBehaviour.reset(kFT2ST3OffsetOutOfRange);
|
||||||
|
@ -673,10 +701,8 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_SongFlags.reset();
|
m_SongFlags.reset();
|
||||||
m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & XMFileHeader::linearSlides) != 0);
|
m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & XMFileHeader::linearSlides) != 0);
|
||||||
m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & XMFileHeader::extendedFilterRange) != 0);
|
m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & XMFileHeader::extendedFilterRange) != 0);
|
||||||
if(m_SongFlags[SONG_EXFILTERRANGE] && madeWith == (verFT2Generic | verNewModPlug))
|
if(m_SongFlags[SONG_EXFILTERRANGE] && madeWith[verNewModPlug])
|
||||||
{
|
madeWith = verFT2Clone | verNewModPlug | verConfirmed | verEmptyOrders;
|
||||||
madeWith = verFT2Clone | verNewModPlug | verConfirmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadOrderFromFile<uint8>(Order(), file, fileHeader.orders);
|
ReadOrderFromFile<uint8>(Order(), file, fileHeader.orders);
|
||||||
if(fileHeader.orders == 0 && !madeWith[verEmptyOrders])
|
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.
|
// 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;
|
std::vector<SampleIO> sampleFlags;
|
||||||
uint8 sampleReserved = 0;
|
uint8 sampleReserved = 0;
|
||||||
int instrType = -1;
|
int16 lastInstrType = -1, lastSampleReserved = -1;
|
||||||
|
int64 lastSampleHeaderSize = -1;
|
||||||
bool unsupportedSamples = false;
|
bool unsupportedSamples = false;
|
||||||
bool anyADPCM = false;
|
bool anyADPCM = false;
|
||||||
|
bool instrumentWithSamplesEncountered = false;
|
||||||
|
|
||||||
// Reading instruments
|
// Reading instruments
|
||||||
for(INSTRUMENTINDEX instr = 1; instr <= m_nInstruments; instr++)
|
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...
|
// First, try to read instrument header length...
|
||||||
uint32 headerSize = file.ReadUint32LE();
|
uint32 headerSize = file.ReadUint32LE();
|
||||||
if(headerSize == 0)
|
if(headerSize == 0)
|
||||||
|
@ -744,38 +777,61 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
else if(madeWith[verFT2Clone | verFT2Generic] && instrHeader.size != 33)
|
else if(madeWith[verFT2Clone | verFT2Generic] && instrHeader.size != 33)
|
||||||
{
|
{
|
||||||
// Sure isn't FT2.
|
// Sure isn't FT2.
|
||||||
// Note: FT2 NORMALLY writes shdr=40 for all samples, but sometimes it
|
// 4-mat's eternity.xm has an empty instruments with a header size of 29.
|
||||||
// just happens to write random garbage there instead. Surprise!
|
// Another module using that size is funky_dumbass.xm. Mysterious!
|
||||||
// Note: 4-mat's eternity.xm has an instrument header size of 29.
|
// 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;
|
madeWith = verUnknown;
|
||||||
}
|
}
|
||||||
}
|
if(instrHeader.size != 33)
|
||||||
|
{
|
||||||
if(AllocateInstrument(instr) == nullptr)
|
madeWith.reset(verPlayerPRO);
|
||||||
{
|
} else if(instrHeader.sampleHeaderSize > sizeof(XMSample) && madeWith[verPlayerPRO])
|
||||||
continue;
|
{
|
||||||
|
// 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]);
|
instrHeader.ConvertToMPT(*Instruments[instr]);
|
||||||
|
|
||||||
if(instrType == -1)
|
if(lastInstrType == -1)
|
||||||
{
|
{
|
||||||
instrType = instrHeader.type;
|
lastInstrType = instrHeader.type;
|
||||||
} else if(instrType != instrHeader.type && madeWith[verFT2Generic])
|
} else if(lastInstrType != instrHeader.type && madeWith[verFT2Generic])
|
||||||
{
|
{
|
||||||
// FT2 writes some random junk for the instrument type field,
|
// FT2 writes some random junk for the instrument type field,
|
||||||
// but it's always the SAME junk for every instrument saved.
|
// 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.reset(verFT2Generic);
|
||||||
madeWith.set(verFT2Clone);
|
madeWith.set(verFT2Clone);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(instrHeader.numSamples > 0)
|
if(instrHeader.numSamples > 0)
|
||||||
{
|
{
|
||||||
|
instrumentWithSamplesEncountered = true;
|
||||||
|
|
||||||
// Yep, there are some samples associated with this instrument.
|
// 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)
|
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.
|
// Note: Earlier (?) PlayerPRO versions do not seem to set the loop points to 0xFF (george_megas_-_q.xm)
|
||||||
madeWith.reset(verOldModPlug | verNewModPlug);
|
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
|
// Read sample headers
|
||||||
|
@ -810,6 +866,18 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
sampleSize[sample] = sampleHeader.length;
|
sampleSize[sample] = sampleHeader.length;
|
||||||
sampleReserved |= sampleHeader.reserved;
|
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())
|
if(sample < sampleSlots.size())
|
||||||
{
|
{
|
||||||
SAMPLEINDEX mptSample = sampleSlots[sample];
|
SAMPLEINDEX mptSample = sampleSlots[sample];
|
||||||
|
@ -818,12 +886,16 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
instrHeader.instrument.ApplyAutoVibratoToMPT(Samples[mptSample]);
|
instrHeader.instrument.ApplyAutoVibratoToMPT(Samples[mptSample]);
|
||||||
|
|
||||||
m_szNames[mptSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
|
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])
|
if((sampleHeader.flags & 3) == 3 && madeWith[verNewModPlug])
|
||||||
{
|
madeWith.set(verModPlugBidiFlag);
|
||||||
// MPT 1.09 and maybe newer / older versions set both loop flags for bidi loops.
|
|
||||||
madeWith.set(verModPlug1_09);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(sampleFlags.back().GetEncoding() == SampleIO::ADPCM)
|
if(sampleFlags.back().GetEncoding() == SampleIO::ADPCM)
|
||||||
anyADPCM = true;
|
anyADPCM = true;
|
||||||
|
@ -882,6 +954,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
{
|
{
|
||||||
m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR);
|
m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR);
|
||||||
madeWith.set(verConfirmed);
|
madeWith.set(verConfirmed);
|
||||||
|
madeWith.reset(verPlayerPRO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read midi config: "MIDI"
|
// Read midi config: "MIDI"
|
||||||
|
@ -892,6 +965,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
m_MidiCfg.Sanitize();
|
m_MidiCfg.Sanitize();
|
||||||
hasMidiConfig = true;
|
hasMidiConfig = true;
|
||||||
madeWith.set(verConfirmed);
|
madeWith.set(verConfirmed);
|
||||||
|
madeWith.reset(verPlayerPRO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read pattern names: "PNAM"
|
// Read pattern names: "PNAM"
|
||||||
|
@ -906,6 +980,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
Patterns[pat].SetName(patName);
|
Patterns[pat].SetName(patName);
|
||||||
}
|
}
|
||||||
madeWith.set(verConfirmed);
|
madeWith.set(verConfirmed);
|
||||||
|
madeWith.reset(verPlayerPRO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read channel names: "CNAM"
|
// 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);
|
file.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[chn].szName, MAX_CHANNELNAME);
|
||||||
}
|
}
|
||||||
madeWith.set(verConfirmed);
|
madeWith.set(verConfirmed);
|
||||||
|
madeWith.reset(verPlayerPRO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read mix plugins information
|
// Read mix plugins information
|
||||||
|
@ -927,19 +1003,27 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
if(file.GetPosition() != oldPos)
|
if(file.GetPosition() != oldPos)
|
||||||
{
|
{
|
||||||
madeWith.set(verConfirmed);
|
madeWith.set(verConfirmed);
|
||||||
|
madeWith.reset(verPlayerPRO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(madeWith[verConfirmed])
|
if(madeWith[verConfirmed])
|
||||||
{
|
{
|
||||||
if(madeWith[verModPlug1_09])
|
if(madeWith[verModPlugBidiFlag])
|
||||||
{
|
{
|
||||||
m_dwLastSavedWithVersion = MPT_V("1.09.00.00");
|
m_dwLastSavedWithVersion = MPT_V("1.11");
|
||||||
madeWithTracker = U_("ModPlug Tracker 1.09");
|
madeWithTracker = U_("ModPlug Tracker 1.0 - 1.11");
|
||||||
} else if(madeWith[verNewModPlug])
|
} else if(madeWith[verNewModPlug] && !madeWith[verPlayerPRO])
|
||||||
{
|
{
|
||||||
m_dwLastSavedWithVersion = MPT_V("1.16.00.00");
|
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(madeWithTracker.empty())
|
||||||
{
|
{
|
||||||
if(madeWith[verDigiTrakker] && sampleReserved == 0 && (instrType ? instrType : -1) == -1)
|
if(madeWith[verDigiTrakker] && sampleReserved == 0 && (lastInstrType ? lastInstrType : -1) == -1)
|
||||||
{
|
{
|
||||||
madeWithTracker = U_("DigiTrakker");
|
madeWithTracker = U_("DigiTrakker");
|
||||||
} else if(madeWith[verFT2Generic])
|
} else if(madeWith[verFT2Generic])
|
||||||
|
@ -1051,6 +1135,17 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags)
|
||||||
#ifndef MODPLUG_NO_FILESAVE
|
#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)
|
bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1212,7 +1307,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport)
|
||||||
if(!p->IsEmpty())
|
if(!p->IsEmpty())
|
||||||
emptyPattern = false;
|
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.
|
// We have to avoid this, so we add a "break to row 0" command in the last row.
|
||||||
if(j == 1 && emptyPattern && numRows != 64)
|
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
|
} else
|
||||||
{
|
{
|
||||||
MemsetZero(insHeader);
|
MemsetZero(insHeader);
|
||||||
|
|
|
@ -50,6 +50,8 @@ struct XMFSampleHeader
|
||||||
return false;
|
return false;
|
||||||
if((flags & smpEnableLoop) && !loopEnd.get())
|
if((flags & smpEnableLoop) && !loopEnd.get())
|
||||||
return false;
|
return false;
|
||||||
|
if(loopStart.get() > loopEnd.get() || loopStart.get() > length)
|
||||||
|
return false;
|
||||||
if(loopEnd.get() != 0 && (loopEnd.get() >= length || loopStart.get() >= loopEnd.get()))
|
if(loopEnd.get() != 0 && (loopEnd.get() >= length || loopStart.get() >= loopEnd.get()))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -91,6 +91,8 @@ struct MIDIMacroConfigData
|
||||||
struct Macro
|
struct Macro
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Macro() = default;
|
||||||
|
Macro(const Macro &other) = default;
|
||||||
Macro &operator=(const Macro &other) = default;
|
Macro &operator=(const Macro &other) = default;
|
||||||
Macro &operator=(const std::string_view other) noexcept
|
Macro &operator=(const std::string_view other) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
@ -79,7 +79,7 @@ struct NoInterpolation
|
||||||
// FilterFunc: Functor for applying the resonant filter
|
// FilterFunc: Functor for applying the resonant filter
|
||||||
// MixFunc: Functor for mixing the computed sample data into the output buffer
|
// MixFunc: Functor for mixing the computed sample data into the output buffer
|
||||||
template<class Traits, class InterpolationFunc, class FilterFunc, class MixFunc>
|
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;
|
ModChannel &c = chn;
|
||||||
const typename Traits::input_t * MPT_RESTRICT inSample = static_cast<const typename Traits::input_t *>(c.pCurrentSample);
|
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(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;
|
return nArpeggioLastNote;
|
||||||
}
|
}
|
||||||
ModCommand::NOTE plugNote = mpt::saturate_cast<ModCommand::NOTE>(nNote - nTranspose);
|
ModCommand::NOTE plugNote = nLastNote;
|
||||||
// Caution: When in compatible mode, ModChannel::nNote stores the "real" note, not the mapped note!
|
if(pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN))
|
||||||
if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN))
|
|
||||||
{
|
{
|
||||||
plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN];
|
plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN];
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ struct ModChannel
|
||||||
|
|
||||||
uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; }
|
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.
|
// 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(); }
|
bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); }
|
||||||
|
@ -208,6 +208,10 @@ struct ModChannel
|
||||||
void InstrumentControl(uint8 param, const CSoundFile &sndFile);
|
void InstrumentControl(uint8 param, const CSoundFile &sndFile);
|
||||||
|
|
||||||
int32 GetMIDIPitchBend() const noexcept { return (static_cast<int32>(microTuning) + 0x8000) / 4; }
|
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.
|
// Translate sample properties between two given formats.
|
||||||
void ModSample::Convert(MODTYPE fromType, MODTYPE toType)
|
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.
|
// Convert between frequency and transpose values if necessary.
|
||||||
if((!(toType & (MOD_TYPE_MOD | MOD_TYPE_XM))) && (fromType & (MOD_TYPE_MOD | MOD_TYPE_XM)))
|
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;
|
nPan = 128;
|
||||||
nVolume = 256;
|
nVolume = 256;
|
||||||
nGlobalVol = 64;
|
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)
|
if(type == MOD_TYPE_XM)
|
||||||
{
|
{
|
||||||
uFlags.set(CHN_PANNING);
|
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
|
void ModSequence::Remove(ORDERINDEX posBegin, ORDERINDEX posEnd) noexcept
|
||||||
{
|
{
|
||||||
if(posEnd < posBegin || posEnd >= size())
|
if(posEnd < posBegin || posEnd >= size())
|
||||||
|
@ -484,6 +496,7 @@ bool ModSequenceSet::MergeSequences()
|
||||||
firstSeq.reserve(firstOrder + lengthTrimmed);
|
firstSeq.reserve(firstOrder + lengthTrimmed);
|
||||||
firstSeq.push_back(); // Separator item
|
firstSeq.push_back(); // Separator item
|
||||||
RestartPosToPattern(seqNum);
|
RestartPosToPattern(seqNum);
|
||||||
|
patternsFixed.resize(m_sndFile.Patterns.Size(), SEQUENCEINDEX_INVALID); // Previous line might have increased pattern count
|
||||||
for(ORDERINDEX ord = 0; ord < lengthTrimmed; ord++)
|
for(ORDERINDEX ord = 0; ord < lengthTrimmed; ord++)
|
||||||
{
|
{
|
||||||
PATTERNINDEX pat = seq[ord];
|
PATTERNINDEX pat = seq[ord];
|
||||||
|
@ -522,6 +535,7 @@ bool ModSequenceSet::MergeSequences()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_Sequences.erase(m_Sequences.begin() + 1, m_Sequences.end());
|
m_Sequences.erase(m_Sequences.begin() + 1, m_Sequences.end());
|
||||||
|
m_currentSeq = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,9 @@ public:
|
||||||
ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const noexcept;
|
ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const noexcept;
|
||||||
ORDERINDEX GetNextOrderIgnoringSkips(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.
|
// Find an order item that contains a given pattern number.
|
||||||
ORDERINDEX FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt = 0, bool searchForward = true) const noexcept;
|
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
|
// Caching gets triggered via a global object that primes the cache during
|
||||||
// construction.
|
// construction.
|
||||||
// This is only really useful with MPT_RESAMPLER_TABLES_CACHED.
|
// 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
|
#endif // LIBOPENMPT_BUILD
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ OPENMPT_NAMESPACE_BEGIN
|
||||||
|
|
||||||
RowVisitor::LoopState::LoopState(const ChannelStates &chnState, const bool ignoreRow)
|
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.
|
// 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,
|
// 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.
|
// 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();
|
auto &order = Order();
|
||||||
const ORDERINDEX endOrder = order.GetLengthTailTrimmed();
|
const ORDERINDEX endOrder = order.GetLengthTailTrimmed();
|
||||||
|
bool reserveLoopStates = true;
|
||||||
m_visitedRows.resize(endOrder);
|
m_visitedRows.resize(endOrder);
|
||||||
if(reset)
|
if(reset)
|
||||||
{
|
{
|
||||||
m_visitedLoopStates.clear();
|
reserveLoopStates = m_visitedLoopStates.empty();
|
||||||
|
for(auto &loopState : m_visitedLoopStates)
|
||||||
|
{
|
||||||
|
loopState.second.clear();
|
||||||
|
}
|
||||||
m_rowsSpentInLoops = 0;
|
m_rowsSpentInLoops = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +109,7 @@ void RowVisitor::Initialize(bool reset)
|
||||||
else
|
else
|
||||||
visitedRows.resize(numRows, false);
|
visitedRows.resize(numRows, false);
|
||||||
|
|
||||||
if(!order.IsValidPat(ord))
|
if(!reserveLoopStates || !order.IsValidPat(ord))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ROWINDEX startRow = std::min(static_cast<ROWINDEX>(reset ? 0 : visitedRows.size()), numRows);
|
const ROWINDEX startRow = std::min(static_cast<ROWINDEX>(reset ? 0 : visitedRows.size()), numRows);
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "openmpt/all/BuildSettings.hpp"
|
#include "openmpt/all/BuildSettings.hpp"
|
||||||
|
#include "openmpt/base/Endian.hpp"
|
||||||
#include "../soundlib/ModSample.h"
|
#include "Snd_defs.h"
|
||||||
#include "../soundlib/SampleIO.h"
|
|
||||||
|
|
||||||
|
|
||||||
OPENMPT_NAMESPACE_BEGIN
|
OPENMPT_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct ModSample;
|
||||||
|
class SampleIO;
|
||||||
|
|
||||||
// S3M File Header
|
// S3M File Header
|
||||||
struct S3MFileHeader
|
struct S3MFileHeader
|
||||||
{
|
{
|
||||||
|
@ -45,13 +46,16 @@ struct S3MFileHeader
|
||||||
|
|
||||||
trkAkord = 0x0208,
|
trkAkord = 0x0208,
|
||||||
trkST3_00 = 0x1300,
|
trkST3_00 = 0x1300,
|
||||||
trkST3_20 = 0x1320,
|
|
||||||
trkST3_01 = 0x1301,
|
trkST3_01 = 0x1301,
|
||||||
|
trkST3_20 = 0x1320,
|
||||||
|
trkIT1_old = 0x3320,
|
||||||
trkIT2_07 = 0x3207,
|
trkIT2_07 = 0x3207,
|
||||||
trkIT2_14 = 0x3214,
|
trkIT2_14 = 0x3214,
|
||||||
trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012
|
trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012
|
||||||
trkGraoumfTracker = 0x5447,
|
trkGraoumfTracker = 0x5447,
|
||||||
|
trkNESMusa = 0x5700,
|
||||||
trkCamoto = 0xCA00,
|
trkCamoto = 0xCA00,
|
||||||
|
trkPlayerPRO = 0x2013, // PlayerPRO on Intel doesn't byte-swap the tracker ID bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
|
|
|
@ -168,7 +168,7 @@ struct SFZFlexEG
|
||||||
break;
|
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.nSustainStart = env.nSustainEnd = sustain;
|
||||||
env.dwFlags.set(ENV_SUSTAIN);
|
env.dwFlags.set(ENV_SUSTAIN);
|
||||||
|
@ -307,7 +307,7 @@ struct SFZRegion
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t filenameOffset = 0;
|
size_t filenameOffset = 0;
|
||||||
std::string filename, name;
|
std::string filename, globalName, regionName;
|
||||||
SFZEnvelope ampEnv, pitchEnv, filterEnv;
|
SFZEnvelope ampEnv, pitchEnv, filterEnv;
|
||||||
std::vector<SFZFlexEG> flexEGs;
|
std::vector<SFZFlexEG> flexEGs;
|
||||||
SmpLength loopStart = 0, loopEnd = 0;
|
SmpLength loopStart = 0, loopEnd = 0;
|
||||||
|
@ -398,8 +398,10 @@ struct SFZRegion
|
||||||
filename = control.defaultPath + value;
|
filename = control.defaultPath + value;
|
||||||
filenameOffset = control.defaultPath.size();
|
filenameOffset = control.defaultPath.size();
|
||||||
}
|
}
|
||||||
|
else if(key == "global_label")
|
||||||
|
globalName = value;
|
||||||
else if(key == "region_label")
|
else if(key == "region_label")
|
||||||
name = value;
|
regionName = value;
|
||||||
else if(key == "lokey")
|
else if(key == "lokey")
|
||||||
keyLo = ReadKey(value, control);
|
keyLo = ReadKey(value, control);
|
||||||
else if(key == "hikey")
|
else if(key == "hikey")
|
||||||
|
@ -761,6 +763,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
||||||
DestroyInstrument(nInstr, deleteAssociatedSamples);
|
DestroyInstrument(nInstr, deleteAssociatedSamples);
|
||||||
if(nInstr > m_nInstruments) m_nInstruments = nInstr;
|
if(nInstr > m_nInstruments) m_nInstruments = nInstr;
|
||||||
Instruments[nInstr] = pIns;
|
Instruments[nInstr] = pIns;
|
||||||
|
pIns->name = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, globals.globalName);
|
||||||
|
|
||||||
SAMPLEINDEX prevSmp = 0;
|
SAMPLEINDEX prevSmp = 0;
|
||||||
for(auto ®ion : regions)
|
for(auto ®ion : regions)
|
||||||
|
@ -841,8 +844,8 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
|
||||||
sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData());
|
sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!region.name.empty())
|
if(!region.regionName.empty())
|
||||||
m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.name);
|
m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.regionName);
|
||||||
if(!m_szNames[smp][0])
|
if(!m_szNames[smp][0])
|
||||||
m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::PathString::FromUTF8(region.filename).GetFilenameBase().ToUnicode());
|
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);
|
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
|
bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const
|
||||||
{
|
{
|
||||||
#ifdef MODPLUG_TRACKER
|
#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);
|
const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast<double>(m_MixerSettings.gdwMixingFreq);
|
||||||
|
|
||||||
f << std::setprecision(10);
|
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 << "// Created with " << mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()) << "\n";
|
||||||
f << "// Envelope tempo base: tempo " << m_PlayState.m_nMusicTempo.ToDouble();
|
f << "// Envelope tempo base: tempo " << m_PlayState.m_nMusicTempo.ToDouble();
|
||||||
switch(m_nTempoMode)
|
switch(m_nTempoMode)
|
||||||
|
@ -1114,8 +1126,12 @@ bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
f << "\n\n<control>\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n";
|
f << "\n\n<control>\ndefault_path=" << sampleDirName.ToUTF8();
|
||||||
f << "<group>";
|
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_up=" << ins->midiPWD * 100;
|
||||||
f << "\nbend_down=" << -ins->midiPWD * 100;
|
f << "\nbend_down=" << -ins->midiPWD * 100;
|
||||||
const uint32 cutoff = ins->IsCutoffEnabled() ? ins->GetCutoff() : 127;
|
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>";
|
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 << "\nsample=" << sampleName.GetFilename().ToUTF8();
|
||||||
f << "\nlokey=" << i;
|
f << "\nlokey=" << i;
|
||||||
|
|
|
@ -9,32 +9,36 @@
|
||||||
|
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "Sndfile.h"
|
#include "ITTools.h"
|
||||||
|
#include "Loaders.h"
|
||||||
#include "mod_specifications.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
|
#ifdef MODPLUG_TRACKER
|
||||||
#include "../mptrack/Moddoc.h"
|
#include "../mptrack/Moddoc.h"
|
||||||
#include "Dlsbank.h"
|
#include "Dlsbank.h"
|
||||||
#endif // MODPLUG_TRACKER
|
#endif // MODPLUG_TRACKER
|
||||||
#include "../soundlib/AudioCriticalSection.h"
|
|
||||||
#include "mpt/format/join.hpp"
|
|
||||||
#ifndef MODPLUG_NO_FILESAVE
|
#ifndef MODPLUG_NO_FILESAVE
|
||||||
#include "mpt/io/base.hpp"
|
#include "mpt/io/base.hpp"
|
||||||
#include "mpt/io/io.hpp"
|
#include "mpt/io/io.hpp"
|
||||||
#include "mpt/io/io_stdstream.hpp"
|
#include "mpt/io/io_stdstream.hpp"
|
||||||
#include "../common/mptFileIO.h"
|
#include "../common/mptFileIO.h"
|
||||||
#endif // !MODPLUG_NO_FILESAVE
|
#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 <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
@ -276,6 +280,15 @@ bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoun
|
||||||
#endif
|
#endif
|
||||||
pIns->Convert(srcSong.GetType(), GetType());
|
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
|
// Copy all referenced samples over
|
||||||
for(size_t i = 0; i < targetSample.size(); i++)
|
for(size_t i = 0; i < targetSample.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -2519,7 +2532,7 @@ struct IFFChunk
|
||||||
|
|
||||||
size_t GetLength() const
|
size_t GetLength() const
|
||||||
{
|
{
|
||||||
if(length == 0) // Broken files
|
if(length == 0 && id == idBODY) // Broken files
|
||||||
return std::numeric_limits<size_t>::max();
|
return std::numeric_limits<size_t>::max();
|
||||||
return length;
|
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)
|
static uint32 WriteIFFStringChunk(std::ostream &f, IFFChunk::ChunkIdentifiers id, const std::string &str)
|
||||||
{
|
{
|
||||||
|
if(str.empty())
|
||||||
|
return 0;
|
||||||
IFFChunk chunk{};
|
IFFChunk chunk{};
|
||||||
chunk.id = id;
|
chunk.id = id;
|
||||||
chunk.length = static_cast<uint32>(str.size());
|
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));
|
restrictedSampleDataView = file.GetPinnedView(CalculateEncodedSize(sample.nLength));
|
||||||
sourceBuf = restrictedSampleDataView.data();
|
sourceBuf = restrictedSampleDataView.data();
|
||||||
fileSize = restrictedSampleDataView.size();
|
fileSize = restrictedSampleDataView.size();
|
||||||
if(sourceBuf == nullptr)
|
if(fileSize < 1)
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
} 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
|
// Stereo split / mono signed 16-bit, big-endian
|
||||||
MPT_ASSERT(len == numSamples * 4);
|
const uint8 numChannels = GetNumChannels();
|
||||||
for(uint8 chn = 0; chn < 2; chn++)
|
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;
|
const int16 *p = sample.sample16() + chn;
|
||||||
for(SmpLength j = 0; j < numSamples; j++)
|
for(SmpLength j = 0; j < numSamples; j++)
|
||||||
{
|
{
|
||||||
mpt::IO::Write(fb, mpt::as_be(*p));
|
mpt::IO::Write(fb, mpt::as_be(static_cast<int16>(static_cast<uint16>(*p) + offset)));
|
||||||
p += 2;
|
p += numChannels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -552,6 +552,7 @@ enum PlayBehaviour
|
||||||
kST3TonePortaWithAdlibNote, // Adlib note next to tone portamento is delayed until next row
|
kST3TonePortaWithAdlibNote, // Adlib note next to tone portamento is delayed until next row
|
||||||
kITResetFilterOnPortaSmpChange, // Filter is reset on portamento if sample is swapped
|
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"
|
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.
|
// Add new play behaviours here.
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,7 @@ public:
|
||||||
|
|
||||||
if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative()))
|
if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative()))
|
||||||
{
|
{
|
||||||
if(!chn.dwFlags[CHN_LOOP])
|
if(!chn.dwFlags[CHN_LOOP] || !loopLength)
|
||||||
{
|
{
|
||||||
// Past sample end.
|
// Past sample end.
|
||||||
stopNote = true;
|
stopNote = true;
|
||||||
|
@ -326,7 +326,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++)
|
for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++)
|
||||||
{
|
{
|
||||||
if(m->note == NOTE_NOTECUT || m->note == NOTE_KEYOFF || (m->note == NOTE_FADE && GetNumInstruments())
|
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;
|
memory.chnSettings[i].ticksToRender = GetLengthMemory::IGNORE_CHANNEL;
|
||||||
}
|
}
|
||||||
|
@ -979,7 +979,7 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_playBehaviour[kST3EffectMemory] && param != 0)
|
if(m_playBehaviour[kST3EffectMemory] && command != CMD_NONE && param != 0)
|
||||||
{
|
{
|
||||||
UpdateS3MEffectMemory(chn, param);
|
UpdateS3MEffectMemory(chn, param);
|
||||||
}
|
}
|
||||||
|
@ -1064,7 +1064,8 @@ std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMod
|
||||||
{
|
{
|
||||||
if(m.command == CMD_OFFSET)
|
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)
|
} else if(m.command == CMD_OFFSETPERCENTAGE)
|
||||||
{
|
{
|
||||||
SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 256));
|
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
|
void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bool bUpdVol, bool bResetEnv) const
|
||||||
{
|
{
|
||||||
const ModInstrument *pIns = instr <= GetNumInstruments() ? Instruments[instr] : nullptr;
|
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;
|
const auto oldInsVol = chn.nInsVol;
|
||||||
ModCommand::NOTE note = chn.nNewNote;
|
ModCommand::NOTE note = chn.nNewNote;
|
||||||
|
|
||||||
|
@ -2707,8 +2708,8 @@ bool CSoundFile::ProcessEffects()
|
||||||
if(m_playBehaviour[kMODSampleSwap])
|
if(m_playBehaviour[kMODSampleSwap])
|
||||||
{
|
{
|
||||||
// ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it
|
// ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it
|
||||||
// Test case: PTSwapEmpty.mod, PTInstrVolume.mod, SampleSwap.s3m
|
// Test cases: PTSwapEmpty.mod, PTInstrVolume.mod, PTStoppedSwap.mod
|
||||||
if(!chn.IsSamplePlaying() && (chn.pModSample == nullptr || !chn.pModSample->HasSampleData()))
|
if(!chn.IsSamplePlaying() && instr <= GetNumSamples() && Samples[instr].uFlags[CHN_LOOP])
|
||||||
keepInstr = true;
|
keepInstr = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2951,7 +2952,8 @@ bool CSoundFile::ProcessEffects()
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteChange(chn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)), false, nChn);
|
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))
|
if ((bPorta) && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr))
|
||||||
{
|
{
|
||||||
chn.dwFlags.set(CHN_FASTVOLRAMP);
|
chn.dwFlags.set(CHN_FASTVOLRAMP);
|
||||||
|
@ -3229,7 +3231,7 @@ bool CSoundFile::ProcessEffects()
|
||||||
{
|
{
|
||||||
// FT2 compatibility: Portamento + Offset = Ignore offset
|
// FT2 compatibility: Portamento + Offset = Ignore offset
|
||||||
// Test case: porta-offset.xm
|
// Test case: porta-offset.xm
|
||||||
if(bPorta && GetType() == MOD_TYPE_XM)
|
if(bPorta && (GetType() & (MOD_TYPE_XM | MOD_TYPE_DBM)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ProcessSampleOffset(chn, nChn, m_PlayState);
|
ProcessSampleOffset(chn, nChn, m_PlayState);
|
||||||
|
@ -3553,7 +3555,7 @@ bool CSoundFile::ProcessEffects()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_playBehaviour[kST3EffectMemory] && param != 0)
|
if(m_playBehaviour[kST3EffectMemory] && cmd != CMD_NONE && param != 0)
|
||||||
{
|
{
|
||||||
UpdateS3MEffectMemory(chn, static_cast<ModCommand::PARAM>(param));
|
UpdateS3MEffectMemory(chn, static_cast<ModCommand::PARAM>(param));
|
||||||
}
|
}
|
||||||
|
@ -4156,7 +4158,7 @@ void CSoundFile::TonePortamento(CHANNELINDEX nChn, uint16 param)
|
||||||
IMixPlugin *plugin = GetChannelInstrumentPlugin(chn);
|
IMixPlugin *plugin = GetChannelInstrumentPlugin(chn);
|
||||||
if(plugin != nullptr)
|
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
|
#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)
|
// SysEx Checksum (not an original Impulse Tracker macro variable, but added for convenience)
|
||||||
auto startPos = outPos;
|
auto startPos = outPos;
|
||||||
while(startPos > 0 && out[--startPos] != 0xF0);
|
while(startPos > 0 && out[--startPos] != 0xF0)
|
||||||
if(outPos - startPos < 5 || out[startPos] != 0xF0)
|
;
|
||||||
{
|
|
||||||
|
if(outPos - startPos < 3 || out[startPos] != 0xF0)
|
||||||
continue;
|
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];
|
data += out[p];
|
||||||
}
|
}
|
||||||
|
@ -6147,10 +6154,16 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe
|
||||||
note -= NOTE_MIN;
|
note -= NOTE_MIN;
|
||||||
if(!UseFinetuneAndTranspose())
|
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.
|
// 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);
|
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)
|
if(!nC5Speed)
|
||||||
nC5Speed = 8363;
|
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).
|
// 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;
|
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);
|
LimitMax(period, Util::MaxValueOfType(period) >> 8);
|
||||||
if (!c5speed) c5speed = 8363;
|
if (!c5speed) c5speed = 8363;
|
||||||
return Util::muldiv_unsigned(c5speed, (1712L << 7) << FREQ_FRACBITS, (period << 8) + nPeriodFrac);
|
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.
|
// Input is already a frequency in Hertz, not a period.
|
||||||
static_assert(FREQ_FRACBITS <= 8, "Check this shift operator");
|
static_assert(FREQ_FRACBITS <= 8, "Check this shift operator");
|
||||||
return uint32(((uint64(period) << 8) + nPeriodFrac) >> (8 - FREQ_FRACBITS));
|
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)
|
if(!c5speed)
|
||||||
c5speed = 8363;
|
c5speed = 8363;
|
||||||
|
|
|
@ -699,13 +699,13 @@ public:
|
||||||
|
|
||||||
enum ModLoadingFlags
|
enum ModLoadingFlags
|
||||||
{
|
{
|
||||||
onlyVerifyHeader = 0x00,
|
|
||||||
loadPatternData = 0x01, // If unset, advise loaders to not process any pattern data (if possible)
|
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)
|
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).
|
loadPluginData = 0x04, // If unset, plugin data is not loaded (and as a consequence, plugins are not instantiated).
|
||||||
loadPluginInstance = 0x08, // If unset, plugins are not instanciated.
|
loadPluginInstance = 0x08, // If unset, plugins are not instantiated.
|
||||||
skipContainer = 0x10,
|
skipContainer = 0x10,
|
||||||
skipModules = 0x20,
|
skipModules = 0x20,
|
||||||
|
onlyVerifyHeader = 0x40, // Do not combine with other flags!
|
||||||
|
|
||||||
// Shortcuts
|
// Shortcuts
|
||||||
loadCompleteModule = loadSampleData | loadPatternData | loadPluginData | loadPluginInstance,
|
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()].
|
// 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;
|
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);
|
size_t ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers);
|
||||||
std::pair<bool, bool> LoadMixPlugins(FileReader &file);
|
std::pair<bool, bool> LoadMixPlugins(FileReader &file);
|
||||||
#ifndef NO_PLUGINS
|
#ifndef NO_PLUGINS
|
||||||
|
|
|
@ -200,6 +200,7 @@ CSoundFile::samplecount_t CSoundFile::ReadOneTick()
|
||||||
{
|
{
|
||||||
const auto origMaxMixChannels = m_MixerSettings.m_nMaxMixChannels;
|
const auto origMaxMixChannels = m_MixerSettings.m_nMaxMixChannels;
|
||||||
m_MixerSettings.m_nMaxMixChannels = 0;
|
m_MixerSettings.m_nMaxMixChannels = 0;
|
||||||
|
ResetMixStat();
|
||||||
while(m_PlayState.m_nBufferCount)
|
while(m_PlayState.m_nBufferCount)
|
||||||
{
|
{
|
||||||
auto framesToRender = std::min(m_PlayState.m_nBufferCount, samplecount_t(MIXBUFFERSIZE));
|
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.
|
// 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.
|
// Hence, we have to translate our "sample rate" into pitch.
|
||||||
auto milliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS);
|
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);
|
const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0);
|
||||||
if(!m_playBehaviour[kOPLNoteStopWith0Hz] || !keyOff)
|
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
|
#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)
|
// 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)
|
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 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.
|
// 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
|
// Currently active program and bank is assumed to be 1 when starting playback
|
||||||
for(auto &plugin : m_MixPlugins)
|
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;
|
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
|
// Fix off-by-one delay length in older Echo DMO emulation
|
||||||
for(auto &plugin : m_MixPlugins)
|
for(auto &plugin : m_MixPlugins)
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "openmpt/all/BuildSettings.hpp"
|
#include "openmpt/all/BuildSettings.hpp"
|
||||||
|
#include "openmpt/base/Endian.hpp"
|
||||||
|
#include "Snd_defs.h"
|
||||||
|
|
||||||
|
|
||||||
OPENMPT_NAMESPACE_BEGIN
|
OPENMPT_NAMESPACE_BEGIN
|
||||||
|
@ -21,24 +23,24 @@ struct XMFileHeader
|
||||||
{
|
{
|
||||||
enum XMHeaderFlags
|
enum XMHeaderFlags
|
||||||
{
|
{
|
||||||
linearSlides = 0x01,
|
linearSlides = 0x01,
|
||||||
extendedFilterRange = 0x1000,
|
extendedFilterRange = 0x1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
char signature[17]; // "Extended Module: "
|
char signature[17]; // "Extended Module: "
|
||||||
char songName[20]; // Song Name, not null-terminated (any nulls are treated as spaces)
|
char songName[20]; // Song Name, not null-terminated (any nulls are treated as spaces)
|
||||||
uint8le eof; // DOS EOF Character (0x1A)
|
uint8le eof; // DOS EOF Character (0x1A)
|
||||||
char trackerName[20]; // Software that was used to create the XM file
|
char trackerName[20]; // Software that was used to create the XM file
|
||||||
uint16le version; // File version (1.02 - 1.04 are supported)
|
uint16le version; // File version (1.02 - 1.04 are supported)
|
||||||
uint32le size; // Header Size
|
uint32le size; // Header Size
|
||||||
uint16le orders; // Number of Orders
|
uint16le orders; // Number of Orders
|
||||||
uint16le restartPos; // Restart Position
|
uint16le restartPos; // Restart Position
|
||||||
uint16le channels; // Number of Channels
|
uint16le channels; // Number of Channels
|
||||||
uint16le patterns; // Number of Patterns
|
uint16le patterns; // Number of Patterns
|
||||||
uint16le instruments; // Number of Unstruments
|
uint16le instruments; // Number of Unstruments
|
||||||
uint16le flags; // Song Flags
|
uint16le flags; // Song Flags
|
||||||
uint16le speed; // Default Speed
|
uint16le speed; // Default Speed
|
||||||
uint16le tempo; // Default Tempo
|
uint16le tempo; // Default Tempo
|
||||||
};
|
};
|
||||||
|
|
||||||
MPT_BINARY_STRUCT(XMFileHeader, 80)
|
MPT_BINARY_STRUCT(XMFileHeader, 80)
|
||||||
|
@ -50,35 +52,35 @@ struct XMInstrument
|
||||||
// Envelope Flags
|
// Envelope Flags
|
||||||
enum XMEnvelopeFlags
|
enum XMEnvelopeFlags
|
||||||
{
|
{
|
||||||
envEnabled = 0x01,
|
envEnabled = 0x01,
|
||||||
envSustain = 0x02,
|
envSustain = 0x02,
|
||||||
envLoop = 0x04,
|
envLoop = 0x04,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8le sampleMap[96]; // Note -> Sample assignment
|
uint8le sampleMap[96]; // Note -> Sample assignment
|
||||||
uint16le volEnv[24]; // Volume envelope nodes / values (0...64)
|
uint16le volEnv[24]; // Volume envelope nodes / values (0...64)
|
||||||
uint16le panEnv[24]; // Panning envelope nodes / values (0...63)
|
uint16le panEnv[24]; // Panning envelope nodes / values (0...63)
|
||||||
uint8le volPoints; // Volume envelope length
|
uint8le volPoints; // Volume envelope length
|
||||||
uint8le panPoints; // Panning envelope length
|
uint8le panPoints; // Panning envelope length
|
||||||
uint8le volSustain; // Volume envelope sustain point
|
uint8le volSustain; // Volume envelope sustain point
|
||||||
uint8le volLoopStart; // Volume envelope loop start point
|
uint8le volLoopStart; // Volume envelope loop start point
|
||||||
uint8le volLoopEnd; // Volume envelope loop end point
|
uint8le volLoopEnd; // Volume envelope loop end point
|
||||||
uint8le panSustain; // Panning envelope sustain point
|
uint8le panSustain; // Panning envelope sustain point
|
||||||
uint8le panLoopStart; // Panning envelope loop start point
|
uint8le panLoopStart; // Panning envelope loop start point
|
||||||
uint8le panLoopEnd; // Panning envelope loop end point
|
uint8le panLoopEnd; // Panning envelope loop end point
|
||||||
uint8le volFlags; // Volume envelope flags
|
uint8le volFlags; // Volume envelope flags
|
||||||
uint8le panFlags; // Panning envelope flags
|
uint8le panFlags; // Panning envelope flags
|
||||||
uint8le vibType; // Sample Auto-Vibrato Type
|
uint8le vibType; // Sample Auto-Vibrato Type
|
||||||
uint8le vibSweep; // Sample Auto-Vibrato Sweep
|
uint8le vibSweep; // Sample Auto-Vibrato Sweep
|
||||||
uint8le vibDepth; // Sample Auto-Vibrato Depth
|
uint8le vibDepth; // Sample Auto-Vibrato Depth
|
||||||
uint8le vibRate; // Sample Auto-Vibrato Rate
|
uint8le vibRate; // Sample Auto-Vibrato Rate
|
||||||
uint16le volFade; // Volume Fade-Out
|
uint16le volFade; // Volume Fade-Out
|
||||||
uint8le midiEnabled; // MIDI Out Enabled (0 / 1)
|
uint8le midiEnabled; // MIDI Out Enabled (0 / 1)
|
||||||
uint8le midiChannel; // MIDI Channel (0...15)
|
uint8le midiChannel; // MIDI Channel (0...15)
|
||||||
uint16le midiProgram; // MIDI Program (0...127)
|
uint16le midiProgram; // MIDI Program (0...127)
|
||||||
uint16le pitchWheelRange; // MIDI Pitch Wheel Range (0...36 halftones)
|
uint16le pitchWheelRange; // MIDI Pitch Wheel Range (0...36 halftones)
|
||||||
uint8le muteComputer; // Mute instrument if MIDI is enabled (0 / 1)
|
uint8le muteComputer; // Mute instrument if MIDI is enabled (0 / 1)
|
||||||
uint8le reserved[15]; // Reserved
|
uint8le reserved[15]; // Reserved
|
||||||
|
|
||||||
enum EnvType
|
enum EnvType
|
||||||
{
|
{
|
||||||
|
@ -109,11 +111,11 @@ MPT_BINARY_STRUCT(XMInstrument, 230)
|
||||||
// XM Instrument Header
|
// XM Instrument Header
|
||||||
struct XMInstrumentHeader
|
struct XMInstrumentHeader
|
||||||
{
|
{
|
||||||
uint32le size; // Size of XMInstrumentHeader + XMInstrument
|
uint32le size; // Size of XMInstrumentHeader + XMInstrument
|
||||||
char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces)
|
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!)
|
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
|
uint16le numSamples; // Number of Samples associated with instrument
|
||||||
uint32le sampleHeaderSize; // Size of XMSample
|
uint32le sampleHeaderSize; // Size of XMSample
|
||||||
XMInstrument instrument;
|
XMInstrument instrument;
|
||||||
|
|
||||||
// Write stuff to the header that's always necessary (also for empty instruments)
|
// Write stuff to the header that's always necessary (also for empty instruments)
|
||||||
|
@ -133,16 +135,16 @@ struct XIInstrumentHeader
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
fileVersion = 0x102,
|
fileVersion = 0x102,
|
||||||
};
|
};
|
||||||
|
|
||||||
char signature[21]; // "Extended Instrument: "
|
char signature[21]; // "Extended Instrument: "
|
||||||
char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces)
|
char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces)
|
||||||
uint8le eof; // DOS EOF Character (0x1A)
|
uint8le eof; // DOS EOF Character (0x1A)
|
||||||
char trackerName[20]; // Software that was used to create the XI file
|
char trackerName[20]; // Software that was used to create the XI file
|
||||||
uint16le version; // File Version (1.02)
|
uint16le version; // File Version (1.02)
|
||||||
XMInstrument instrument;
|
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.
|
// Convert OpenMPT's internal sample representation to an XIInstrumentHeader.
|
||||||
void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport);
|
void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport);
|
||||||
|
@ -158,24 +160,23 @@ struct XMSample
|
||||||
{
|
{
|
||||||
enum XMSampleFlags
|
enum XMSampleFlags
|
||||||
{
|
{
|
||||||
sampleLoop = 0x01,
|
sampleLoop = 0x01,
|
||||||
sampleBidiLoop = 0x02,
|
sampleBidiLoop = 0x02,
|
||||||
sample16Bit = 0x10,
|
sample16Bit = 0x10,
|
||||||
sampleStereo = 0x20,
|
sampleStereo = 0x20,
|
||||||
|
sampleADPCM = 0xAD, // MODPlugin :(
|
||||||
sampleADPCM = 0xAD, // MODPlugin :(
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32le length; // Sample Length (in bytes)
|
uint32le length; // Sample Length (in bytes)
|
||||||
uint32le loopStart; // Loop Start (in bytes)
|
uint32le loopStart; // Loop Start (in bytes)
|
||||||
uint32le loopLength; // Loop Length (in bytes)
|
uint32le loopLength; // Loop Length (in bytes)
|
||||||
uint8le vol; // Default Volume
|
uint8le vol; // Default Volume
|
||||||
int8le finetune; // Sample Finetune
|
int8le finetune; // Sample Finetune
|
||||||
uint8le flags; // Sample Flags
|
uint8le flags; // Sample Flags
|
||||||
uint8le pan; // Sample Panning
|
uint8le pan; // Sample Panning
|
||||||
int8le relnote; // Sample Transpose
|
int8le relnote; // Sample Transpose
|
||||||
uint8le reserved; // Reserved (abused for ModPlug's ADPCM compression)
|
uint8le reserved; // Reserved (abused for ModPlug's ADPCM compression)
|
||||||
char name[22]; // Sample Name, not null-terminated (any nulls are treated as spaces)
|
char name[22]; // Sample Name, not null-terminated (any nulls are treated as spaces)
|
||||||
|
|
||||||
// Convert OpenMPT's internal sample representation to an XMSample.
|
// Convert OpenMPT's internal sample representation to an XMSample.
|
||||||
void ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compatibilityExport);
|
void ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compatibilityExport);
|
||||||
|
|
|
@ -1173,7 +1173,7 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd)
|
||||||
CMD_TONEPORTAMENTO,
|
CMD_TONEPORTAMENTO,
|
||||||
CMD_TONEPORTAVOL,
|
CMD_TONEPORTAVOL,
|
||||||
CMD_DBMECHO,
|
CMD_DBMECHO,
|
||||||
CMD_GLOBALVOLSLIDE,
|
CMD_CHANNELVOLSLIDE,
|
||||||
CMD_CHANNELVOLUME,
|
CMD_CHANNELVOLUME,
|
||||||
CMD_GLOBALVOLSLIDE,
|
CMD_GLOBALVOLSLIDE,
|
||||||
CMD_GLOBALVOLUME,
|
CMD_GLOBALVOLUME,
|
||||||
|
@ -1192,6 +1192,7 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Invalid / unknown command.
|
// Invalid / unknown command.
|
||||||
|
MPT_ASSERT_NOTREACHED();
|
||||||
return 0;
|
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)
|
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++)
|
for(uint8 n = 0; n < 4; n++)
|
||||||
{
|
{
|
||||||
if(auto volCmd = ModCommand::ConvertToVolCommand(effect1, param1, (n > 1)); effect1 == CMD_NONE || volCmd.first != VOLCMD_NONE)
|
if(auto volCmd = ModCommand::ConvertToVolCommand(effect1, param1, (n > 1)); effect1 == CMD_NONE || volCmd.first != VOLCMD_NONE)
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "openmpt/all/BuildSettings.hpp"
|
#include "openmpt/all/BuildSettings.hpp"
|
||||||
|
|
||||||
#include "Snd_defs.h"
|
#include "Snd_defs.h"
|
||||||
#include "../common/misc_util.h"
|
#include "mpt/base/algorithm.hpp"
|
||||||
|
|
||||||
OPENMPT_NAMESPACE_BEGIN
|
OPENMPT_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include <cstdint>
|
#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
|
// The 4-op channels are 0, 1, 2, 9, 10, 11
|
||||||
uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6);
|
uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6);
|
||||||
|
// cppcheck false-positive
|
||||||
|
// cppcheck-suppress arrayIndexOutOfBounds
|
||||||
Channel *primary = &Chan[chan];
|
Channel *primary = &Chan[chan];
|
||||||
|
// cppcheck false-positive
|
||||||
|
// cppcheck-suppress arrayIndexOutOfBounds
|
||||||
Channel *secondary = &Chan[chan + 3];
|
Channel *secondary = &Chan[chan + 3];
|
||||||
|
|
||||||
if (val & mask) {
|
if (val & mask) {
|
||||||
|
@ -570,9 +575,9 @@ void Opal::Sample(int16_t *left, int16_t *right) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mix with the partial accumulation
|
// Mix with the partial accumulation
|
||||||
int32_t omblend = SampleRate - SampleAccum;
|
const int32_t fract = Util::muldivr(SampleAccum, 65536, SampleRate);
|
||||||
*left = static_cast<uint16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
|
*left = static_cast<int16_t>(LastOutput[0] + ((fract * (CurrOutput[0] - LastOutput[0])) / 65536));
|
||||||
*right = static_cast<uint16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate);
|
*right = static_cast<int16_t>(LastOutput[1] + ((fract * (CurrOutput[1] - LastOutput[1])) / 65536));
|
||||||
|
|
||||||
SampleAccum += OPL3SampleRate;
|
SampleAccum += OPL3SampleRate;
|
||||||
}
|
}
|
||||||
|
@ -602,14 +607,14 @@ void Opal::Output(int16_t &left, int16_t &right) {
|
||||||
else if (leftmix > 0x7FFF)
|
else if (leftmix > 0x7FFF)
|
||||||
left = 0x7FFF;
|
left = 0x7FFF;
|
||||||
else
|
else
|
||||||
left = static_cast<uint16_t>(leftmix);
|
left = static_cast<int16_t>(leftmix);
|
||||||
|
|
||||||
if (rightmix < -0x8000)
|
if (rightmix < -0x8000)
|
||||||
right = -0x8000;
|
right = -0x8000;
|
||||||
else if (rightmix > 0x7FFF)
|
else if (rightmix > 0x7FFF)
|
||||||
right = 0x7FFF;
|
right = 0x7FFF;
|
||||||
else
|
else
|
||||||
right = static_cast<uint16_t>(rightmix);
|
right = static_cast<int16_t>(rightmix);
|
||||||
|
|
||||||
Clock++;
|
Clock++;
|
||||||
|
|
||||||
|
|
|
@ -739,7 +739,7 @@ IMidiPlugin::IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGI
|
||||||
for(auto &chn : m_MidiCh)
|
for(auto &chn : m_MidiCh)
|
||||||
{
|
{
|
||||||
chn.midiPitchBendPos = EncodePitchBendParam(MIDIEvents::pitchBendCentre); // centre pitch bend on all channels
|
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 lastNote = 0 /* NOTE_NONE */;
|
||||||
uint8 noteOnMap[128][MAX_CHANNELS];
|
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
|
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