diff --git a/Frameworks/OpenMPT/OpenMPT/LICENSE b/Frameworks/OpenMPT/OpenMPT/LICENSE index ad8c18f5d..834603bf0 100644 --- a/Frameworks/OpenMPT/OpenMPT/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/LICENSE @@ -1,6 +1,4 @@ -The OpenMPT code is licensed under the BSD license. - -Copyright (c) 2004-2018, OpenMPT contributors +Copyright (c) 2004-2019, OpenMPT contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. @@ -24,4 +22,5 @@ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Frameworks/OpenMPT/OpenMPT/Makefile b/Frameworks/OpenMPT/OpenMPT/Makefile index 3731afe5e..fb62059af 100644 --- a/Frameworks/OpenMPT/OpenMPT/Makefile +++ b/Frameworks/OpenMPT/OpenMPT/Makefile @@ -53,6 +53,7 @@ # TEST=1 Include test suite in default target. # ONLY_TEST=0 Only build the test suite. # STRICT=0 Treat warnings as errors. +# MODERN=0 Pass more modern compiler options. # STDCXX=c++11 C++ standard version (only for GCC and clang) # CHECKED=0 Enable run-time assertions. # CHECKED_ADDRESS=0 Enable address sanitizer @@ -68,10 +69,11 @@ # NO_VORBIS=1 Avoid using libvorbis, even if found # NO_VORBISFILE=1 Avoid using libvorbisfile, even if found # +# NO_MINIMP3=1 Do not fallback to minimp3 # NO_STBVORBIS=1 Do not fallback to stb_vorbis # -# USE_MINIMP3=1 Use minimp3. -# Beware that minimp3 is LGPL 2.1 licensed. +# USE_ALLEGRO42=1 Use liballegro 4.2 (DJGPP only) +# BUNDLED_ALLEGRO42=1 Use liballegro 4.2 in libopenmpt source tree (DJGPP only) # # Build flags for libopenmpt examples and openmpt123 # (provide on each `make` invocation) @@ -157,6 +159,7 @@ ONLY_TEST=0 SOSUFFIX=.so SOSUFFIXWINDOWS=0 OPENMPT123=1 +MODERN=0 STRICT=0 CHECKED=0 @@ -201,6 +204,11 @@ ifeq ($(OS),Windows_NT) HOST=windows HOST_FLAVOUR= +TOOLCHAIN_SUFFIX= + +CPPCHECK = cppcheck + +MKDIR_P = mkdir RM = del /q /f RMTREE = del /q /f /s INSTALL = echo install @@ -208,11 +216,18 @@ INSTALL_MAKE_DIR = echo install INSTALL_DIR = echo install FIXPATH = $(subst /,\,$1) +NUMTHREADS:=$(NUMBER_OF_PROCESSORS) + else HOST=unix HOST_FLAVOUR= +TOOLCHAIN_SUFFIX= + +CPPCHECK = cppcheck + +MKDIR_P = mkdir -p RM = rm -f RMTREE = rm -rf INSTALL = install @@ -230,10 +245,24 @@ endif ifeq ($(UNAME_S),FreeBSD) HOST_FLAVOUR=FREEBSD endif +ifeq ($(UNAME_S),Haiku) +HOST_FLAVOUR=HAIKU +endif + +ifeq ($(HOST_FLAVOUR),LINUX) +NUMTHREADS:=$(shell nproc) +else +NUMTHREADS:=1 +endif endif +# early build setup + +BINDIR_MADE:=$(shell $(MKDIR_P) bin) + + # compiler setup ifeq ($(CONFIG)x,x) @@ -249,8 +278,6 @@ endif # build setup -BINDIR_MADE:=$(shell mkdir -p bin) - ifeq ($(SOSUFFIXWINDOWS),1) LIBOPENMPT_SONAME=libopenmpt-$(LIBOPENMPT_SO_VERSION)$(SOSUFFIX) else @@ -295,8 +322,13 @@ CFLAGS += -Wall else +ifeq ($(MPT_COMPILER_NOVISIBILITY),1) +CXXFLAGS += +CFLAGS += +else CXXFLAGS += -fvisibility=hidden CFLAGS += -fvisibility=hidden +endif LDFLAGS += LDLIBS += ARFLAGS += @@ -307,9 +339,15 @@ CXXFLAGS += -O0 -g -fno-omit-frame-pointer CFLAGS += -O0 -g -fno-omit-frame-pointer else ifeq ($(OPTIMIZE_SIZE),1) -CXXFLAGS += -ffunction-sections -fdata-sections -Os -ffast-math -CFLAGS += -ffunction-sections -fdata-sections -Os -ffast-math -fno-strict-aliasing +CXXFLAGS += -Os -ffast-math +CFLAGS += -Os -ffast-math -fno-strict-aliasing +LDFLAGS += +ifeq ($(MPT_COMPILER_NOGCSECTIONS),1) +else +CXXFLAGS += -ffunction-sections -fdata-sections +CFLAGS += -ffunction-sections -fdata-sections LDFLAGS += -Wl,--gc-sections +endif else ifeq ($(OPTIMIZE),1) CXXFLAGS += -O3 -ffast-math @@ -332,6 +370,7 @@ endif CXXFLAGS += -Wall -Wextra -Wundef -Wcast-qual -Wcast-align $(CXXFLAGS_WARNINGS) CFLAGS += -Wall -Wextra -Wundef -Wcast-qual -Wcast-align $(CFLAGS_WARNINGS) +LDFLAGS += $(LDFLAGS_WARNINGS) endif @@ -371,10 +410,10 @@ endif ifeq ($(NO_ZLIB),1) else #LDLIBS += -lz -ifeq ($(shell pkg-config --exists zlib && echo yes),yes) -CPPFLAGS_ZLIB := $(shell pkg-config --cflags-only-I zlib ) -DMPT_WITH_ZLIB -LDFLAGS_ZLIB := $(shell pkg-config --libs-only-L zlib ) $(shell pkg-config --libs-only-other zlib ) -LDLIBS_ZLIB := $(shell pkg-config --libs-only-l zlib ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists zlib && echo yes),yes) +CPPFLAGS_ZLIB := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I zlib ) -DMPT_WITH_ZLIB +LDFLAGS_ZLIB := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L zlib ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other zlib ) +LDLIBS_ZLIB := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l zlib ) PC_REQUIRES_ZLIB := zlib else ifeq ($(FORCE_DEPS),1) @@ -389,10 +428,10 @@ endif ifeq ($(NO_MPG123),1) else #LDLIBS += -lmpg123 -ifeq ($(shell pkg-config --exists 'libmpg123 >= 1.13.0' && echo yes),yes) -CPPFLAGS_MPG123 := $(shell pkg-config --cflags-only-I 'libmpg123 >= 1.13.0' ) -DMPT_WITH_MPG123 -LDFLAGS_MPG123 := $(shell pkg-config --libs-only-L 'libmpg123 >= 1.13.0' ) $(shell pkg-config --libs-only-other 'libmpg123 >= 1.13.0' ) -LDLIBS_MPG123 := $(shell pkg-config --libs-only-l 'libmpg123 >= 1.13.0' ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists 'libmpg123 >= 1.14.0' && echo yes),yes) +CPPFLAGS_MPG123 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I 'libmpg123 >= 1.14.0' ) -DMPT_WITH_MPG123 +LDFLAGS_MPG123 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L 'libmpg123 >= 1.14.0' ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other 'libmpg123 >= 1.14.0' ) +LDLIBS_MPG123 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l 'libmpg123 >= 1.14.0' ) PC_REQUIRES_MPG123 := libmpg123 else ifeq ($(FORCE_DEPS),1) @@ -407,10 +446,10 @@ endif ifeq ($(NO_OGG),1) else #LDLIBS += -logg -ifeq ($(shell pkg-config --exists ogg && echo yes),yes) -CPPFLAGS_OGG := $(shell pkg-config --cflags-only-I ogg ) -DMPT_WITH_OGG -LDFLAGS_OGG := $(shell pkg-config --libs-only-L ogg ) $(shell pkg-config --libs-only-other ogg ) -LDLIBS_OGG := $(shell pkg-config --libs-only-l ogg ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists ogg && echo yes),yes) +CPPFLAGS_OGG := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I ogg ) -DMPT_WITH_OGG +LDFLAGS_OGG := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L ogg ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other ogg ) +LDLIBS_OGG := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l ogg ) PC_REQUIRES_OGG := ogg else ifeq ($(FORCE_DEPS),1) @@ -425,10 +464,10 @@ endif ifeq ($(NO_VORBIS),1) else #LDLIBS += -lvorbis -ifeq ($(shell pkg-config --exists vorbis && echo yes),yes) -CPPFLAGS_VORBIS := $(shell pkg-config --cflags-only-I vorbis ) -DMPT_WITH_VORBIS -LDFLAGS_VORBIS := $(shell pkg-config --libs-only-L vorbis ) $(shell pkg-config --libs-only-other vorbis ) -LDLIBS_VORBIS := $(shell pkg-config --libs-only-l vorbis ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists vorbis && echo yes),yes) +CPPFLAGS_VORBIS := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I vorbis ) -DMPT_WITH_VORBIS +LDFLAGS_VORBIS := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L vorbis ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other vorbis ) +LDLIBS_VORBIS := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l vorbis ) PC_REQUIRES_VORBIS := vorbis else ifeq ($(FORCE_DEPS),1) @@ -443,10 +482,10 @@ endif ifeq ($(NO_VORBISFILE),1) else #LDLIBS += -lvorbisfile -ifeq ($(shell pkg-config --exists vorbisfile && echo yes),yes) -CPPFLAGS_VORBISFILE := $(shell pkg-config --cflags-only-I vorbisfile ) -DMPT_WITH_VORBISFILE -LDFLAGS_VORBISFILE := $(shell pkg-config --libs-only-L vorbisfile ) $(shell pkg-config --libs-only-other vorbisfile ) -LDLIBS_VORBISFILE := $(shell pkg-config --libs-only-l vorbisfile ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists vorbisfile && echo yes),yes) +CPPFLAGS_VORBISFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I vorbisfile ) -DMPT_WITH_VORBISFILE +LDFLAGS_VORBISFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L vorbisfile ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other vorbisfile ) +LDLIBS_VORBISFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l vorbisfile ) PC_REQUIRES_VORBISFILE := vorbisfile else ifeq ($(FORCE_DEPS),1) @@ -461,10 +500,10 @@ endif ifeq ($(NO_SDL2),1) else #LDLIBS += -lsdl2 -ifeq ($(shell pkg-config --exists sdl2 && echo yes),yes) -CPPFLAGS_SDL := $(shell pkg-config --cflags-only-I sdl2 ) -DMPT_WITH_SDL2 -LDFLAGS_SDL := $(shell pkg-config --libs-only-L sdl2 ) $(shell pkg-config --libs-only-other sdl2 ) -LDLIBS_SDL := $(shell pkg-config --libs-only-l sdl2 ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists sdl2 && echo yes),yes) +CPPFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I sdl2 ) -DMPT_WITH_SDL2 +LDFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L sdl2 ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other sdl2 ) +LDLIBS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l sdl2 ) NO_SDL:=1 else ifeq ($(FORCE_DEPS),1) @@ -479,10 +518,10 @@ endif ifeq ($(NO_SDL),1) else #LDLIBS += -lsdl -ifeq ($(shell pkg-config --exists sdl && echo yes),yes) -CPPFLAGS_SDL := $(shell pkg-config --cflags-only-I sdl ) -DMPT_WITH_SDL -LDFLAGS_SDL := $(shell pkg-config --libs-only-L sdl ) $(shell pkg-config --libs-only-other sdl ) -LDLIBS_SDL := $(shell pkg-config --libs-only-l sdl ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists sdl && echo yes),yes) +CPPFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I sdl ) -DMPT_WITH_SDL +LDFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L sdl ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other sdl ) +LDLIBS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l sdl ) else ifeq ($(FORCE_DEPS),1) $(error sdl not found) @@ -496,10 +535,10 @@ endif ifeq ($(NO_PORTAUDIO),1) else #LDLIBS += -lportaudio -ifeq ($(shell pkg-config --exists portaudio-2.0 && echo yes),yes) -CPPFLAGS_PORTAUDIO := $(shell pkg-config --cflags-only-I portaudio-2.0 ) -DMPT_WITH_PORTAUDIO -LDFLAGS_PORTAUDIO := $(shell pkg-config --libs-only-L portaudio-2.0 ) $(shell pkg-config --libs-only-other portaudio-2.0 ) -LDLIBS_PORTAUDIO := $(shell pkg-config --libs-only-l portaudio-2.0 ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists portaudio-2.0 && echo yes),yes) +CPPFLAGS_PORTAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I portaudio-2.0 ) -DMPT_WITH_PORTAUDIO +LDFLAGS_PORTAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L portaudio-2.0 ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other portaudio-2.0 ) +LDLIBS_PORTAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l portaudio-2.0 ) else ifeq ($(FORCE_DEPS),1) $(error portaudio not found) @@ -513,10 +552,10 @@ endif ifeq ($(NO_PORTAUDIOCPP),1) else #LDLIBS += -lportaudiocpp -ifeq ($(shell pkg-config --exists portaudiocpp && echo yes),yes) -CPPFLAGS_PORTAUDIOCPP := $(shell pkg-config --cflags-only-I portaudiocpp ) -DMPT_WITH_PORTAUDIOCPP -LDFLAGS_PORTAUDIOCPP := $(shell pkg-config --libs-only-L portaudiocpp ) $(shell pkg-config --libs-only-other portaudiocpp ) -LDLIBS_PORTAUDIOCPP := $(shell pkg-config --libs-only-l portaudiocpp ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists portaudiocpp && echo yes),yes) +CPPFLAGS_PORTAUDIOCPP := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I portaudiocpp ) -DMPT_WITH_PORTAUDIOCPP +LDFLAGS_PORTAUDIOCPP := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L portaudiocpp ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other portaudiocpp ) +LDLIBS_PORTAUDIOCPP := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l portaudiocpp ) else ifeq ($(FORCE_DEPS),1) $(error portaudiocpp not found) @@ -530,10 +569,10 @@ endif ifeq ($(NO_PULSEAUDIO),1) else #LDLIBS += -lpulse-simple -ifeq ($(shell pkg-config --exists libpulse libpulse-simple && echo yes),yes) -CPPFLAGS_PULSEAUDIO := $(shell pkg-config --cflags-only-I libpulse libpulse-simple ) -DMPT_WITH_PULSEAUDIO -LDFLAGS_PULSEAUDIO := $(shell pkg-config --libs-only-L libpulse libpulse-simple ) $(shell pkg-config --libs-only-other libpulse libpulse-simple ) -LDLIBS_PULSEAUDIO := $(shell pkg-config --libs-only-l libpulse libpulse-simple ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists libpulse libpulse-simple && echo yes),yes) +CPPFLAGS_PULSEAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I libpulse libpulse-simple ) -DMPT_WITH_PULSEAUDIO +LDFLAGS_PULSEAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L libpulse libpulse-simple ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other libpulse libpulse-simple ) +LDLIBS_PULSEAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l libpulse libpulse-simple ) else ifeq ($(FORCE_DEPS),1) $(error pulseaudio not found) @@ -547,10 +586,10 @@ endif ifeq ($(NO_FLAC),1) else #LDLIBS += -lFLAC -ifeq ($(shell pkg-config --exists 'flac >= 1.3.0' && echo yes),yes) -CPPFLAGS_FLAC := $(shell pkg-config --cflags-only-I 'flac >= 1.3.0' ) -DMPT_WITH_FLAC -LDFLAGS_FLAC := $(shell pkg-config --libs-only-L 'flac >= 1.3.0' ) $(shell pkg-config --libs-only-other 'flac >= 1.3.0' ) -LDLIBS_FLAC := $(shell pkg-config --libs-only-l 'flac >= 1.3.0' ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists 'flac >= 1.3.0' && echo yes),yes) +CPPFLAGS_FLAC := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I 'flac >= 1.3.0' ) -DMPT_WITH_FLAC +LDFLAGS_FLAC := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L 'flac >= 1.3.0' ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other 'flac >= 1.3.0' ) +LDLIBS_FLAC := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l 'flac >= 1.3.0' ) else ifeq ($(FORCE_DEPS),1) $(error flac not found) @@ -564,10 +603,10 @@ endif ifeq ($(NO_SNDFILE),1) else #LDLIBS += -lsndfile -ifeq ($(shell pkg-config --exists sndfile && echo yes),yes) -CPPFLAGS_SNDFILE := $(shell pkg-config --cflags-only-I sndfile ) -DMPT_WITH_SNDFILE -LDFLAGS_SNDFILE := $(shell pkg-config --libs-only-L sndfile ) $(shell pkg-config --libs-only-other sndfile ) -LDLIBS_SNDFILE := $(shell pkg-config --libs-only-l sndfile ) +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists sndfile && echo yes),yes) +CPPFLAGS_SNDFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I sndfile ) -DMPT_WITH_SNDFILE +LDFLAGS_SNDFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L sndfile ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other sndfile ) +LDLIBS_SNDFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l sndfile ) else ifeq ($(FORCE_DEPS),1) $(error sndfile not found) @@ -578,17 +617,31 @@ NO_SNDFILE:=1 endif endif +ifeq ($(USE_ALLEGRO42),1) +CPPFLAGS_ALLEGRO42 ?= +LDFLAGS_ALLEGRO42 ?= +LDLIBS_ALLEGRO42 ?= liballeg.a +CPPFLAGS_ALLEGRO42 += -DMPT_WITH_ALLEGRO42 +endif + ifeq ($(HACK_ARCHIVE_SUPPORT),1) CPPFLAGS += -DMPT_BUILD_HACK_ARCHIVE_SUPPORT endif +CPPCHECK_FLAGS += -j $(NUMTHREADS) +CPPCHECK_FLAGS += --std=c99 --std=c++11 +CPPCHECK_FLAGS += --quiet +CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]' +CPPCHECK_FLAGS += --suppress=missingIncludeSystem + +CPPCHECK_FLAGS += $(CPPFLAGS) CPPFLAGS += $(CPPFLAGS_ZLIB) $(CPPFLAGS_MPG123) $(CPPFLAGS_OGG) $(CPPFLAGS_VORBIS) $(CPPFLAGS_VORBISFILE) LDFLAGS += $(LDFLAGS_ZLIB) $(LDFLAGS_MPG123) $(LDFLAGS_OGG) $(LDFLAGS_VORBIS) $(LDFLAGS_VORBISFILE) LDLIBS += $(LDLIBS_ZLIB) $(LDLIBS_MPG123) $(LDLIBS_OGG) $(LDLIBS_VORBIS) $(LDLIBS_VORBISFILE) -CPPFLAGS_OPENMPT123 += $(CPPFLAGS_SDL2) $(CPPFLAGS_SDL) $(CPPFLAGS_PORTAUDIO) $(CPPFLAGS_PULSEAUDIO) $(CPPFLAGS_FLAC) $(CPPFLAGS_SNDFILE) -LDFLAGS_OPENMPT123 += $(LDFLAGS_SDL2) $(LDFLAGS_SDL) $(LDFLAGS_PORTAUDIO) $(LDFLAGS_PULSEAUDIO) $(LDFLAGS_FLAC) $(LDFLAGS_SNDFILE) -LDLIBS_OPENMPT123 += $(LDLIBS_SDL2) $(LDLIBS_SDL) $(LDLIBS_PORTAUDIO) $(LDLIBS_PULSEAUDIO) $(LDLIBS_FLAC) $(LDLIBS_SNDFILE) +CPPFLAGS_OPENMPT123 += $(CPPFLAGS_SDL2) $(CPPFLAGS_SDL) $(CPPFLAGS_PORTAUDIO) $(CPPFLAGS_PULSEAUDIO) $(CPPFLAGS_FLAC) $(CPPFLAGS_SNDFILE) $(CPPFLAGS_ALLEGRO42) +LDFLAGS_OPENMPT123 += $(LDFLAGS_SDL2) $(LDFLAGS_SDL) $(LDFLAGS_PORTAUDIO) $(LDFLAGS_PULSEAUDIO) $(LDFLAGS_FLAC) $(LDFLAGS_SNDFILE) $(LDFLAGS_ALLEGRO42) +LDLIBS_OPENMPT123 += $(LDLIBS_SDL2) $(LDLIBS_SDL) $(LDLIBS_PORTAUDIO) $(LDLIBS_PULSEAUDIO) $(LDLIBS_FLAC) $(LDLIBS_SNDFILE) $(LDLIBS_ALLEGRO42) %: %.o @@ -717,7 +770,8 @@ endif include/minimp3/minimp3.o : CFLAGS+=$(CFLAGS_SILENT) include/minimp3/minimp3.test.o : CFLAGS+=$(CFLAGS_SILENT) ifeq ($(NO_MPG123),1) -ifeq ($(USE_MINIMP3),1) +ifeq ($(NO_MINIMP3),1) +else LIBOPENMPT_C_SOURCES += include/minimp3/minimp3.c LIBOPENMPTTEST_C_SOURCES += include/minimp3/minimp3.c CPPFLAGS += -DMPT_WITH_MINIMP3 @@ -869,6 +923,8 @@ ifeq ($(SHARED_SONAME),1) LIBOPENMPT_LDFLAGS += -Wl,-soname,$(LIBOPENMPT_SONAME) endif +MISC_OUTPUTS += bin/empty.cpp +MISC_OUTPUTS += bin/empty.out MISC_OUTPUTS += bin/openmpt123$(EXESUFFIX).norpath MISC_OUTPUTS += bin/libopenmpt_example_c$(EXESUFFIX).norpath MISC_OUTPUTS += bin/libopenmpt_example_c_mem$(EXESUFFIX).norpath @@ -880,15 +936,23 @@ MISC_OUTPUTS += bin/libopenmpt_example_c_stdout$(EXESUFFIX).norpath MISC_OUTPUTS += libopenmpt$(SOSUFFIX) MISC_OUTPUTS += bin/.docs MISC_OUTPUTS += bin/libopenmpt_test$(EXESUFFIX) +MISC_OUTPUTS += bin/libopenmpt_test.wasm MISC_OUTPUTS += bin/libopenmpt_test.js.mem MISC_OUTPUTS += bin/made.docs MISC_OUTPUTS += bin/$(LIBOPENMPT_SONAME) +MISC_OUTPUTS += bin/libopenmpt.wasm MISC_OUTPUTS += bin/libopenmpt.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c.wasm MISC_OUTPUTS += bin/libopenmpt_example_c.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_mem.wasm MISC_OUTPUTS += bin/libopenmpt_example_c_mem.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.wasm MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_probe.wasm MISC_OUTPUTS += bin/libopenmpt_example_c_probe.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.wasm MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.wasm MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.js.mem MISC_OUTPUTS += bin/openmpt.a #old @@ -905,7 +969,8 @@ DIST_OUTPUTS += bin/dist-tar.tar DIST_OUTPUTS += bin/dist-zip.tar DIST_OUTPUTS += bin/dist-doc.tar DIST_OUTPUTS += bin/dist-autotools.tar -DIST_OUTPUTS += bin/dist-js.tar +DIST_OUTPUTS += bin/dist-js.tar +DIST_OUTPUTS += bin/dist-dos.tar DIST_OUTPUTS += bin/made.docs DIST_OUTPUTDIRS += bin/dist @@ -914,6 +979,7 @@ DIST_OUTPUTDIRS += bin/dist-tar DIST_OUTPUTDIRS += bin/dist-zip DIST_OUTPUTDIRS += bin/dist-autotools DIST_OUTPUTDIRS += bin/dist-js +DIST_OUTPUTDIRS += bin/dist-dos DIST_OUTPUTDIRS += bin/docs @@ -1090,6 +1156,16 @@ bin/dist-js.tar: bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar.gz cd bin/dist-js/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar.gz libopenmpt/dev.js/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ cd bin/dist-js/ && tar cvf ../dist-js.tar libopenmpt +.PHONY: dist-dos +dist-dos: bin/dist-dos.tar + +bin/dist-dos.tar: bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip + rm -rf bin/dist-dos.tar + cd bin/dist-dos/ && rm -rf libopenmpt + cd bin/dist-dos/ && mkdir -p libopenmpt/bin.dos/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-dos/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip libopenmpt/bin.dos/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-dos/ && tar cvf ../dist-dos.tar libopenmpt + .PHONY: bin/dist.mk bin/dist.mk: rm -rf $@ @@ -1145,6 +1221,8 @@ bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin svn export ./examples bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/examples svn export ./openmpt123 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/openmpt123 svn export ./contrib bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/contrib + svn export ./include/allegro42 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/allegro42 + svn export ./include/cwsdpmi bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/cwsdpmi svn export ./include/minimp3 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/minimp3 svn export ./include/miniz bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/miniz svn export ./include/modplug bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/modplug @@ -1159,17 +1237,24 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn rm -rf bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION) mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION) mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/genie + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include svn export ./LICENSE bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE --native-eol CRLF svn export ./README.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md --native-eol CRLF svn export ./Makefile bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/Makefile --native-eol CRLF svn export ./bin bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin --native-eol CRLF + svn export ./build/genie/def bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/genie/def --native-eol CRLF + svn export ./build/premake/def bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake/def --native-eol CRLF + svn export ./build/premake/inc bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake/inc --native-eol CRLF + svn export ./build/premake/lnk bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake/lnk --native-eol CRLF svn export ./build/svn_version bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version --native-eol CRLF svn export ./build/vcpkg bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vcpkg --native-eol CRLF svn export ./build/vs bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs --native-eol CRLF svn export ./build/vs2015 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2015 --native-eol CRLF svn export ./build/vs2015xp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2015xp --native-eol CRLF svn export ./build/vs2017 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017 --native-eol CRLF + svn export ./build/vs2017win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win10 svn export ./build/vs2017xp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017xp --native-eol CRLF svn export ./build/windesktop81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/windesktop81 --native-eol CRLF svn export ./build/winstore82 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/winstore82 --native-eol CRLF @@ -1210,25 +1295,64 @@ bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION).zip: bin/svn_version_dist.h cd bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION)/ && zip -r ../OpenMPT-src-$(DIST_OPENMPT_VERSION).zip --compression-method deflate -9 * .PHONY: bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar -bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: bin/libopenmpt.js +bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: mkdir -p bin/dist-js - rm -rf bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) - mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) - mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses + rm -rf bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses svn export ./LICENSE bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/license.txt + svn export ./include/minimp3/LICENSE bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.minimp3.txt svn export ./include/miniz/miniz.c bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.miniz.txt svn export ./include/stb_vorbis/stb_vorbis.c bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.stb_vorbis.txt - cp bin/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt.js - cp bin/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt.js.mem + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm + cp bin/stage/wasm/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm/libopenmpt.js + cp bin/stage/wasm/libopenmpt.wasm bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm/libopenmpt.wasm + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs128m + cp bin/stage/asmjs128m/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs128m/libopenmpt.js + cp bin/stage/asmjs128m/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs128m/libopenmpt.js.mem + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs + cp bin/stage/asmjs/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs/libopenmpt.js + cp bin/stage/asmjs/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs/libopenmpt.js.mem + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js + cp bin/stage/js/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js/libopenmpt.js + cp bin/stage/js/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js/libopenmpt.js.mem cd bin/dist-js/ && tar cv libopenmpt-$(DIST_LIBOPENMPT_VERSION) > libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar +.PHONY: bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip +bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip: + mkdir -p bin/dist-dos + rm -rf bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES + svn export ./LICENSE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE.TXT --native-eol CRLF + cd bin/dist-dos && unzip ../../build/externals/all422s.zip allegro/readme.txt + mv bin/dist-dos/allegro/readme.txt bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/ALLEGRO.TXT + rmdir bin/dist-dos/allegro + cp include/cwsdpmi/bin/cwsdpmi.doc bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/CWSDPMI.TXT + svn export ./include/minimp3/LICENSE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/MINIMP3.TXT --native-eol CRLF + svn export ./include/miniz/miniz.c bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/MINIZ.TXT --native-eol CRLF + svn export ./include/stb_vorbis/stb_vorbis.c bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/STBVORB.TXT --native-eol CRLF + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC + cp build/externals/csdpmi7s.zip bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC/CSDPMI7S.ZIP + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN + cp bin/openmpt123.exe bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/OMPT123.EXE + cp include/cwsdpmi/bin/cwsdpmi.doc bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDPMI.DOC + cp include/cwsdpmi/bin/CWSDPMI.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDPMI.EXE + cp include/cwsdpmi/bin/CWSDPR0.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDPR0.EXE + cp include/cwsdpmi/bin/cwsparam.doc bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSPARAM.DOC + cp include/cwsdpmi/bin/CWSPARAM.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSPARAM.EXE + cp include/cwsdpmi/bin/CWSDSTUB.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDSTUB.EXE + cp include/cwsdpmi/bin/CWSDSTR0.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDSTR0.EXE + cd bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/ && zip -r ../libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip --compression-method deflate -9 * + bin/libopenmpt.a: $(LIBOPENMPT_OBJECTS) $(INFO) [AR] $@ $(SILENT)$(AR) $(ARFLAGS) $@ $^ bin/libopenmpt$(SOSUFFIX): $(LIBOPENMPT_OBJECTS) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) -shared $(LIBOPENMPT_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ + $(SILENT)$(LINK.cc) -shared $(LIBOPENMPT_LDFLAGS) $(SO_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ ifeq ($(SHARED_SONAME),1) $(SILENT)mv bin/libopenmpt$(SOSUFFIX) bin/$(LIBOPENMPT_SONAME) $(SILENT)ln -sf $(LIBOPENMPT_SONAME) bin/libopenmpt$(SOSUFFIX) @@ -1236,7 +1360,7 @@ endif bin/libopenmpt_modplug$(SOSUFFIX): $(LIBOPENMPT_MODPLUG_OBJECTS) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) -shared $(LDFLAGS_LIBOPENMPT) $(LIBOPENMPT_MODPLUG_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + $(SILENT)$(LINK.cc) -shared $(SO_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LIBOPENMPT_MODPLUG_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ bin/openmpt123.1: bin/openmpt123$(EXESUFFIX) openmpt123/openmpt123.h2m $(INFO) [HELP2MAN] $@ @@ -1248,11 +1372,11 @@ openmpt123/openmpt123.o: openmpt123/openmpt123.cpp $(SILENT)$(COMPILE.cc) $(CXXFLAGS_OPENMPT123) $(CPPFLAGS_OPENMPT123) $(OUTPUT_OPTION) $< bin/openmpt123$(EXESUFFIX): $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ endif contrib/fuzzing/fuzz.o: contrib/fuzzing/fuzz.c @@ -1298,61 +1422,82 @@ examples/libopenmpt_example_cxx.o: examples/libopenmpt_example_cxx.cpp $(SILENT)$(COMPILE.cc) $(CXXFLAGS_PORTAUDIOCPP) $(CPPFLAGS_PORTAUDIOCPP) $(OUTPUT_OPTION) $< bin/libopenmpt_example_c$(EXESUFFIX): examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(BIN_LDFLAGS)$(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ endif bin/libopenmpt_example_c_mem$(EXESUFFIX): examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ endif bin/libopenmpt_example_c_unsafe$(EXESUFFIX): examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ endif bin/libopenmpt_example_c_pipe$(EXESUFFIX): examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ endif bin/libopenmpt_example_c_stdout$(EXESUFFIX): examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ endif bin/libopenmpt_example_c_probe$(EXESUFFIX): examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ endif bin/libopenmpt_example_cxx$(EXESUFFIX): examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ ifeq ($(HOST),unix) $(SILENT)mv $@ $@.norpath $(INFO) [LD] $@ - $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ endif +.PHONY: cppcheck-libopenmpt +cppcheck-libopenmpt: + $(INFO) [CPPCHECK] libopenmpt + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) --check-config --suppress=unmatchedSuppression $(LIBOPENMPT_CXX_SOURCES) $(LIBOPENMPT_C_SOURCES) + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) $(LIBOPENMPT_CXX_SOURCES) $(LIBOPENMPT_C_SOURCES) + +.PHONY: cppcheck-libopenmpt-test +cppcheck-libopenmpt-test: + $(INFO) [CPPCHECK] libopenmpt-test + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) -DLIBOPENMPT_BUILD_TEST --check-config --suppress=unmatchedSuppression $(LIBOPENMPTTEST_CXX_SOURCES) $(LIBOPENMPTTEST_C_SOURCES) + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) -DLIBOPENMPT_BUILD_TEST $(LIBOPENMPTTEST_CXX_SOURCES) $(LIBOPENMPTTEST_C_SOURCES) + +.PHONY: cppcheck-openmpt123 +cppcheck-openmpt123: + $(INFO) [CPPCHECK] openmpt123 + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) --check-config --suppress=unmatchedSuppression $(OPENMPT123_CXX_SOURCES) + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) $(OPENMPT123_CXX_SOURCES) + +.PHONY: cppcheck +cppcheck: cppcheck-libopenmpt cppcheck-libopenmpt-test cppcheck-openmpt123 + .PHONY: clean clean: $(INFO) clean ... diff --git a/Frameworks/OpenMPT/OpenMPT/README.md b/Frameworks/OpenMPT/OpenMPT/README.md index 9dd48da4a..9c51819bf 100644 --- a/Frameworks/OpenMPT/OpenMPT/README.md +++ b/Frameworks/OpenMPT/OpenMPT/README.md @@ -1,10 +1,26 @@ - -README -====== - OpenMPT and libopenmpt ====================== +This repository contains OpenMPT, a free Windows/Wine-based +[tracker](https://en.wikipedia.org/wiki/Music_tracker) and libopenmpt, +a library to render tracker music (MOD, XM, S3M, IT MPTM and dozens of other +legacy formats) to a PCM audio stream. libopenmpt is directly based on OpenMPT, +offering the same playback quality and format support, and development of the +two happens in parallel. + + +License +------- + +The OpenMPT/libopenmpt project is distributed under the *BSD-3-Clause* License. +See [LICENSE](LICENSE) for the full license text. + +Files below the `include/` (external projects) and `contrib/` (related assets +not directly considered integral part of the project) folders may be subject to +other licenses. See the respective subfolders for license information. These +folders are not distributed in all source packages, and in particular they are +not distributed in the Autotools packages. + How to compile -------------- @@ -24,58 +40,35 @@ How to compile To compile the project, open `build/vs2017/OpenMPT.sln` and hit the compile button. + - OpenMPT requires the compile host system to be 64bit x86-64. + - The Windows 8.1 SDK and Microsoft Foundation Classes (MFC) are required to build OpenMPT (both are included with Visual Studio, however may need to be selected explicitly during setup). In order to build OpenMPT for Windows XP, the XP targetting toolset also needs to be installed. - - The VST and ASIO SDKs are needed for compiling with VST and ASIO support. + - The ASIO SDK is needed for compiling with ASIO support. - If you don't want this, uncomment `#define NO_VST` and comment out - `#define MPT_WITH_ASIO` in the file `common/BuildSettings.h`. + If you don't want this, comment out `#define MPT_WITH_ASIO` in the file + `common/BuildSettings.h`. - The ASIO and VST SDKs can be downloaded automatically on Windows 7 or later - with 7-Zip installed by just running the `build/download_externals.cmd` - script. + The ASIO SDK can be downloaded automatically on Windows 7 or later by just + running the `build/download_externals.cmd` script. If you do not want to or cannot use this script, you may follow these manual steps instead: - - ASIO: - - If you use `#define MPT_WITH_ASIO`, you will need to put the ASIO SDK in - the `include/ASIOSDK2` folder. The top level directory of the SDK is - already named `ASIOSDK2`, so simply move that directory in the include - folder. - - Please visit - [steinberg.net](http://www.steinberg.net/en/company/developers.html) to + - Visit + [steinberg.net](https://www.steinberg.net/en/company/developers.html) to download the SDK. - - VST: + - Put the ASIO SDK in the `include/ASIOSDK2` folder. The top level + directory of the SDK is already named `ASIOSDK2`, so simply move that + directory in the include folder. - If you don't use `#define NO_VST`, you will need to put the VST SDK in - the `include/vstsdk2.4` folder. - - Simply copy all files from the `VST3 SDK` folder in the SDK .zip file to - `include/vstsdk2.4/`. - - Note: OpenMPT makes use of the VST 2.4 specification only. The VST3 SDK - still contains all necessary files in the right locations. If you still - have the old VST 2.4 SDK laying around, this should also work fine. - - Please visit - [steinberg.net](http://www.steinberg.net/en/company/developers.html) to - download the SDK. - - If you need further help with the VST and ASIO SDKs, get in touch with the + If you need further help with the ASIO SDK, get in touch with the main OpenMPT developers. - - 7-Zip is required to be installed in the default path in order to build the - required files for OpenMPT Wine integration. - - Please visit [7-zip.org](http://www.7-zip.org/) to download 7-Zip. - ### libopenmpt and openmpt123 @@ -90,12 +83,31 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. make check sudo make install + Cross-compilation is generally supported (although only tested for + targetting MinGW-w64). + + Note that some MinGW-w64 distributions come with the `win32` threading model + enabled by default instead of the `posix` threading model. The `win32` + threading model lacks proper support for C++11 `` and `` as + well as thread-safe magic statics. It is recommended to use the `posix` + threading model for libopenmpt for this reason. On Debian, the appropriate + configure command is + `./configure --host=x86_64-w64-mingw32 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix` + for 64bit, or + `./configure --host=i686-w64-mingw32 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix` + for 32bit. Other MinGW-w64 distributions may differ. + - Visual Studio: - You will find solutions for Visual Studio 2015 to 2017 in the corresponding `build/vsVERSION/` folder. Projects that target Windows versions before Windows 7 are available in - `build/vsVERSIONxp/` + `build/vsVERSIONxp/`. + Projects that target Windows 10 1709 Desktop (10.0.16299.0, including + ARM and ARM64) or later versions are available in + `build/vsVERSIONwin10/`. + Minimal projects that target Windows 10 UWP are available in + `build/winstore82/`. Most projects are supported with any of the mentioned Visual Studio verions, with the following exceptions: @@ -103,10 +115,13 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - xmp-openmpt: Requires Visual Studio with MFC. + - libopenmpt requires the compile host system to be 64bit x86-64 when + building with Visual Studio. + - You will need the Winamp 5 SDK and the XMPlay SDK if you want to compile the plugins for these 2 players. They can be downloaded - automatically on Windows 7 or later with 7-Zip installed by just running - the `build/download_externals.cmd` script. + automatically on Windows 7 or later by just running the + `build/download_externals.cmd` script. If you do not want to or cannot use this script, you may follow these manual steps instead: @@ -126,7 +141,7 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. To build libopenmpt with XMPlay input plugin support, copy the contents of xmp-sdk.zip into include/xmplay/. - Please visit [un4seen.com](http://www.un4seen.com/xmplay.html) to + Please visit [un4seen.com](https://www.un4seen.com/xmplay.html) to download the SDK. You can disable xmp-openmpt in the solution configuration. @@ -147,13 +162,14 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. make CONFIG=mingw64-win64 # for win64 - - gcc or clang (on Unix-like systems, including Mac OS X with MacPorts): + - gcc or clang (on Unix-like systems, including Mac OS X with MacPorts, + and Haiku (32-bit Hybrid and 64-bit)): The minimum required compiler versions are: - gcc 4.8 - - clang 3.4 + - clang 3.6 The Makefile requires pkg-config for native builds. For sound output in openmpt123, PortAudio or SDL is required. @@ -176,27 +192,54 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - emscripten (on Unix-like systems): - libopenmpt has been tested and verified to work with emscripten 1.31 or - later (earlier versions might or might not work). + libopenmpt has been tested and verified to work with emscripten 1.38.5 + or later. Earlier versions are not supported. Run: - make CONFIG=emscripten # for emscripten >= 1.38.1 + # generates WebAssembly with dynamic heap growth + make CONFIG=emscripten EMSCRIPTEN_TARGET=wasm - make CONFIG=emscripten-old # for emscripten < 1.38.0 + or + + # generates asm.js with a fixed size 128MB heap + make CONFIG=emscripten EMSCRIPTEN_TARGET=asmjs128m + + or + + # generates asm.js with a fixed default size heap (as of Emscripten + # 1.38.11, this amounts to 16MB) + make CONFIG=emscripten EMSCRIPTEN_TARGET=asmjs + + or + + # generates JavaScript with dynamic heap growth and with + # compatibility for older VMs + make CONFIG=emscripten EMSCRIPTEN_TARGET=js Running the test suite on the command line is also supported by using - node.js. Version 0.10.25 or greater has been tested. Earlier versions + node.js. Version 8.9.1 or greater has been tested. Earlier versions might or might not work. Depending on how your distribution calls the `node.js` binary, you might have to edit - `build/make/config-emscripten.mk` or - `build/make/config-emscripten-old.mk`. + `build/make/config-emscripten.mk`. - - Haiku: + - DJGPP / DOS - To compile libopenmpt on Haiku (using the 32-bit gcc2h), run: + Cross-compilation from Linux systems is supported with DJGPP GCC 7.2 or + later via - make CONFIG=haiku + make CONFIG=djgpp + + `openmpt123` can use liballegro 4.2 for sound output on DJGPP/DOS. + liballegro can either be installed system-wide in the DJGPP environment + or downloaded into the `libopenmpt` source tree. + + make CONFIG=djgpp USE_ALLEGRO42=1 # use installed liballegro + + or + + ./build/download_externals.sh # download liballegro binaries + make CONFIG=djgpp USE_ALLEGRO42=1 BUNDLED_ALLEGRO42=1 - American Fuzzy Lop: @@ -204,11 +247,11 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. make CONFIG=afl - For more detailed instructions, read contrib/fuzzing/readme.md + For more detailed instructions, read `contrib/fuzzing/readme.md`. - other compilers: - To compiler libopenmpt with other C++11 compliant compilers, run: + To compile libopenmpt with other C++11 compliant compilers, run: make CONFIG=generic @@ -243,131 +286,9 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. -Coding conventions ------------------- +Contributing to OpenMPT/libopenmpt +---------------------------------- -### OpenMPT +See [contributing](doc/contributing.md). -(see below for an example) - -- Place curly braces at the beginning of the line, not at the end -- Generally make use of the custom index types like `SAMPLEINDEX` or - `ORDERINDEX` when referring to samples, orders, etc. -- When changing playback behaviour, make sure that you use the function - `CSoundFile::IsCompatibleMode()` so that modules made with previous versions - of MPT still sound correct (if the change is extremely small, this might be - unnecessary) -- `CamelCase` function and variable names are preferred. - -#### OpenMPT code example - -~~~~{.cpp} -void Foo::Bar(int foobar) -{ - while(true) - { - // some code - } -} -~~~~ - - -### libopenmpt - -**Note:** -**This applies to `libopenmpt/` and `openmpt123/` directories only.** -**Use OpenMPT style (see above) otherwise.** - -The code generally tries to follow these conventions, but they are not -strictly enforced and there are valid reasons to diverge from these -conventions. Using common sense is recommended. - - - In general, the most important thing is to keep style consistent with - directly surrounding code. - - Use C++ std types when possible, prefer `std::size_t` and `std::int32_t` - over `long` or `int`. Do not use C99 std types (e.g. no pure `int32_t`) - - Qualify namespaces explicitly, do not use `using`. - Members of `namespace openmpt` can be named without full namespace - qualification. - - Prefer the C++ version in `namespace std` if the same functionality is - provided by the C standard library as well. Also, include the C++ - version of C standard library headers (e.g. use `` instead of - ``. - - Do not use ANY locale-dependant C functions. For locale-dependant C++ - functionaly (especially iostream), always imbue the - `std::locale::classic()` locale. - - Prefer kernel_style_names over CamelCaseNames. - - If a folder (or one of its parent folders) contains .clang-format, - use clang-format v3.5 for indenting C++ and C files, otherwise: - - `{` are placed at the end of the opening line. - - Enclose even single statements in curly braces. - - Avoid placing single statements on the same line as the `if`. - - Opening parentheses are separated from keywords with a space. - - Opening parentheses are not separated from function names. - - Place spaces around operators and inside parentheses. - - Align `:` and `,` when inheriting or initializing members in a - constructor. - - The pointer `*` is separated from both the type and the variable name. - - Use tabs for identation, spaces for formatting. - Tabs should only appear at the very beginning of a line. - Do not assume any particular width of the TAB character. If width is - important for formatting reasons, use spaces. - - Use empty lines at will. - - API documentation is done with doxygen. - Use general C doxygen for the C API. - Use QT-style doxygen for the C++ API. - -#### libopenmpt indentation example - -~~~~{.cpp} -namespace openmpt { - -// This is totally meaningless code and just illustrates indentation. - -class foo - : public base - , public otherbase -{ - -private: - - std::int32_t x; - std::int16_t y; - -public: - - foo() - : x(0) - , y(-1) - { - return; - } - - int bar() const; - -}; // class foo - -int foo::bar() const { - - for ( int i = 0; i < 23; ++i ) { - switch ( x ) { - case 2: - something( y ); - break; - default: - something( ( y - 1 ) * 2 ); - break; - } - } - if ( x == 12 ) { - return -1; - } else if ( x == 42 ) { - return 1; - } - return 42; - -} - -} // namespace openmpt -~~~~ diff --git a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h index d6b64fbbe..6e1354d56 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h +++ b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h @@ -100,16 +100,17 @@ // OpenMPT-only dependencies #define MPT_WITH_ASIO -#define MPT_WITH_DSOUND +#define MPT_WITH_LAME #define MPT_WITH_LHASA #define MPT_WITH_MINIZIP +#define MPT_WITH_NLOHMANNJSON #define MPT_WITH_OPUS #define MPT_WITH_OPUSENC #define MPT_WITH_OPUSFILE -#define MPT_WITH_PICOJSON #define MPT_WITH_PORTAUDIO //#define MPT_WITH_PULSEAUDIO //#define MPT_WITH_PULSEAUDIOSIMPLE +#define MPT_WITH_RTAUDIO #define MPT_WITH_SMBPITCHSHIFT #define MPT_WITH_UNRAR #define MPT_WITH_VORBISENC @@ -142,27 +143,7 @@ #error "only one of LIBOPENMPT_BUILD_FULL or LIBOPENMPT_BUILD_SMALL can be defined" #endif // LIBOPENMPT_BUILD_FULL && LIBOPENMPT_BUILD_SMALL -#if defined(LIBOPENMPT_BUILD_FULL) - -//#define MPT_WITH_DL -//#define MPT_WITH_FLAC -//#define MPT_WITH_ICONV -//#define MPT_WITH_LTDL -#if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT -#if (_WIN32_WINNT >= 0x0601) -#define MPT_WITH_MEDIAFOUNDATION -#endif -#endif -//#define MPT_WITH_MINIMP3 -//#define MPT_WITH_MINIZ -#define MPT_WITH_MPG123 -#define MPT_WITH_OGG -//#define MPT_WITH_STBVORBIS -#define MPT_WITH_VORBIS -#define MPT_WITH_VORBISFILE -#define MPT_WITH_ZLIB - -#elif defined(LIBOPENMPT_BUILD_SMALL) +#if defined(LIBOPENMPT_BUILD_SMALL) //#define MPT_WITH_DL //#define MPT_WITH_FLAC @@ -201,11 +182,40 @@ #endif // MPT_BUILD_MSVC +#if defined(MPT_BUILD_XCODE) + +#if defined(MODPLUG_TRACKER) + +// n/a + +#endif // MODPLUG_TRACKER + +#if defined(LIBOPENMPT_BUILD) + +//#define MPT_WITH_DL +//#define MPT_WITH_FLAC +//#define MPT_WITH_ICONV +//#define MPT_WITH_LTDL +//#define MPT_WITH_MEDIAFOUNDATION +//#define MPT_WITH_MINIMP3 +//#define MPT_WITH_MINIZ +#define MPT_WITH_MPG123 +#define MPT_WITH_OGG +//#define MPT_WITH_STBVORBIS +#define MPT_WITH_VORBIS +#define MPT_WITH_VORBISFILE +#define MPT_WITH_ZLIB + +#endif // LIBOPENMPT_BUILD + +#endif // MPT_BUILD_XCODE + + #if defined(MODPLUG_TRACKER) // Enable built-in test suite. -#ifdef _DEBUG +#if defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_CHECKED) #define ENABLE_TESTS #endif @@ -214,13 +224,13 @@ // Disable any debug logging //#define NO_LOGGING -#if !defined(_DEBUG) && !defined(MPT_BUILD_WINESUPPORT) +#if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT) #define MPT_LOG_GLOBAL_LEVEL_STATIC #define MPT_LOG_GLOBAL_LEVEL 0 #endif // Disable all runtime asserts -#if !defined(_DEBUG) && !defined(MPT_BUILD_WINESUPPORT) +#if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT) #define NO_ASSERTS #endif @@ -284,7 +294,7 @@ #else #define MODPLUG_NO_FILESAVE #endif -#if defined(MPT_BUILD_ANALZYED) || defined(MPT_BUILD_CHECKED) || defined(ENABLE_TESTS) +#if defined(MPT_BUILD_ANALZYED) || defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_CHECKED) || defined(ENABLE_TESTS) // enable asserts #else #define NO_ASSERTS @@ -324,6 +334,9 @@ #if MPT_OS_WINDOWS #define MPT_CHARSET_WIN32 + #ifndef MPT_ENABLE_CHARSET_LOCALE + #define MPT_ENABLE_CHARSET_LOCALE + #endif #elif MPT_OS_LINUX @@ -354,6 +367,13 @@ //#define MPT_LOCALE_ASSUME_CHARSET CharsetUTF8 //#endif +#elif MPT_OS_DJGPP + + #define MPT_CHARSET_INTERNAL + #ifndef MPT_LOCALE_ASSUME_CHARSET + #define MPT_LOCALE_ASSUME_CHARSET CharsetCP437 + #endif + #elif defined(MPT_WITH_ICONV) #define MPT_CHARSET_ICONV @@ -362,7 +382,7 @@ -#if MPT_COMPILER_MSVC +#if MPT_COMPILER_MSVC && !defined(MPT_USTRING_MODE_UTF8_FORCE) // Use wide strings for MSVC because this is the native encoding on // microsoft platforms. @@ -418,13 +438,6 @@ // fixing stuff up -#if defined(MPT_BUILD_TARGET_XP) -// Also support Wine 1.6 in addition to Windows XP -#ifndef MPT_QUIRK_NO_CPP_THREAD -#define MPT_QUIRK_NO_CPP_THREAD -#endif -#endif - #if defined(MPT_BUILD_ANALYZED) || defined(MPT_BUILD_CHECKED) #ifdef NO_ASSERTS #undef NO_ASSERTS // static or dynamic analyzers want assertions on @@ -444,36 +457,45 @@ #if defined(ENABLE_ASM) #if MPT_COMPILER_MSVC && defined(_M_IX86) -// Generate general x86 inline assembly / intrinsics. +// Generate general x86 inline assembly and intrinsics. #define ENABLE_X86 -// Generate inline assembly using MMX instructions (only used when the CPU supports it). +// Generate MMX instructions (only used when the CPU supports it). #define ENABLE_MMX -// Generate inline assembly using SSE instructions (only used when the CPU supports it). +// Generate SSE instructions (only used when the CPU supports it). #define ENABLE_SSE -// Generate inline assembly using SSE2 instructions (only used when the CPU supports it). +// Generate SSE2 instructions (only used when the CPU supports it). #define ENABLE_SSE2 -// Generate inline assembly using SSE3 instructions (only used when the CPU supports it). +// Generate SSE3 instructions (only used when the CPU supports it). #define ENABLE_SSE3 -// Generate inline assembly using SSE4 instructions (only used when the CPU supports it). +// Generate SSE4 instructions (only used when the CPU supports it). #define ENABLE_SSE4 -// Generate inline assembly using AMD specific instruction set extensions (only used when the CPU supports it). + +#if defined(MPT_BUILD_TARGET_XP) +// Generate AMD specific instruction set extensions (only used when the CPU supports it). #define ENABLE_X86_AMD +#endif #elif MPT_COMPILER_MSVC && defined(_M_X64) -// Generate general x64 inline assembly / intrinsics. +// Generate general x64 intrinsics. #define ENABLE_X64 -// Generate inline assembly using SSE2 instructions (only used when the CPU supports it). +// Generate SSE instructions (only used when the CPU supports it). +#define ENABLE_SSE +// Generate SSE2 instructions (only used when the CPU supports it). #define ENABLE_SSE2 -// Generate inline assembly using SSE3 instructions (only used when the CPU supports it). +// Generate SSE3 instructions (only used when the CPU supports it). #define ENABLE_SSE3 -// Generate inline assembly using SSE4 instructions (only used when the CPU supports it). +// Generate SSE4 instructions (only used when the CPU supports it). #define ENABLE_SSE4 #endif // arch #endif // ENABLE_ASM -#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && !defined(LIBOPENMPT_NODELAYLOAD) && !MPT_OS_WINDOWS_WINRT +#if defined(MPT_WITH_LAME) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && defined(MODPLUG_TRACKER) && !MPT_OS_WINDOWS_WINRT +#define MPT_ENABLE_LAME_DELAYLOAD +#endif + +#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && defined(MODPLUG_TRACKER) && !MPT_OS_WINDOWS_WINRT #define MPT_ENABLE_MPG123_DELAYLOAD #endif @@ -490,6 +512,10 @@ #undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires Windows #endif +#if !MPT_COMPILER_MSVC && !MPT_COMPILER_CLANG && defined(MPT_WITH_MEDIAFOUNDATION) +#undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires MSVC or Clang due to ATL (no MinGW support) +#endif + #if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_TEMPFILE) #define MPT_ENABLE_TEMPFILE #endif @@ -498,6 +524,10 @@ #define MPT_ENABLE_TEMPFILE #endif +#if MPT_CXX_AT_LEAST(17) && defined(MPT_CHARSET_CODECVTUTF8) +#undef MPT_CHARSET_CODECVTUTF8 // std::codecvt_utf8 is deprecated in c++17 +#endif + #if !defined(MPT_CHARSET_WIN32) && !defined(MPT_CHARSET_ICONV) && !defined(MPT_CHARSET_CODECVTUTF8) && !defined(MPT_CHARSET_INTERNAL) #define MPT_CHARSET_INTERNAL #endif @@ -506,6 +536,10 @@ #define MPT_ENABLE_DYNBIND // Tracker requires dynamic library loading for export codecs #endif +#if defined(MPT_ENABLE_LAME_DELAYLOAD) && !defined(MPT_ENABLE_DYNBIND) +#define MPT_ENABLE_DYNBIND // static MSVC builds require dynbind to load delay-loaded DLLs +#endif + #if defined(MPT_ENABLE_MPG123_DELAYLOAD) && !defined(MPT_ENABLE_DYNBIND) #define MPT_ENABLE_DYNBIND // static MSVC builds require dynbind to load delay-loaded DLLs #endif @@ -538,10 +572,6 @@ #define MPT_ENABLE_FILEIO // External samples require disk file io #endif -#if !defined(MODPLUG_NO_FILESAVE) && !defined(MPT_ENABLE_FILEIO_STDIO) -#define MPT_ENABLE_FILEIO_STDIO // file saving requires FILE* -#endif - #if defined(NO_PLUGINS) // Any plugin type requires NO_PLUGINS to not be defined. #define NO_VST @@ -592,6 +622,16 @@ // platform configuration +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#if !defined(MPT_BUILD_WINESUPPORT) +#ifndef MPT_MFC_FULL +#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // Do not include support for MFC controls in dialogs (reduces binary bloat; remove this #define if you want to use MFC controls) +#endif // !MPT_MFC_FULL +#endif // !MPT_BUILD_WINESUPPORT +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + #if MPT_OS_WINDOWS #define WIN32_LEAN_AND_MEAN @@ -632,16 +672,23 @@ // stdlib configuration +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif + #define __STDC_CONSTANT_MACROS #define __STDC_LIMIT_MACROS #define _USE_MATH_DEFINES -#if !MPT_OS_ANDROID #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif -#endif // !MPT_OS_ANDROID + +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif @@ -682,30 +729,20 @@ #endif // MPT_COMPILER_MSVC -#if MPT_COMPILER_MSVCCLANGC2 - -#if MPT_OS_WINDOWS -// As Clang defines __STDC__ 1, Windows headers will use named union fields. The MediaFoundation headers do not support this, though. -// Clang supports nameless union fields just fine, and luckily there is a way to override the Windows headers behaviour. -#define _FORCENAMELESSUNION -#endif // MPT_OS_WINDOWS - -#endif // MPT_COMPILER_MSVCCLANGC2 - // third-party library configuration +#if defined(MODPLUG_TRACKER) +//#define MPT_MFC_FULL // use full MFC, including MFC controls +#endif + #ifdef MPT_WITH_FLAC #ifdef MPT_BUILD_MSVC_STATIC #define FLAC__NO_DLL #endif #endif -#ifdef MPT_WITH_PICOJSON -#define PICOJSON_USE_INT64 -#endif - #ifdef MPT_WITH_SMBPITCHSHIFT #ifdef MPT_BUILD_MSVC_SHARED #define SMBPITCHSHIFT_USE_DLL diff --git a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h index efcdea050..e04565dc5 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h +++ b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h @@ -25,10 +25,7 @@ #elif defined(__clang__) && defined(_MSC_VER) && defined(__c2__) -#define MPT_COMPILER_MSVCCLANGC2 1 -#define MPT_COMPILER_MSVCCLANGC2_VERSION (__c2_version__) -#define MPT_MSVCCLANGC2_AT_LEAST(major,minor,build) (MPT_COMPILER_MSVCCLANGC2_VERSION >= MPT_COMPILER_MAKE_VERSION3_BUILD((major),(minor),(build))) -#define MPT_MSVCCLANGC2_BEFORE(major,minor,build) (MPT_COMPILER_MSVCCLANGC2_VERSION < MPT_COMPILER_MAKE_VERSION3_BUILD((major),(minor),(build))) +#error "Clang/C2 is not supported. Please use Clang/LLVM for Windows instead." #elif defined(__clang__) @@ -37,11 +34,11 @@ #define MPT_CLANG_AT_LEAST(major,minor,patch) (MPT_COMPILER_CLANG_VERSION >= MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) #define MPT_CLANG_BEFORE(major,minor,patch) (MPT_COMPILER_CLANG_VERSION < MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) -#if MPT_CLANG_BEFORE(3,4,0) -#error "clang version 3.4 required" +#if MPT_CLANG_BEFORE(3,6,0) +#error "clang version 3.6 required" #endif -#if defined(__clang_analyzer__) +#if defined(__clang_analyzer__) #ifndef MPT_BUILD_ANALYZED #define MPT_BUILD_ANALYZED #endif @@ -61,7 +58,9 @@ #elif defined(_MSC_VER) #define MPT_COMPILER_MSVC 1 -#if (_MSC_VER >= 1915) +#if (_MSC_VER >= 1916) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,9) +#elif (_MSC_VER >= 1915) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,8) #elif (_MSC_VER >= 1914) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,7) @@ -73,6 +72,8 @@ #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,3) #elif (_MSC_VER >= 1910) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,0) +#elif (_MSC_VER >= 1900) && defined(_MSVC_LANG) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015,3) #elif (_MSC_VER >= 1900) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015,0) #elif (_MSC_VER >= 1800) @@ -110,11 +111,6 @@ #ifndef MPT_COMPILER_GENERIC #define MPT_COMPILER_GENERIC 0 #endif -#ifndef MPT_COMPILER_MSVCCLANGC2 -#define MPT_COMPILER_MSVCCLANGC2 0 -#define MPT_MSVCCLANGC2_AT_LEAST(major,minor,build) 0 -#define MPT_MSVCCLANGC2_BEFORE(major,minor,build) 0 -#endif #ifndef MPT_COMPILER_CLANG #define MPT_COMPILER_CLANG 0 #define MPT_CLANG_AT_LEAST(major,minor,patch) 0 @@ -145,8 +141,10 @@ #elif MPT_COMPILER_MSVC -#if MPT_MSVC_AT_LEAST(2017,0) -#if (_MSVC_LANG >= 201402) +#if MPT_MSVC_AT_LEAST(2015,3) +#if (_MSVC_LANG >= 201703) +#define MPT_CXX 17 +#elif (_MSVC_LANG >= 201402) #define MPT_CXX 14 #else #define MPT_CXX 11 @@ -171,58 +169,6 @@ -#if MPT_COMPILER_MSVC - #define MPT_PLATFORM_LITTLE_ENDIAN -#elif MPT_COMPILER_GCC - #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - #define MPT_PLATFORM_BIG_ENDIAN - #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - #define MPT_PLATFORM_LITTLE_ENDIAN - #endif -#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 - #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - #define MPT_PLATFORM_BIG_ENDIAN - #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - #define MPT_PLATFORM_LITTLE_ENDIAN - #endif -#endif - -// fallback: -#if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN) - // taken from boost/detail/endian.hpp - #if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \ - || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ - || (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN)) - #define MPT_PLATFORM_BIG_ENDIAN - #elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \ - || (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ - || (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN)) - #define MPT_PLATFORM_LITTLE_ENDIAN - #elif defined(__sparc) || defined(__sparc__) \ - || defined(_POWER) || defined(__powerpc__) \ - || defined(__ppc__) || defined(__hpux) || defined(__hppa) \ - || defined(_MIPSEB) || defined(_POWER) \ - || defined(__s390__) - #define MPT_PLATFORM_BIG_ENDIAN - #elif defined(__i386__) || defined(__alpha__) \ - || defined(__ia64) || defined(__ia64__) \ - || defined(_M_IX86) || defined(_M_IA64) \ - || defined(_M_ALPHA) || defined(__amd64) \ - || defined(__amd64__) || defined(_M_AMD64) \ - || defined(__x86_64) || defined(__x86_64__) \ - || defined(_M_X64) || defined(__bfin__) - #define MPT_PLATFORM_LITTLE_ENDIAN - #endif -#endif - -#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN) -#define MPT_PLATFORM_ENDIAN_KNOWN 1 -#else -#define MPT_PLATFORM_ENDIAN_KNOWN 0 -#endif - - - // This should really be based on __STDCPP_THREADS__, but that is not defined by // GCC or clang. Stupid. // Just assume multithreaded and disable for platforms we know are @@ -235,14 +181,6 @@ -// C++11 constexpr - -#if MPT_COMPILER_MSVC -#define MPT_COMPILER_QUIRK_CONSTEXPR_NO_STRING_LITERALS -#endif - - - #if MPT_COMPILER_MSVC // Compiler has multiplication/division semantics when shifting signed integers. #define MPT_COMPILER_SHIFT_SIGNED 1 @@ -254,37 +192,19 @@ -#if MPT_COMPILER_GCC || MPT_COMPILER_MSVC -// Compiler supports type-punning through unions. This is not stricly standard-conforming. -// For GCC, this is documented, for MSVC this is apparently not documented, but we assume it. -#define MPT_COMPILER_UNION_TYPE_ALIASES 1 -#endif - -#ifndef MPT_COMPILER_UNION_TYPE_ALIASES -// Compiler does not support type-punning through unions. std::memcpy is used instead. -// This is the safe fallback and strictly standard-conforming. -// Another standard-compliant alternative would be casting pointers to a character type pointer. -// This results in rather unreadable code and, -// in most cases, compilers generate better code by just inlining the memcpy anyway. -// (see ). -#define MPT_COMPILER_UNION_TYPE_ALIASES 0 -#endif - - - // The order of the checks matters! -#if defined(__EMSCRIPTEN__) +#if defined(__DJGPP__) + #define MPT_OS_DJGPP 1 +#elif defined(__EMSCRIPTEN__) #define MPT_OS_EMSCRIPTEN 1 #if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__) #if (__EMSCRIPTEN_major__ > 1) - #define MPT_OS_EMSCRIPTEN_ANCIENT 0 - #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ >= 36) - #define MPT_OS_EMSCRIPTEN_ANCIENT 0 + // ok + #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ >= 38) + // ok #else - #define MPT_OS_EMSCRIPTEN_ANCIENT 1 + #error "Emscripten >= 1.38 is required." #endif - #else - #define MPT_OS_EMSCRIPTEN_ANCIENT 1 #endif #elif defined(_WIN32) #define MPT_OS_WINDOWS 1 @@ -324,6 +244,9 @@ #define MPT_OS_UNKNOWN 1 #endif +#ifndef MPT_OS_DJGPP +#define MPT_OS_DJGPP 0 +#endif #ifndef MPT_OS_EMSCRIPTEN #define MPT_OS_EMSCRIPTEN 0 #endif @@ -367,7 +290,18 @@ -#if MPT_OS_EMSCRIPTEN +#if (MPT_OS_DJGPP || MPT_OS_EMSCRIPTEN) #undef MPT_PLATFORM_MULTITHREADED #define MPT_PLATFORM_MULTITHREADED 0 #endif + +#if MPT_OS_DJGPP +#define MPT_COMPILER_QUIRK_NO_WCHAR +#endif + +#if MPT_MSVC_BEFORE(2017,8) +// fixed in VS2017 15.8 +// see +#define MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM +#endif + diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp index ebe158bac..5ba5a4d5b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp @@ -190,7 +190,7 @@ std::string ComponentFactoryBase::GetSettingsKey() const void ComponentFactoryBase::PreConstruct() const { MPT_LOG(LogInformation, "Components", - mpt::format(MPT_USTRING("Constructing Component %1")) + mpt::format(U_("Constructing Component %1")) ( mpt::ToUnicode(mpt::CharsetASCII, m_ID) ) ); @@ -245,7 +245,7 @@ static std::shared_ptr g_ComponentManager; void ComponentManager::Init(const IComponentManagerSettings &settings) { - MPT_LOG(LogInformation, "Components", MPT_USTRING("Init")); + MPT_LOG(LogInformation, "Components", U_("Init")); // cannot use make_shared because the constructor is private g_ComponentManager = std::shared_ptr(new ComponentManager(settings)); } @@ -253,7 +253,7 @@ void ComponentManager::Init(const IComponentManagerSettings &settings) void ComponentManager::Release() { - MPT_LOG(LogInformation, "Components", MPT_USTRING("Release")); + MPT_LOG(LogInformation, "Components", U_("Release")); g_ComponentManager = nullptr; } @@ -292,7 +292,7 @@ void ComponentManager::Register(const IComponentFactory &componentFactory) void ComponentManager::Startup() { - MPT_LOG(LogDebug, "Components", MPT_USTRING("Startup")); + MPT_LOG(LogDebug, "Components", U_("Startup")); if(m_Settings.LoadOnStartup()) { for(auto &it : m_Components) @@ -313,6 +313,10 @@ void ComponentManager::Startup() bool ComponentManager::IsComponentBlocked(const std::string &settingsKey) const { + if(settingsKey.empty()) + { + return false; + } return m_Settings.IsBlocked(settingsKey); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h index 2a2b79903..6586cae61 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include #include "../common/misc_util.h" @@ -53,11 +55,11 @@ class IComponent protected: - IComponent() { } + IComponent() = default; public: - virtual ~IComponent() { } + virtual ~IComponent() = default; public: @@ -98,15 +100,15 @@ protected: public: - virtual ComponentType GetType() const; - virtual bool IsInitialized() const; - virtual bool IsAvailable() const; + ComponentType GetType() const override; + bool IsInitialized() const override; + bool IsAvailable() const override; - virtual mpt::ustring GetVersion() const; + mpt::ustring GetVersion() const override; public: - virtual void Initialize(); + void Initialize() override; protected: @@ -123,7 +125,7 @@ public: { return; } - virtual bool DoInitialize() + bool DoInitialize() override { return true; } @@ -175,7 +177,7 @@ public: protected: - virtual bool DoInitialize() = 0; + bool DoInitialize() override = 0; }; @@ -185,6 +187,18 @@ protected: #define MPT_COMPONENT_BIND_SYMBOL(libName, symbol, func) MPT_DO { if(!Bind( func , libName , symbol )) { SetBindFailed(); } } MPT_WHILE_0 #define MPT_COMPONENT_BIND_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol ) +#if MPT_OS_WINDOWS +#ifdef UNICODE +#define MPT_COMPONENT_BINDWIN_SUFFIX "W" +#else +#define MPT_COMPONENT_BINDWIN_SUFFIX "A" +#endif +#define MPT_COMPONENT_BINDWIN(libName, func) MPT_DO { if(!Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BINDWIN_OPTIONAL(libName, func) Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX ) +#define MPT_COMPONENT_BINDWIN_SYMBOL(libName, symbol, func) MPT_DO { if(!Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BINDWIN_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX ) +#endif + class ComponentSystemDLL : public ComponentLibrary { @@ -197,7 +211,7 @@ public: { return; } - virtual bool DoInitialize() + bool DoInitialize() override { AddLibrary(m_BaseName.ToUTF8(), mpt::LibraryPath::System(m_BaseName)); return GetLibrary(m_BaseName.ToUTF8()).IsValid(); @@ -216,7 +230,7 @@ public: { return; } - virtual bool DoInitialize() + bool DoInitialize() override { AddLibrary(m_FullName.ToUTF8(), mpt::LibraryPath::AppFullName(m_FullName)); return GetLibrary(m_FullName.ToUTF8()).IsValid(); @@ -238,9 +252,9 @@ typedef std::shared_ptr (*ComponentFactoryMethod)(ComponentManager & class IComponentFactory { protected: - IComponentFactory() { } + IComponentFactory() = default; public: - virtual ~IComponentFactory() { } + virtual ~IComponentFactory() = default; public: virtual std::string GetID() const = 0; virtual std::string GetSettingsKey() const = 0; @@ -261,10 +275,10 @@ protected: void Initialize(ComponentManager &componentManager, std::shared_ptr component) const; public: virtual ~ComponentFactoryBase(); - virtual std::string GetID() const; - virtual std::string GetSettingsKey() const; - virtual std::shared_ptr Construct(ComponentManager &componentManager) const = 0; - virtual ComponentFactoryMethod GetStaticConstructor() const = 0; + std::string GetID() const override; + std::string GetSettingsKey() const override; + std::shared_ptr Construct(ComponentManager &componentManager) const override = 0; + ComponentFactoryMethod GetStaticConstructor() const override = 0; }; @@ -278,12 +292,8 @@ public: { return; } - virtual ~ComponentFactory() - { - return; - } public: - virtual std::shared_ptr Construct(ComponentManager &componentManager) const + std::shared_ptr Construct(ComponentManager &componentManager) const override { PreConstruct(); std::shared_ptr component = std::make_shared(); @@ -294,7 +304,7 @@ public: { return ComponentFactory().Construct(componentManager); } - virtual ComponentFactoryMethod GetStaticConstructor() const + virtual ComponentFactoryMethod GetStaticConstructor() const override { return &StaticConstruct; } @@ -315,10 +325,10 @@ class ComponentManagerSettingsDefault : public IComponentManagerSettings { public: - virtual bool LoadOnStartup() const { return false; } - virtual bool KeepLoaded() const { return true; } - virtual bool IsBlocked(const std::string & /*key*/ ) const { return false; } - virtual mpt::PathString Path() const { return mpt::PathString(); } + bool LoadOnStartup() const override { return false; } + bool KeepLoaded() const override { return true; } + bool IsBlocked(const std::string & /*key*/ ) const override { return false; } + mpt::PathString Path() const override { return mpt::PathString(); } }; diff --git a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h index 7599908db..1960f1884 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h @@ -10,41 +10,98 @@ #pragma once +#include "BuildSettings.h" + +#include +#include + #include #include + #include #include + #if MPT_COMPILER_MSVC #include #endif -// Platform has native IEEE floating point representation _AND_ floating point -// endianess is the same as integer endianness. -// We just test __STDC_IEC_559__ for now. -#if MPT_COMPILER_GENERIC - #define MPT_PLATFORM_IEEE_FLOAT 0 -#elif MPT_COMPILER_MSVC - #define MPT_PLATFORM_IEEE_FLOAT 1 -#else // MPT_COMPILER - #if MPT_PLATFORM_ENDIAN_KNOWN - #if defined(__STDC_IEC_559__) - #if (__STDC_IEC_559__) - #define MPT_PLATFORM_IEEE_FLOAT 1 - #else - #define MPT_PLATFORM_IEEE_FLOAT 0 - #endif - #else - #define MPT_PLATFORM_IEEE_FLOAT 0 - #endif - #else - #define MPT_PLATFORM_IEEE_FLOAT 0 - #endif -#endif // MPT_COMPILER +#if MPT_CXX_AT_LEAST(20) -#if !MPT_PLATFORM_IEEE_FLOAT -#include +// nothing + +#elif MPT_COMPILER_GENERIC + +// rely on runtime detection instead of using non-standard macros + +#else + +#if MPT_COMPILER_MSVC + #define MPT_PLATFORM_LITTLE_ENDIAN +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define MPT_PLATFORM_BIG_ENDIAN + #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define MPT_PLATFORM_LITTLE_ENDIAN + #endif +#endif + +// fallback: +#if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN) + // taken from boost/detail/endian.hpp + #if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \ + || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ + || (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN)) + #define MPT_PLATFORM_BIG_ENDIAN + #elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \ + || (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ + || (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN)) + #define MPT_PLATFORM_LITTLE_ENDIAN + #elif defined(__sparc) || defined(__sparc__) \ + || defined(_POWER) || defined(__powerpc__) \ + || defined(__ppc__) || defined(__hpux) || defined(__hppa) \ + || defined(_MIPSEB) || defined(_POWER) \ + || defined(__s390__) + #define MPT_PLATFORM_BIG_ENDIAN + #elif defined(__i386__) || defined(__alpha__) \ + || defined(__ia64) || defined(__ia64__) \ + || defined(_M_IX86) || defined(_M_IA64) \ + || defined(_M_ALPHA) || defined(__amd64) \ + || defined(__amd64__) || defined(_M_AMD64) \ + || defined(__x86_64) || defined(__x86_64__) \ + || defined(_M_X64) || defined(__bfin__) + #define MPT_PLATFORM_LITTLE_ENDIAN + #endif +#endif + +#endif + +#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN) +#define MPT_PLATFORM_ENDIAN_KNOWN 1 +#else +#define MPT_PLATFORM_ENDIAN_KNOWN 0 +#endif + + + +#if MPT_PLATFORM_ENDIAN_KNOWN && MPT_CXX_AT_LEAST(14) +//#define MPT_ENDIAN_IS_CONSTEXPR 1 +// For now, we do not want to use constexpr endianness functions and types. +// It bloats the binary size somewhat (possibly because of either the zeroing +// constructor or because of not being able to use byteswap intrinsics) and has +// currently no compelling benefit for us +#define MPT_ENDIAN_IS_CONSTEXPR 0 +#else +#define MPT_ENDIAN_IS_CONSTEXPR 0 +#endif + +#if MPT_ENDIAN_IS_CONSTEXPR +#define MPT_ENDIAN_CONSTEXPR_FUN MPT_CONSTEXPR14_FUN +#define MPT_ENDIAN_CONSTEXPR_VAR MPT_CONSTEXPR14_VAR +#else +#define MPT_ENDIAN_CONSTEXPR_FUN MPT_FORCEINLINE +#define MPT_ENDIAN_CONSTEXPR_VAR const #endif @@ -52,86 +109,159 @@ OPENMPT_NAMESPACE_BEGIN + namespace mpt { -struct endian_type { uint16 value; }; -static MPT_FORCEINLINE bool operator == (const endian_type & a, const endian_type & b) { return a.value == b.value; } -static MPT_FORCEINLINE bool operator != (const endian_type & a, const endian_type & b) { return a.value != b.value; } -static const endian_type endian_big = { 0x1234u }; -static const endian_type endian_little = { 0x3412u }; + +// C++20 std::endian +#if MPT_CXX_AT_LEAST(20) +using std::endian; +#else // !C++20 +enum class endian +{ + little = 0x78563412u, + big = 0x12345678u, + weird = 1u, +#if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) + native = little +#elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) + native = big +#else + native = 0u +#endif +}; +#endif // C++20 + +MPT_CONSTEXPR11_FUN bool endian_known() noexcept +{ + return ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)); +} + +MPT_CONSTEXPR11_FUN bool endian_unknown() noexcept +{ + return ((mpt::endian::native != mpt::endian::little) && (mpt::endian::native != mpt::endian::big)); +} + + + +#if MPT_CXX_AT_LEAST(20) + +static MPT_CONSTEXPR11_FUN mpt::endian get_endian() noexcept +{ + return mpt::endian::native; +} + +static MPT_CONSTEXPR11_FUN bool endian_is_little() noexcept +{ + return get_endian() == mpt::endian::little; +} + +static MPT_CONSTEXPR11_FUN bool endian_is_big() noexcept +{ + return get_endian() == mpt::endian::big; +} + +static MPT_CONSTEXPR11_FUN bool endian_is_weird() noexcept +{ + return !endian_is_little() && !endian_is_big(); +} + +#else // !C++20 namespace detail { - static MPT_FORCEINLINE endian_type endian_probe() + + static MPT_FORCEINLINE mpt::endian endian_probe() noexcept { - STATIC_ASSERT(sizeof(endian_type) == 2); - const mpt::byte probe[2] = { 0x12, 0x34 }; - endian_type test; - std::memcpy(&test, probe, 2); - return test; + typedef uint32 endian_probe_type; + MPT_STATIC_ASSERT(sizeof(endian_probe_type) == 4); + constexpr endian_probe_type endian_probe_big = 0x12345678u; + constexpr endian_probe_type endian_probe_little = 0x78563412u; + const mpt::byte probe[sizeof(endian_probe_type)] = { mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; + endian_probe_type test; + std::memcpy(&test, probe, sizeof(endian_probe_type)); + mpt::endian result = mpt::endian::native; + switch(test) + { + case endian_probe_big: + result = mpt::endian::big; + break; + case endian_probe_little: + result = mpt::endian::little; + break; + default: + result = mpt::endian::weird; + break; + } + return result; + } + +} + +static MPT_FORCEINLINE mpt::endian get_endian() noexcept +{ + MPT_CONSTANT_IF(mpt::endian_known()) + { + return mpt::endian::native; + } else + { + return detail::endian_probe(); } } -static MPT_FORCEINLINE endian_type endian() +static MPT_FORCEINLINE bool endian_is_little() noexcept { - #if defined(MPT_PLATFORM_LITTLE_ENDIAN) - return endian_little; - #elif defined(MPT_PLATFORM_BIG_ENDIAN) - return endian_big; - #else - return detail::endian_probe(); - #endif + return get_endian() == mpt::endian::little; } -static MPT_FORCEINLINE bool endian_is_little() +static MPT_FORCEINLINE bool endian_is_big() noexcept { - return endian() == endian_little; + return get_endian() == mpt::endian::big; } -static MPT_FORCEINLINE bool endian_is_big() +static MPT_FORCEINLINE bool endian_is_weird() noexcept { - return endian() == endian_big; + return !endian_is_little() && !endian_is_big(); +} + +#endif // C++20 + + + +} // namespace mpt + + + +struct BigEndian_tag +{ + static MPT_CONSTEXPR11_VAR mpt::endian endian = mpt::endian::big; +}; + +struct LittleEndian_tag +{ + static MPT_CONSTEXPR11_VAR mpt::endian endian = mpt::endian::little; +}; + + + +namespace mpt { + +template +inline void SwapBufferEndian(std::size_t elementSize, Tbyte * buffer, std::size_t elements) +{ + MPT_STATIC_ASSERT(sizeof(Tbyte) == 1); + for(std::size_t element = 0; element < elements; ++element) + { + std::reverse(&buffer[0], &buffer[elementSize]); + buffer += elementSize; + } } } // namespace mpt -namespace mpt { namespace detail { -enum Endianness -{ - BigEndian, - LittleEndian, -#if MPT_PLATFORM_ENDIAN_KNOWN -#if defined(MPT_PLATFORM_BIG_ENDIAN) - NativeEndian = BigEndian, -#else - NativeEndian = LittleEndian, -#endif -#endif -}; -} } // namespace mpt::detail - -struct BigEndian_tag -{ - static const mpt::detail::Endianness Endianness = mpt::detail::BigEndian; -}; - -struct LittleEndian_tag -{ - static const mpt::detail::Endianness Endianness = mpt::detail::LittleEndian; -}; - - - -// Ending swaps: -// SwapBytesBE(x) and variants may be used either to: -// -Convert integer x, which is in big endian format (for example read from file), -// to endian format of current architecture. -// -Convert value x from endian format of current architecture to big endian format. -// Similarly SwapBytesLE(x) converts known little endian format to format of current -// endian architecture or value x in format of current architecture to little endian -// format. +#if !MPT_ENDIAN_IS_CONSTEXPR #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 @@ -165,6 +295,8 @@ static MPT_FORCEINLINE uint64 mpt_bswap64(uint64 x) { return bswap64(x); } #endif } } // namespace mpt::detail +#endif // !MPT_ENDIAN_IS_CONSTEXPR + // No intrinsics available #ifndef MPT_bswap16 @@ -201,33 +333,10 @@ static MPT_FORCEINLINE uint64 mpt_bswap64(uint64 x) { return bswap64(x); } #endif -#if MPT_PLATFORM_ENDIAN_KNOWN - -#if defined(MPT_PLATFORM_BIG_ENDIAN) - -#define MPT_bswap64le(x) MPT_bswap64(x) -#define MPT_bswap32le(x) MPT_bswap32(x) -#define MPT_bswap16le(x) MPT_bswap16(x) -#define MPT_bswap64be(x) (x) -#define MPT_bswap32be(x) (x) -#define MPT_bswap16be(x) (x) - -#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) - -#define MPT_bswap64be(x) MPT_bswap64(x) -#define MPT_bswap32be(x) MPT_bswap32(x) -#define MPT_bswap16be(x) MPT_bswap16(x) -#define MPT_bswap64le(x) (x) -#define MPT_bswap32le(x) (x) -#define MPT_bswap16le(x) (x) - -#endif - -#else // !MPT_PLATFORM_ENDIAN_KNOWN - template -static MPT_FORCEINLINE std::array EndianEncode(T val) +static MPT_CONSTEXPR17_FUN std::array EndianEncode(T val) noexcept { + MPT_STATIC_ASSERT(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); STATIC_ASSERT(std::numeric_limits::is_integer); STATIC_ASSERT(!std::numeric_limits::is_signed); STATIC_ASSERT(sizeof(T) == size); @@ -236,7 +345,7 @@ static MPT_FORCEINLINE std::array EndianEncode(T val) typedef Tendian endian_type; unsigned_base_type uval = static_cast(val); std::array data; - MPT_CONSTANT_IF(endian_type::Endianness == mpt::detail::LittleEndian) + MPT_CONSTANT_IF(endian_type::endian == mpt::endian::little) { for(std::size_t i = 0; i < sizeof(base_type); ++i) { @@ -253,8 +362,9 @@ static MPT_FORCEINLINE std::array EndianEncode(T val) } template -static MPT_FORCEINLINE T EndianDecode(std::array data) +static MPT_CONSTEXPR17_FUN T EndianDecode(std::array data) noexcept { + MPT_STATIC_ASSERT(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); STATIC_ASSERT(std::numeric_limits::is_integer); STATIC_ASSERT(!std::numeric_limits::is_signed); STATIC_ASSERT(sizeof(T) == size); @@ -263,7 +373,7 @@ static MPT_FORCEINLINE T EndianDecode(std::array data) typedef Tendian endian_type; base_type val = base_type(); unsigned_base_type uval = unsigned_base_type(); - MPT_CONSTANT_IF(endian_type::Endianness == mpt::detail::LittleEndian) + MPT_CONSTANT_IF(endian_type::endian == mpt::endian::little) { for(std::size_t i = 0; i < sizeof(base_type); ++i) { @@ -280,64 +390,30 @@ static MPT_FORCEINLINE T EndianDecode(std::array data) return val; } -template -static MPT_FORCEINLINE T MPT_bswap_impl(T val) + +namespace mpt +{ +namespace detail { - typedef typename std::make_unsigned::type Tu; - std::array data = EndianEncode(val); - std::memcpy(&val, data.data(), sizeof(T)); - return val; -} -#define MPT_bswap64be(x) MPT_bswap_impl(x) -#define MPT_bswap32be(x) MPT_bswap_impl(x) -#define MPT_bswap16be(x) MPT_bswap_impl(x) -#define MPT_bswap64le(x) MPT_bswap_impl(x) -#define MPT_bswap32le(x) MPT_bswap_impl(x) -#define MPT_bswap16le(x) MPT_bswap_impl(x) - -#endif // MPT_PLATFORM_ENDIAN_KNOWN - -static MPT_FORCEINLINE uint64 SwapBytesBE(uint64 value) { return MPT_bswap64be(value); } -static MPT_FORCEINLINE uint32 SwapBytesBE(uint32 value) { return MPT_bswap32be(value); } -static MPT_FORCEINLINE uint16 SwapBytesBE(uint16 value) { return MPT_bswap16be(value); } -static MPT_FORCEINLINE uint64 SwapBytesLE(uint64 value) { return MPT_bswap64le(value); } -static MPT_FORCEINLINE uint32 SwapBytesLE(uint32 value) { return MPT_bswap32le(value); } -static MPT_FORCEINLINE uint16 SwapBytesLE(uint16 value) { return MPT_bswap16le(value); } -static MPT_FORCEINLINE int64 SwapBytesBE(int64 value) { return MPT_bswap64be(value); } -static MPT_FORCEINLINE int32 SwapBytesBE(int32 value) { return MPT_bswap32be(value); } -static MPT_FORCEINLINE int16 SwapBytesBE(int16 value) { return MPT_bswap16be(value); } -static MPT_FORCEINLINE int64 SwapBytesLE(int64 value) { return MPT_bswap64le(value); } -static MPT_FORCEINLINE int32 SwapBytesLE(int32 value) { return MPT_bswap32le(value); } -static MPT_FORCEINLINE int16 SwapBytesLE(int16 value) { return MPT_bswap16le(value); } +static MPT_ENDIAN_CONSTEXPR_FUN uint64 SwapBytes(uint64 value) noexcept { return MPT_bswap64(value); } +static MPT_ENDIAN_CONSTEXPR_FUN uint32 SwapBytes(uint32 value) noexcept { return MPT_bswap32(value); } +static MPT_ENDIAN_CONSTEXPR_FUN uint16 SwapBytes(uint16 value) noexcept { return MPT_bswap16(value); } +static MPT_ENDIAN_CONSTEXPR_FUN int64 SwapBytes(int64 value) noexcept { return MPT_bswap64(value); } +static MPT_ENDIAN_CONSTEXPR_FUN int32 SwapBytes(int32 value) noexcept { return MPT_bswap32(value); } +static MPT_ENDIAN_CONSTEXPR_FUN int16 SwapBytes(int16 value) noexcept { return MPT_bswap16(value); } // Do NOT remove these overloads, even if they seem useless. // We do not want risking to extend 8bit integers to int and then // endian-converting and casting back to int. // Thus these overloads. -static MPT_FORCEINLINE uint8 SwapBytesLE(uint8 value) { return value; } -static MPT_FORCEINLINE int8 SwapBytesLE(int8 value) { return value; } -static MPT_FORCEINLINE char SwapBytesLE(char value) { return value; } -static MPT_FORCEINLINE uint8 SwapBytesBE(uint8 value) { return value; } -static MPT_FORCEINLINE int8 SwapBytesBE(int8 value) { return value; } -static MPT_FORCEINLINE char SwapBytesBE(char value) { return value; } +static MPT_ENDIAN_CONSTEXPR_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } +static MPT_ENDIAN_CONSTEXPR_FUN int8 SwapBytes(int8 value) noexcept { return value; } +static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return value; } -static MPT_FORCEINLINE uint64 SwapBytes(uint64 value) { return MPT_bswap64(value); } -static MPT_FORCEINLINE uint32 SwapBytes(uint32 value) { return MPT_bswap32(value); } -static MPT_FORCEINLINE uint16 SwapBytes(uint16 value) { return MPT_bswap16(value); } -static MPT_FORCEINLINE int64 SwapBytes(int64 value) { return MPT_bswap64(value); } -static MPT_FORCEINLINE int32 SwapBytes(int32 value) { return MPT_bswap32(value); } -static MPT_FORCEINLINE int16 SwapBytes(int16 value) { return MPT_bswap16(value); } -static MPT_FORCEINLINE uint8 SwapBytes(uint8 value) { return value; } -static MPT_FORCEINLINE int8 SwapBytes(int8 value) { return value; } -static MPT_FORCEINLINE char SwapBytes(char value) { return value; } +} // namespace detail +} // namespace mpt -#undef MPT_bswap16le -#undef MPT_bswap32le -#undef MPT_bswap64le -#undef MPT_bswap16be -#undef MPT_bswap32be -#undef MPT_bswap64be #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 @@ -346,161 +422,121 @@ static MPT_FORCEINLINE char SwapBytes(char value) { return value; } // 1.0f --> 0x3f800000u static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) { -#if MPT_PLATFORM_IEEE_FLOAT - STATIC_ASSERT(sizeof(uint32) == sizeof(float32)); - #if MPT_COMPILER_UNION_TYPE_ALIASES - union { - float32 f; - uint32 i; - } conv; - conv.f = f; - return conv.i; - #else - uint32 i = 0; - std::memcpy(&i, &f, sizeof(float32)); - return i; - #endif -#else - int e = 0; - float m = std::frexp(f, &e); - if(e == 0 && std::fabs(m) == 0.0f) + MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) { - uint32 expo = 0u; - uint32 sign = std::signbit(m) ? 0x01u : 0x00u; - uint32 mant = 0u; - uint32 i = 0u; - i |= (mant << 0) & 0x007fffffu; - i |= (expo << 23) & 0x7f800000u; - i |= (sign << 31) & 0x80000000u; - return i; + return mpt::bit_cast(f); } else { - uint32 expo = e + 127 - 1; - uint32 sign = std::signbit(m) ? 0x01u : 0x00u; - uint32 mant = static_cast(std::fabs(std::ldexp(m, 24))); - uint32 i = 0u; - i |= (mant << 0) & 0x007fffffu; - i |= (expo << 23) & 0x7f800000u; - i |= (sign << 31) & 0x80000000u; - return i; + int e = 0; + float m = std::frexp(f, &e); + if(e == 0 && std::fabs(m) == 0.0f) + { + uint32 expo = 0u; + uint32 sign = std::signbit(m) ? 0x01u : 0x00u; + uint32 mant = 0u; + uint32 i = 0u; + i |= (mant << 0) & 0x007fffffu; + i |= (expo << 23) & 0x7f800000u; + i |= (sign << 31) & 0x80000000u; + return i; + } else + { + uint32 expo = e + 127 - 1; + uint32 sign = std::signbit(m) ? 0x01u : 0x00u; + uint32 mant = static_cast(std::fabs(std::ldexp(m, 24))); + uint32 i = 0u; + i |= (mant << 0) & 0x007fffffu; + i |= (expo << 23) & 0x7f800000u; + i |= (sign << 31) & 0x80000000u; + return i; + } } -#endif } static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) { -#if MPT_PLATFORM_IEEE_FLOAT - STATIC_ASSERT(sizeof(uint64) == sizeof(float64)); - #if MPT_COMPILER_UNION_TYPE_ALIASES - union { - float64 f; - uint64 i; - } conv; - conv.f = f; - return conv.i; - #else - uint64 i = 0; - std::memcpy(&i, &f, sizeof(float64)); - return i; - #endif -#else - int e = 0; - double m = std::frexp(f, &e); - if(e == 0 && std::fabs(m) == 0.0) + MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) { - uint64 expo = 0u; - uint64 sign = std::signbit(m) ? 0x01u : 0x00u; - uint64 mant = 0u; - uint64 i = 0u; - i |= (mant << 0) & 0x000fffffffffffffull; - i |= (expo << 52) & 0x7ff0000000000000ull; - i |= (sign << 63) & 0x8000000000000000ull; - return i; + return mpt::bit_cast(f); } else { - uint64 expo = e + 1023 - 1; - uint64 sign = std::signbit(m) ? 0x01u : 0x00u; - uint64 mant = static_cast(std::fabs(std::ldexp(m, 53))); - uint64 i = 0u; - i |= (mant << 0) & 0x000fffffffffffffull; - i |= (expo << 52) & 0x7ff0000000000000ull; - i |= (sign << 63) & 0x8000000000000000ull; - return i; + int e = 0; + double m = std::frexp(f, &e); + if(e == 0 && std::fabs(m) == 0.0) + { + uint64 expo = 0u; + uint64 sign = std::signbit(m) ? 0x01u : 0x00u; + uint64 mant = 0u; + uint64 i = 0u; + i |= (mant << 0) & 0x000fffffffffffffull; + i |= (expo << 52) & 0x7ff0000000000000ull; + i |= (sign << 63) & 0x8000000000000000ull; + return i; + } else + { + uint64 expo = e + 1023 - 1; + uint64 sign = std::signbit(m) ? 0x01u : 0x00u; + uint64 mant = static_cast(std::fabs(std::ldexp(m, 53))); + uint64 i = 0u; + i |= (mant << 0) & 0x000fffffffffffffull; + i |= (expo << 52) & 0x7ff0000000000000ull; + i |= (sign << 63) & 0x8000000000000000ull; + return i; + } } -#endif } // 0x3f800000u --> 1.0f static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) { -#if MPT_PLATFORM_IEEE_FLOAT - STATIC_ASSERT(sizeof(uint32) == sizeof(float32)); - #if MPT_COMPILER_UNION_TYPE_ALIASES - union { - uint32 i; - float32 f; - } conv; - conv.i = i; - return conv.f; - #else - float32 f = 0.0f; - std::memcpy(&f, &i, sizeof(float32)); - return f; - #endif -#else - uint32 mant = (i & 0x007fffffu) >> 0; - uint32 expo = (i & 0x7f800000u) >> 23; - uint32 sign = (i & 0x80000000u) >> 31; - if(expo == 0) + MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) { - float m = sign ? -static_cast(mant) : static_cast(mant); - int e = expo - 127 + 1 - 24; - float f = std::ldexp(m, e); - return static_cast(f); + return mpt::bit_cast(i); } else { - mant |= 0x00800000u; - float m = sign ? -static_cast(mant) : static_cast(mant); - int e = expo - 127 + 1 - 24; - float f = std::ldexp(m, e); - return static_cast(f); + uint32 mant = (i & 0x007fffffu) >> 0; + uint32 expo = (i & 0x7f800000u) >> 23; + uint32 sign = (i & 0x80000000u) >> 31; + if(expo == 0) + { + float m = sign ? -static_cast(mant) : static_cast(mant); + int e = static_cast(expo) - 127 + 1 - 24; + float f = std::ldexp(m, e); + return static_cast(f); + } else + { + mant |= 0x00800000u; + float m = sign ? -static_cast(mant) : static_cast(mant); + int e = static_cast(expo) - 127 + 1 - 24; + float f = std::ldexp(m, e); + return static_cast(f); + } } -#endif } static MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) { -#if MPT_PLATFORM_IEEE_FLOAT - STATIC_ASSERT(sizeof(uint64) == sizeof(float64)); - #if MPT_COMPILER_UNION_TYPE_ALIASES - union { - uint64 i; - float64 f; - } conv; - conv.i = i; - return conv.f; - #else - float64 f = 0.0; - std::memcpy(&f, &i, sizeof(float64)); - return f; - #endif -#else - uint64 mant = (i & 0x000fffffffffffffull) >> 0; - uint64 expo = (i & 0x7ff0000000000000ull) >> 52; - uint64 sign = (i & 0x8000000000000000ull) >> 63; - if(expo == 0) + MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) { - double m = sign ? -static_cast(mant) : static_cast(mant); - int e = expo - 1023 + 1 - 53; - double f = std::ldexp(m, e); - return static_cast(f); + return mpt::bit_cast(i); } else { - mant |= 0x0010000000000000ull; - double m = sign ? -static_cast(mant) : static_cast(mant); - int e = expo - 1023 + 1 - 53; - double f = std::ldexp(m, e); - return static_cast(f); + uint64 mant = (i & 0x000fffffffffffffull) >> 0; + uint64 expo = (i & 0x7ff0000000000000ull) >> 52; + uint64 sign = (i & 0x8000000000000000ull) >> 63; + if(expo == 0) + { + double m = sign ? -static_cast(mant) : static_cast(mant); + int e = static_cast(expo) - 1023 + 1 - 53; + double f = std::ldexp(m, e); + return static_cast(f); + } else + { + mant |= 0x0010000000000000ull; + double m = sign ? -static_cast(mant) : static_cast(mant); + int e = static_cast(expo) - 1023 + 1 - 53; + double f = std::ldexp(m, e); + return static_cast(f); + } } -#endif } @@ -651,8 +687,7 @@ MPT_BINARY_STRUCT(IEEE754binary32EmulatedLE, 4) MPT_BINARY_STRUCT(IEEE754binary64EmulatedBE, 8) MPT_BINARY_STRUCT(IEEE754binary64EmulatedLE, 8) -#if MPT_PLATFORM_IEEE_FLOAT - +template struct IEEE754binary32Native { public: @@ -660,13 +695,15 @@ public: public: MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const { - #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); + MPT_CONSTANT_IF(endian == mpt::endian::little) + { return static_cast(EncodeIEEE754binary32(value) >> (i*8)); - #elif defined(MPT_PLATFORM_BIG_ENDIAN) + } + MPT_CONSTANT_IF(endian == mpt::endian::big) + { return static_cast(EncodeIEEE754binary32(value) >> ((4-1-i)*8)); - #else - STATIC_ASSERT(false); - #endif + } } MPT_FORCEINLINE IEEE754binary32Native() { } MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) @@ -678,23 +715,25 @@ public: // big endian: (0x3f,0x80,0x00,0x00) MPT_FORCEINLINE explicit IEEE754binary32Native(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3) { - #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); + MPT_CONSTANT_IF(endian == mpt::endian::little) + { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 0) | (static_cast(b1) << 8) | (static_cast(b2) << 16) | (static_cast(b3) << 24) ); - #elif defined(MPT_PLATFORM_BIG_ENDIAN) + } + MPT_CONSTANT_IF(endian == mpt::endian::big) + { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 24) | (static_cast(b1) << 16) | (static_cast(b2) << 8) | (static_cast(b3) << 0) ); - #else - STATIC_ASSERT(false); - #endif + } } MPT_FORCEINLINE operator float32 () const { @@ -719,6 +758,7 @@ public: } }; +template struct IEEE754binary64Native { public: @@ -726,13 +766,15 @@ public: public: MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const { - #if defined(MPT_PLATFORM_LITTLE_ENDIAN) - return static_cast(EncodeIEEE754binary64(value) >> (i*8)); - #elif defined(MPT_PLATFORM_BIG_ENDIAN) - return static_cast(EncodeIEEE754binary64(value) >> ((8-1-i)*8)); - #else - STATIC_ASSERT(false); - #endif + MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); + MPT_CONSTANT_IF(endian == mpt::endian::little) + { + return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> (i*8))); + } + MPT_CONSTANT_IF(endian == mpt::endian::big) + { + return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> ((8-1-i)*8))); + } } MPT_FORCEINLINE IEEE754binary64Native() { } MPT_FORCEINLINE explicit IEEE754binary64Native(float64 f) @@ -741,7 +783,9 @@ public: } MPT_FORCEINLINE explicit IEEE754binary64Native(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3, mpt::byte b4, mpt::byte b5, mpt::byte b6, mpt::byte b7) { - #if defined(MPT_PLATFORM_LITTLE_ENDIAN) + MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); + MPT_CONSTANT_IF(endian == mpt::endian::little) + { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 0) | (static_cast(b1) << 8) @@ -752,7 +796,9 @@ public: | (static_cast(b6) << 48) | (static_cast(b7) << 56) ); - #elif defined(MPT_PLATFORM_BIG_ENDIAN) + } + MPT_CONSTANT_IF(endian == mpt::endian::big) + { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 56) | (static_cast(b1) << 48) @@ -763,9 +809,7 @@ public: | (static_cast(b6) << 8) | (static_cast(b7) << 0) ); - #else - STATIC_ASSERT(false); - #endif + } } MPT_FORCEINLINE operator float64 () const { @@ -790,46 +834,50 @@ public: } }; -STATIC_ASSERT(sizeof(IEEE754binary32Native) == 4); -STATIC_ASSERT(sizeof(IEEE754binary64Native) == 8); +MPT_STATIC_ASSERT((sizeof(IEEE754binary32Native<>) == 4)); +MPT_STATIC_ASSERT((sizeof(IEEE754binary64Native<>) == 8)); -#if MPT_PLATFORM_IEEE_FLOAT namespace mpt { -template <> struct is_binary_safe< IEEE754binary32Native > : public std::true_type { }; -template <> struct is_binary_safe< IEEE754binary64Native > : public std::true_type { }; +template <> struct is_binary_safe< IEEE754binary32Native<> > : public std::true_type { }; +template <> struct is_binary_safe< IEEE754binary64Native<> > : public std::true_type { }; } -#endif // MPT_PLATFORM_IEEE_FLOAT -#if defined(MPT_PLATFORM_LITTLE_ENDIAN) -typedef IEEE754binary32Native IEEE754binary32LE; -typedef IEEE754binary32EmulatedBE IEEE754binary32BE; -typedef IEEE754binary64Native IEEE754binary64LE; -typedef IEEE754binary64EmulatedBE IEEE754binary64BE; -#elif defined(MPT_PLATFORM_BIG_ENDIAN) -typedef IEEE754binary32EmulatedLE IEEE754binary32LE; -typedef IEEE754binary32Native IEEE754binary32BE; -typedef IEEE754binary64EmulatedLE IEEE754binary64LE; -typedef IEEE754binary64Native IEEE754binary64BE; -#endif +template struct IEEE754binary_types { + typedef IEEE754binary32EmulatedLE IEEE754binary32LE; + typedef IEEE754binary32EmulatedBE IEEE754binary32BE; + typedef IEEE754binary64EmulatedLE IEEE754binary64LE; + typedef IEEE754binary64EmulatedBE IEEE754binary64BE; +}; +template <> struct IEEE754binary_types { + typedef IEEE754binary32Native<> IEEE754binary32LE; + typedef IEEE754binary32EmulatedBE IEEE754binary32BE; + typedef IEEE754binary64Native<> IEEE754binary64LE; + typedef IEEE754binary64EmulatedBE IEEE754binary64BE; +}; +template <> struct IEEE754binary_types { + typedef IEEE754binary32EmulatedLE IEEE754binary32LE; + typedef IEEE754binary32Native<> IEEE754binary32BE; + typedef IEEE754binary64EmulatedLE IEEE754binary64LE; + typedef IEEE754binary64Native<> IEEE754binary64BE; +}; -#else // !MPT_PLATFORM_IEEE_FLOAT - -typedef IEEE754binary32EmulatedLE IEEE754binary32LE; -typedef IEEE754binary32EmulatedBE IEEE754binary32BE; -typedef IEEE754binary64EmulatedLE IEEE754binary64LE; -typedef IEEE754binary64EmulatedBE IEEE754binary64BE; - -#endif // MPT_PLATFORM_IEEE_FLOAT +typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary32LE IEEE754binary32LE; +typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary32BE IEEE754binary32BE; +typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary64LE IEEE754binary64LE; +typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary64BE IEEE754binary64BE; STATIC_ASSERT(sizeof(IEEE754binary32LE) == 4); STATIC_ASSERT(sizeof(IEEE754binary32BE) == 4); STATIC_ASSERT(sizeof(IEEE754binary64LE) == 8); STATIC_ASSERT(sizeof(IEEE754binary64BE) == 8); -typedef IEEE754binary32LE float32le; -typedef IEEE754binary32BE float32be; -typedef IEEE754binary64LE float64le; -typedef IEEE754binary64BE float64be; + +// unaligned + +typedef IEEE754binary32EmulatedLE float32le; +typedef IEEE754binary32EmulatedBE float32be; +typedef IEEE754binary64EmulatedLE float64le; +typedef IEEE754binary64EmulatedBE float64be; STATIC_ASSERT(sizeof(float32le) == 4); STATIC_ASSERT(sizeof(float32be) == 4); @@ -837,6 +885,20 @@ STATIC_ASSERT(sizeof(float64le) == 8); STATIC_ASSERT(sizeof(float64be) == 8); +// potentially aligned + +typedef IEEE754binary32LE float32le_fast; +typedef IEEE754binary32BE float32be_fast; +typedef IEEE754binary64LE float64le_fast; +typedef IEEE754binary64BE float64be_fast; + +STATIC_ASSERT(sizeof(float32le_fast) == 4); +STATIC_ASSERT(sizeof(float32be_fast) == 4); +STATIC_ASSERT(sizeof(float64le_fast) == 8); +STATIC_ASSERT(sizeof(float64be_fast) == 8); + + + // On-disk integer types with defined endianness and no alignemnt requirements // Note: To easily debug module loaders (and anything else that uses this // wrapper struct), you can use the Debugger Visualizers available in @@ -849,57 +911,99 @@ public: typedef T base_type; typedef Tendian endian_type; public: -#if MPT_PLATFORM_ENDIAN_KNOWN - mpt::byte data[sizeof(base_type)]; -#else // !MPT_PLATFORM_ENDIAN_KNOWN +#if MPT_ENDIAN_IS_CONSTEXPR + mpt::byte data[sizeof(base_type)]{}; +#else // !MPT_ENDIAN_IS_CONSTEXPR std::array data; -#endif // MPT_PLATFORM_ENDIAN_KNOWN +#endif // MPT_ENDIAN_IS_CONSTEXPR public: - MPT_FORCEINLINE void set(base_type val) + MPT_ENDIAN_CONSTEXPR_FUN void set(base_type val) noexcept { STATIC_ASSERT(std::numeric_limits::is_integer); - #if MPT_PLATFORM_ENDIAN_KNOWN - MPT_CONSTANT_IF(mpt::detail::NativeEndian != endian_type::Endianness) + #if MPT_ENDIAN_IS_CONSTEXPR + MPT_CONSTANT_IF(endian_type::endian == mpt::endian::big) { - val = SwapBytes(val); + typename std::make_unsigned::type uval = val; + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + data[i] = static_cast((uval >> (8*(sizeof(base_type)-1-i))) & 0xffu); + } + } else + { + typename std::make_unsigned::type uval = val; + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + data[i] = static_cast((uval >> (8*i)) & 0xffu); + } } - std::memcpy(data, &val, sizeof(val)); - #else // !MPT_PLATFORM_ENDIAN_KNOWN - typedef typename std::make_unsigned::type unsigned_base_type; - data = EndianEncode(val); - #endif // MPT_PLATFORM_ENDIAN_KNOWN + #else // !MPT_ENDIAN_IS_CONSTEXPR + MPT_CONSTANT_IF(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) + { + MPT_CONSTANT_IF(mpt::endian::native != endian_type::endian) + { + val = mpt::detail::SwapBytes(val); + } + std::memcpy(data.data(), &val, sizeof(val)); + } else + { + typedef typename std::make_unsigned::type unsigned_base_type; + data = EndianEncode(val); + } + #endif // MPT_ENDIAN_IS_CONSTEXPR } - MPT_FORCEINLINE base_type get() const + MPT_ENDIAN_CONSTEXPR_FUN base_type get() const noexcept { STATIC_ASSERT(std::numeric_limits::is_integer); - #if MPT_PLATFORM_ENDIAN_KNOWN - base_type val = base_type(); - std::memcpy(&val, data, sizeof(val)); - MPT_CONSTANT_IF(mpt::detail::NativeEndian != endian_type::Endianness) + #if MPT_ENDIAN_IS_CONSTEXPR + MPT_CONSTANT_IF(endian_type::endian == mpt::endian::big) { - val = SwapBytes(val); + typename std::make_unsigned::type uval = 0; + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + uval |= static_cast::type>(data[i]) << (8*(sizeof(base_type)-1-i)); + } + return static_cast(uval); + } else + { + typename std::make_unsigned::type uval = 0; + for(std::size_t i = 0; i < sizeof(base_type); ++i) + { + uval |= static_cast::type>(data[i]) << (8*i); + } + return static_cast(uval); } - return val; - #else // !MPT_PLATFORM_ENDIAN_KNOWN - typedef typename std::make_unsigned::type unsigned_base_type; - return EndianDecode(data); - #endif // MPT_PLATFORM_ENDIAN_KNOWN + #else // !MPT_ENDIAN_IS_CONSTEXPR + MPT_CONSTANT_IF(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) + { + base_type val = base_type(); + std::memcpy(&val, data.data(), sizeof(val)); + MPT_CONSTANT_IF(mpt::endian::native != endian_type::endian) + { + val = mpt::detail::SwapBytes(val); + } + return val; + } else + { + typedef typename std::make_unsigned::type unsigned_base_type; + return EndianDecode(data); + } + #endif // MPT_ENDIAN_IS_CONSTEXPR } - MPT_FORCEINLINE packed & operator = (const base_type & val) { set(val); return *this; } - MPT_FORCEINLINE operator base_type () const { return get(); } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN operator base_type () const noexcept { return get(); } public: - packed & operator &= (base_type val) { set(get() & val); return *this; } - packed & operator |= (base_type val) { set(get() | val); return *this; } - packed & operator ^= (base_type val) { set(get() ^ val); return *this; } - packed & operator += (base_type val) { set(get() + val); return *this; } - packed & operator -= (base_type val) { set(get() - val); return *this; } - packed & operator *= (base_type val) { set(get() * val); return *this; } - packed & operator /= (base_type val) { set(get() / val); return *this; } - packed & operator %= (base_type val) { set(get() % val); return *this; } - packed & operator ++ () { set(get() + 1); return *this; } // prefix - packed & operator -- () { set(get() - 1); return *this; } // prefix - base_type operator ++ (int) { base_type old = get(); set(old + 1); return old; } // postfix - base_type operator -- (int) { base_type old = get(); set(old - 1); return old; } // postfix + MPT_ENDIAN_CONSTEXPR_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } + MPT_ENDIAN_CONSTEXPR_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix + MPT_ENDIAN_CONSTEXPR_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix + MPT_ENDIAN_CONSTEXPR_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix + MPT_ENDIAN_CONSTEXPR_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix }; typedef packed< int64, LittleEndian_tag> int64le; @@ -920,6 +1024,10 @@ typedef packed uint32be; typedef packed uint16be; typedef packed uint8be; +namespace mpt { +template struct limits> : mpt::limits {}; +} // namespace mpt + MPT_BINARY_STRUCT(int64le, 8) MPT_BINARY_STRUCT(int32le, 4) MPT_BINARY_STRUCT(int16le, 2) @@ -940,13 +1048,75 @@ MPT_BINARY_STRUCT(uint8be , 1) namespace mpt { -template struct make_le { typedef packed type; }; -template struct make_be { typedef packed type; }; +template struct make_le { typedef packed::type, LittleEndian_tag> type; }; +template struct make_be { typedef packed::type, BigEndian_tag> type; }; + +template +MPT_ENDIAN_CONSTEXPR_FUN auto as_le(T v) noexcept -> typename mpt::make_le::type>::type +{ + typename mpt::make_le::type>::type res; + res = v; + return res; +} +template +MPT_ENDIAN_CONSTEXPR_FUN auto as_be(T v) noexcept -> typename mpt::make_be::type>::type +{ + typename mpt::make_be::type>::type res; + res = v; + return res; +} + +template +MPT_ENDIAN_CONSTEXPR_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept +{ + Tpacked res; + res = v; + return res; +} } // namespace mpt +// 24-bit integer wrapper (for 24-bit PCM) +struct int24 +{ + uint8 bytes[3]; + int24() noexcept + { + bytes[0] = bytes[1] = bytes[2] = 0; + } + explicit int24(int other) noexcept + { + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) + { + bytes[0] = (static_cast(other)>>16)&0xff; + bytes[1] = (static_cast(other)>> 8)&0xff; + bytes[2] = (static_cast(other)>> 0)&0xff; + } else + { + bytes[0] = (static_cast(other)>> 0)&0xff; + bytes[1] = (static_cast(other)>> 8)&0xff; + bytes[2] = (static_cast(other)>>16)&0xff; + } + } + operator int() const noexcept + { + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) + { + return (static_cast(bytes[0]) * 65536) + (bytes[1] * 256) + bytes[2]; + } else + { + return (static_cast(bytes[2]) * 65536) + (bytes[1] * 256) + bytes[0]; + } + } +}; +MPT_STATIC_ASSERT(sizeof(int24) == 3); +#define int24_min (0-0x00800000) +#define int24_max (0+0x007fffff) + + + // Small helper class to support unaligned memory access on all platforms. // This is only used to make old module loaders work everywhere. // Do not use in new code. @@ -959,21 +1129,15 @@ private: const mpt::byte *mem; value_type Read() const { - mpt::byte bytes[sizeof(value_type)]; - std::memcpy(bytes, mem, sizeof(value_type)); - #if defined(MPT_PLATFORM_BIG_ENDIAN) - std::reverse(bytes, bytes + sizeof(value_type)); - #endif - value_type val = value_type(); - std::memcpy(&val, bytes, sizeof(value_type)); + typename mpt::make_le::type val; + std::memcpy(&val, mem, sizeof(value_type)); return val; } public: const_unaligned_ptr_le() : mem(nullptr) {} const_unaligned_ptr_le(const const_unaligned_ptr_le & other) : mem(other.mem) {} const_unaligned_ptr_le & operator = (const const_unaligned_ptr_le & other) { mem = other.mem; return *this; } - explicit const_unaligned_ptr_le(const uint8 *mem) : mem(mem) {} - explicit const_unaligned_ptr_le(const char *mem) : mem(mpt::byte_cast(mem)) {} + template explicit const_unaligned_ptr_le(const Tbyte *mem) : mem(mpt::byte_cast(mem)) {} const_unaligned_ptr_le & operator += (std::size_t count) { mem += count * sizeof(value_type); return *this; } const_unaligned_ptr_le & operator -= (std::size_t count) { mem -= count * sizeof(value_type); return *this; } const_unaligned_ptr_le & operator ++ () { mem += sizeof(value_type); return *this; } @@ -996,21 +1160,15 @@ private: const mpt::byte *mem; value_type Read() const { - mpt::byte bytes[sizeof(value_type)]; - std::memcpy(bytes, mem, sizeof(value_type)); - #if defined(MPT_PLATFORM_LITTLE_ENDIAN) - std::reverse(bytes, bytes + sizeof(value_type)); - #endif - value_type val = value_type(); - std::memcpy(&val, bytes, sizeof(value_type)); + typename mpt::make_be::type val; + std::memcpy(&val, mem, sizeof(value_type)); return val; } public: const_unaligned_ptr_be() : mem(nullptr) {} const_unaligned_ptr_be(const const_unaligned_ptr_be & other) : mem(other.mem) {} const_unaligned_ptr_be & operator = (const const_unaligned_ptr_be & other) { mem = other.mem; return *this; } - explicit const_unaligned_ptr_be(const uint8 *mem) : mem(mem) {} - explicit const_unaligned_ptr_be(const char *mem) : mem(mpt::byte_cast(mem)) {} + template explicit const_unaligned_ptr_be(const Tbyte *mem) : mem(mpt::byte_cast(mem)) {} const_unaligned_ptr_be & operator += (std::size_t count) { mem += count * sizeof(value_type); return *this; } const_unaligned_ptr_be & operator -= (std::size_t count) { mem -= count * sizeof(value_type); return *this; } const_unaligned_ptr_be & operator ++ () { mem += sizeof(value_type); return *this; } @@ -1041,8 +1199,7 @@ public: const_unaligned_ptr() : mem(nullptr) {} const_unaligned_ptr(const const_unaligned_ptr & other) : mem(other.mem) {} const_unaligned_ptr & operator = (const const_unaligned_ptr & other) { mem = other.mem; return *this; } - explicit const_unaligned_ptr(const uint8 *mem) : mem(mem) {} - explicit const_unaligned_ptr(const char *mem) : mem(mpt::byte_cast(mem)) {} + template explicit const_unaligned_ptr(const Tbyte *mem) : mem(mpt::byte_cast(mem)) {} const_unaligned_ptr & operator += (std::size_t count) { mem += count * sizeof(value_type); return *this; } const_unaligned_ptr & operator -= (std::size_t count) { mem -= count * sizeof(value_type); return *this; } const_unaligned_ptr & operator ++ () { mem += sizeof(value_type); return *this; } diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp b/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp index 6f7b33f91..f703f68f3 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp @@ -13,6 +13,7 @@ #if defined(MPT_ENABLE_TEMPFILE) && MPT_OS_WINDOWS #include +#include "mptFileIO.h" #endif // MPT_ENABLE_TEMPFILE && MPT_OS_WINDOWS @@ -30,7 +31,7 @@ OnDiskFileWrapper::OnDiskFileWrapper(FileReader &file, const mpt::PathString &fi file.Rewind(); if(file.GetFileName().empty()) { - const mpt::PathString tempName = mpt::CreateTempFileName(MPT_PATHSTRING("OpenMPT"), fileNameExtension); + const mpt::PathString tempName = mpt::CreateTempFileName(P_("OpenMPT"), fileNameExtension); #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if (_WIN32_WINNT < 0x0602) @@ -40,7 +41,7 @@ OnDiskFileWrapper::OnDiskFileWrapper(FileReader &file, const mpt::PathString &fi #ifdef MPT_ONDISKFILEWRAPPER_NO_CREATEFILE - FILE * f = _wfopen(tempName.AsNative().c_str(), L"wb"); + mpt::ofstream f(tempName, std::ios::binary); if(!f) { throw std::runtime_error(""); @@ -53,20 +54,17 @@ OnDiskFileWrapper::OnDiskFileWrapper(FileReader &file, const mpt::PathString &fi do { std::size_t chunkSize = mpt::saturate_cast(towrite); - std::size_t chunkDone = 0; - chunkDone = fwrite(view.data() + written, 1, chunkSize, f); - if(chunkDone != chunkSize) + bool chunkOk = false; + chunkOk = mpt::IO::WriteRaw(f, mpt::const_byte_span(view.data() + written, chunkSize)); + if(!chunkOk) { - fclose(f); - f = NULL; throw std::runtime_error(""); } - towrite -= chunkDone; - written += chunkDone; + towrite -= chunkSize; + written += chunkSize; } while(towrite > 0); } - fclose(f); - f = NULL; + f.close(); #else // !MPT_ONDISKFILEWRAPPER_NO_CREATEFILE @@ -74,7 +72,7 @@ OnDiskFileWrapper::OnDiskFileWrapper(FileReader &file, const mpt::PathString &fi #if MPT_OS_WINDOWS_WINRT hFile = CreateFile2(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, NULL); #else - hFile = CreateFileW(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); + hFile = CreateFile(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); #endif if(hFile == NULL || hFile == INVALID_HANDLE_VALUE) { @@ -123,7 +121,7 @@ OnDiskFileWrapper::~OnDiskFileWrapper() { if(m_IsTempFile) { - DeleteFileW(m_Filename.AsNative().c_str()); + DeleteFile(m_Filename.AsNative().c_str()); m_IsTempFile = false; } m_Filename = mpt::PathString(); diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReader.h b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h index 188c32a05..d85fce384 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FileReader.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h @@ -10,10 +10,10 @@ #pragma once +#include "BuildSettings.h" -#include "typedefs.h" -#include "mptTypeTraits.h" -#include "StringFixer.h" + +#include "mptStringBuffer.h" #include "misc_util.h" #include "Endianness.h" #include "mptIO.h" @@ -141,14 +141,6 @@ public: // Initialize file reader object based on an existing file reader object window. explicit FileReader(value_data_type other, const mpt::PathString *filename = nullptr) : m_data(other), streamPos(0), fileName(filename) { } - // Initialize file reader object based on an existing file reader object. The other object's stream position is copied. - FileReader(const FileReader &) = default; - FileReader& operator=(const FileReader &) = default; - - // Move an existing file reader object - FileReader(FileReader &&) noexcept = default; - FileReader& operator=(FileReader &&) noexcept = default; - public: mpt::PathString GetFileName() const @@ -343,6 +335,8 @@ public: cache.resize(size_); if(!cache.empty()) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds file.GetRaw(&(cache[0]), size); } } @@ -440,6 +434,12 @@ public: return mpt::byte_cast(DataContainer().GetRawData() + streamPos); } + template + std::size_t GetRawWithOffset(std::size_t offset, T *dst, std::size_t count) const + { + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos + offset, count)); + } + template std::size_t GetRaw(T *dst, std::size_t count) const { @@ -478,22 +478,22 @@ public: std::string GetRawDataAsString() const { PinnedRawDataView view = GetPinnedRawDataView(); - return std::string(view.span().begin(), view.span().end()); + return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); } std::string ReadRawDataAsString() { PinnedRawDataView view = ReadPinnedRawDataView(); - return std::string(view.span().begin(), view.span().end()); + return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); } std::string GetRawDataAsString(std::size_t size) const { PinnedRawDataView view = GetPinnedRawDataView(size); - return std::string(view.span().begin(), view.span().end()); + return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); } std::string ReadRawDataAsString(std::size_t size) { PinnedRawDataView view = ReadPinnedRawDataView(size); - return std::string(view.span().begin(), view.span().end()); + return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); } protected: @@ -504,11 +504,12 @@ protected: template bool Read(T &target) { - if(sizeof(T) != DataContainer().Read(reinterpret_cast(&target), streamPos, sizeof(T))) + mpt::byte_span dst = mpt::as_raw_memory(target); + if(dst.size() != DataContainer().Read(streamPos, dst)) { return false; } - streamPos += sizeof(T); + streamPos += dst.size(); return true; } @@ -520,10 +521,10 @@ public: T ReadIntLE() { static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); - T target; + typename mpt::make_le::type target; if(Read(target)) { - return SwapBytesLE(target); + return target; } else { return 0; @@ -536,10 +537,10 @@ public: T ReadIntBE() { static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); - T target; + typename mpt::make_be::type target; if(Read(target)) { - return SwapBytesBE(target); + return target; } else { return 0; @@ -577,9 +578,9 @@ public: } buf[i] = byte; } - T target; + typename mpt::make_le::type target; std::memcpy(&target, buf, sizeof(T)); - return SwapBytesLE(target); + return target; } // Read a supplied-size little endian integer to a fixed size variable. @@ -663,6 +664,20 @@ public: return ReadIntBE(); } + // Read a single 8bit character. + // If successful, the file cursor is advanced by the size of the integer. + char ReadChar() + { + char target; + if(Read(target)) + { + return target; + } else + { + return 0; + } + } + // Read unsigned 8-Bit integer. // If successful, the file cursor is advanced by the size of the integer. uint8 ReadUint8() @@ -728,7 +743,7 @@ public: return target; } else { - return 0.0f; + return 0.0; } } @@ -742,7 +757,7 @@ public: return target; } else { - return 0.0f; + return 0.0; } } @@ -757,7 +772,7 @@ public: return true; } else { - MemsetZero(target); + Clear(target); return false; } } @@ -773,8 +788,8 @@ public: { copyBytes = BytesLeft(); } - DataContainer().Read(reinterpret_cast(&target), streamPos, copyBytes); - std::memset(reinterpret_cast(&target) + copyBytes, 0, sizeof(target) - copyBytes); + GetRaw(mpt::as_raw_memory(target).data(), copyBytes); + std::memset(mpt::as_raw_memory(target).data() + copyBytes, 0, sizeof(target) - copyBytes); Skip(partialSize); return true; } @@ -849,15 +864,15 @@ public: { char buffer[64]; off_t avail = 0; - while((avail = std::min(DataContainer().Read(reinterpret_cast(buffer), streamPos, sizeof(buffer)), maxLength - dest.length())) != 0) + while((avail = std::min(GetRaw(buffer, mpt::size(buffer)), maxLength - dest.length())) != 0) { auto end = std::find(buffer, buffer + avail, '\0'); dest.insert(dest.end(), buffer, end); - streamPos += (end - buffer); + Skip(end - buffer); if(end < buffer + avail) { // Found null char - streamPos++; + Skip(1); break; } } @@ -868,9 +883,6 @@ public: return dest.length() != 0; } -private: - static MPT_FORCEINLINE bool IsLineEnding(char c) { return c == '\r' || c == '\n'; } -public: // Read a string up to the next line terminator into a std::string bool ReadLine(std::string &dest, const off_t maxLength = std::numeric_limits::max()) { @@ -879,17 +891,18 @@ public: return false; try { - char buffer[64], c = '\0'; + char buffer[64]; + char c = '\0'; off_t avail = 0; - while((avail = std::min(DataContainer().Read(reinterpret_cast(buffer), streamPos, sizeof(buffer)), maxLength - dest.length())) != 0) + while((avail = std::min(GetRaw(buffer, mpt::size(buffer)), maxLength - dest.length())) != 0) { - auto end = std::find_if(buffer, buffer + avail, IsLineEnding); + auto end = std::find_if(buffer, buffer + avail, mpt::String::Traits::IsLineEnding); dest.insert(dest.end(), buffer, end); - streamPos += (end - buffer); + Skip(end - buffer); if(end < buffer + avail) { // Found line ending - streamPos++; + Skip(1); // Handle CRLF line ending if(*end == '\r') { @@ -909,7 +922,7 @@ public: // Read an array of binary-safe T values. // If successful, the file cursor is advanced by the size of the array. // Otherwise, the target is zeroed. - template + template bool ReadArray(T (&destArray)[destSize]) { STATIC_ASSERT(mpt::is_binary_safe::value); @@ -922,7 +935,24 @@ public: return true; } else { - MemsetZero(destArray); + Clear(destArray); + return false; + } + } + template + bool ReadArray(std::array &destArray) + { + STATIC_ASSERT(mpt::is_binary_safe::value); + if(CanRead(sizeof(destArray))) + { + for(auto &element : destArray) + { + Read(element); + } + return true; + } else + { + destArray.fill(T()); return false; } } @@ -963,10 +993,10 @@ public: { mpt::byte bytes[N - 1]; STATIC_ASSERT(sizeof(bytes) == sizeof(magic) - 1); - DataContainer().Read(bytes, streamPos, N - 1); + GetRaw(bytes, N - 1); if(!std::memcmp(bytes, magic, N - 1)) { - streamPos += (N - 1); + Skip(N - 1); return true; } } @@ -980,8 +1010,8 @@ public: bool identical = true; for(std::size_t i = 0; i < magicLength; ++i) { - mpt::byte c = 0; - DataContainer().Read(&c, streamPos + i, 1); + mpt::byte c = mpt::as_byte(0); + GetRawWithOffset(i, &c, 1); if(c != mpt::byte_cast(magic[i])) { identical = false; @@ -990,7 +1020,7 @@ public: } if(identical) { - streamPos += magicLength; + Skip(magicLength); return true; } else { @@ -1020,10 +1050,11 @@ public: } mpt::byte bytes[16]; // More than enough for any valid VarInt - off_t avail = DataContainer().Read(bytes, streamPos, sizeof(bytes)), readPos = 1; + off_t avail = GetRaw(bytes, sizeof(bytes)); + off_t readPos = 1; size_t writtenBits = 0; - uint8 b = bytes[0]; + uint8 b = mpt::byte_cast(bytes[0]); target = (b & 0x7F); // Count actual bits used in most significant byte (i.e. this one) @@ -1037,18 +1068,18 @@ public: while(readPos < avail && (b & 0x80) != 0) { - b = bytes[readPos++]; + b = mpt::byte_cast(bytes[readPos++]); target <<= 7; target |= (b & 0x7F); writtenBits += 7; if(readPos == avail) { - streamPos += readPos; - avail = DataContainer().Read(bytes, streamPos, sizeof(bytes)); + Skip(readPos); + avail = GetRaw(bytes, sizeof(bytes)); readPos = 0; } } - streamPos += readPos; + Skip(readPos); if(writtenBits > sizeof(target) * 8u) { @@ -1072,8 +1103,6 @@ typedef detail::FileReader FileReader; typedef detail::FileReader MemoryFileReader; -#if defined(LIBOPENMPT_BUILD) - // Initialize file reader object with pointer to data and data length. template static inline FileReader make_FileReader(mpt::span bytedata, const mpt::PathString *filename = nullptr) { @@ -1111,8 +1140,6 @@ static inline FileReader make_FileReader(std::istream *s, const mpt::PathString #endif // MPT_FILEREADER_STD_ISTREAM -#endif // LIBOPENMT_BUILD - #if defined(MPT_ENABLE_FILEIO) // templated in order to reduce header inter-dependencies @@ -1129,10 +1156,10 @@ FileReader GetFileReader(TInputFile &file) { return FileReader(); } - return FileReader(tmp.first, tmp.second); + return make_FileReader(tmp.first, tmp.second); #else typename TInputFile::ContentsRef tmp = file.Get(); - return FileReader(mpt::as_span(tmp.first.data, tmp.first.size), tmp.second); + return make_FileReader(mpt::as_span(tmp.first.data, tmp.first.size), tmp.second); #endif } #endif // MPT_ENABLE_FILEIO @@ -1150,7 +1177,7 @@ private: public: - OnDiskFileWrapper(FileReader &file, const mpt::PathString &fileNameExtension = MPT_PATHSTRING("tmp")); + OnDiskFileWrapper(FileReader &file, const mpt::PathString &fileNameExtension = P_("tmp")); ~OnDiskFileWrapper(); diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h index 794d7fdf8..6f73a69f7 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h @@ -9,7 +9,8 @@ #pragma once -#include "typedefs.h" +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h index 51ec8a0c3..e0aa76dc7 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + #include OPENMPT_NAMESPACE_BEGIN @@ -29,6 +31,7 @@ struct enum_traits // Type-safe wrapper around an enum, that can represent all enum values and bitwise compositions thereof. // Conversions to and from plain integers as well as conversions to the base enum are always explicit. template +// cppcheck-suppress copyCtorAndEqOperator class enum_value_type { public: @@ -38,50 +41,50 @@ public: private: store_type bits; public: - MPT_CONSTEXPR11_FUN enum_value_type() : bits(0) { } - MPT_CONSTEXPR11_FUN enum_value_type(const enum_value_type &x) : bits(x.bits) { } - MPT_CONSTEXPR11_FUN enum_value_type(enum_type x) : bits(static_cast(x)) { } + MPT_CONSTEXPR11_FUN enum_value_type() noexcept : bits(0) { } + MPT_CONSTEXPR11_FUN enum_value_type(const enum_value_type &x) noexcept : bits(x.bits) { } + MPT_CONSTEXPR11_FUN enum_value_type(enum_type x) noexcept : bits(static_cast(x)) { } private: - explicit MPT_CONSTEXPR11_FUN enum_value_type(store_type x) : bits(x) { } // private in order to prevent accidental conversions. use from_bits. - MPT_CONSTEXPR11_FUN operator store_type () const { return bits; } // private in order to prevent accidental conversions. use as_bits. + explicit MPT_CONSTEXPR11_FUN enum_value_type(store_type x) noexcept : bits(x) { } // private in order to prevent accidental conversions. use from_bits. + MPT_CONSTEXPR11_FUN operator store_type () const noexcept { return bits; } // private in order to prevent accidental conversions. use as_bits. public: - static MPT_CONSTEXPR11_FUN enum_value_type from_bits(store_type bits) { return value_type(bits); } - MPT_CONSTEXPR11_FUN enum_type as_enum() const { return static_cast(bits); } - MPT_CONSTEXPR11_FUN store_type as_bits() const { return bits; } + static MPT_CONSTEXPR11_FUN enum_value_type from_bits(store_type bits) noexcept { return value_type(bits); } + MPT_CONSTEXPR11_FUN enum_type as_enum() const noexcept { return static_cast(bits); } + MPT_CONSTEXPR11_FUN store_type as_bits() const noexcept { return bits; } public: - MPT_CONSTEXPR11_FUN operator bool () const { return bits != store_type(); } - MPT_CONSTEXPR11_FUN bool operator ! () const { return bits == store_type(); } + MPT_CONSTEXPR11_FUN operator bool () const noexcept { return bits != store_type(); } + MPT_CONSTEXPR11_FUN bool operator ! () const noexcept { return bits == store_type(); } - MPT_CONSTEXPR11_FUN const enum_value_type operator ~ () const { return enum_value_type(~bits); } + MPT_CONSTEXPR11_FUN const enum_value_type operator ~ () const noexcept { return enum_value_type(~bits); } - friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_value_type b) { return a.bits == b.bits; } - friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_value_type b) { return a.bits != b.bits; } + friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_value_type b) noexcept { return a.bits == b.bits; } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_value_type b) noexcept { return a.bits != b.bits; } - friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_t b) { return a == enum_value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_t b) { return a != enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_t b) noexcept { return a == enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_t b) noexcept { return a != enum_value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (enum_t a, enum_value_type b) { return enum_value_type(a) == b; } - friend MPT_CONSTEXPR11_FUN bool operator != (enum_t a, enum_value_type b) { return enum_value_type(a) != b; } + friend MPT_CONSTEXPR11_FUN bool operator == (enum_t a, enum_value_type b) noexcept { return enum_value_type(a) == b; } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_t a, enum_value_type b) noexcept { return enum_value_type(a) != b; } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits | b.bits); } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits & b.bits); } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits ^ b.bits); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_value_type b) noexcept { return enum_value_type(a.bits | b.bits); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_value_type b) noexcept { return enum_value_type(a.bits & b.bits); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_value_type b) noexcept { return enum_value_type(a.bits ^ b.bits); } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_t b) { return a | enum_value_type(b); } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_t b) { return a & enum_value_type(b); } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_t b) { return a ^ enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_t b) noexcept { return a | enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_t b) noexcept { return a & enum_value_type(b); } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_t b) noexcept { return a ^ enum_value_type(b); } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_t a, enum_value_type b) { return enum_value_type(a) | b; } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_t a, enum_value_type b) { return enum_value_type(a) & b; } - friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_t a, enum_value_type b) { return enum_value_type(a) ^ b; } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_t a, enum_value_type b) noexcept { return enum_value_type(a) | b; } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_t a, enum_value_type b) noexcept { return enum_value_type(a) & b; } + friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_t a, enum_value_type b) noexcept { return enum_value_type(a) ^ b; } - MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_value_type b) { *this = *this | b; return *this; } - MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_value_type b) { *this = *this & b; return *this; } - MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_value_type b) { *this = *this ^ b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_value_type b) noexcept { *this = *this | b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_value_type b) noexcept { *this = *this & b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_value_type b) noexcept { *this = *this ^ b; return *this; } - MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_t b) { *this = *this | b; return *this; } - MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_t b) { *this = *this & b; return *this; } - MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_t b) { *this = *this ^ b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_t b) noexcept { *this = *this | b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_t b) noexcept { *this = *this & b; return *this; } + MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_t b) noexcept { *this = *this ^ b; return *this; } }; @@ -98,51 +101,52 @@ public: private: enum_type value; public: - explicit MPT_CONSTEXPR11_FUN Enum(enum_type val) : value(val) { } - MPT_CONSTEXPR11_FUN operator enum_type () const { return value; } - MPT_CONSTEXPR14_FUN Enum &operator = (enum_type val) { value = val; return *this; } + explicit MPT_CONSTEXPR11_FUN Enum(enum_type val) noexcept : value(val) { } + MPT_CONSTEXPR11_FUN operator enum_type () const noexcept { return value; } + MPT_CONSTEXPR14_FUN Enum &operator = (enum_type val) noexcept { value = val; return *this; } public: MPT_CONSTEXPR11_FUN const value_type operator ~ () const { return ~value_type(value); } - friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) { return value_type(a) == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) { return value_type(a) != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) noexcept { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) noexcept { return value_type(a) != value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) { return value_type(a) == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) { return value_type(a) != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) noexcept { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) noexcept { return value_type(a) != value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) { return value_type(a) == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) { return value_type(a) != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) noexcept { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) noexcept { return value_type(a) != value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) { return value_type(a) == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) { return value_type(a) != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) noexcept { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) noexcept { return value_type(a) != value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) { return value_type(a) == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) { return value_type(a) != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) noexcept { return value_type(a) == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) noexcept { return value_type(a) != value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) { return value_type(a) | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) { return value_type(a) & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) { return value_type(a) ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) noexcept { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) noexcept { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) noexcept { return value_type(a) ^ value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) { return value_type(a) | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) { return value_type(a) & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) { return value_type(a) ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) noexcept { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) noexcept { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) noexcept { return value_type(a) ^ value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) { return value_type(a) | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) { return value_type(a) & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) { return value_type(a) ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) noexcept { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) noexcept { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) noexcept { return value_type(a) ^ value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) { return value_type(a) | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) { return value_type(a) & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) { return value_type(a) ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) noexcept { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) noexcept { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) noexcept { return value_type(a) ^ value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) { return value_type(a) | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) { return value_type(a) & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) { return value_type(a) ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) noexcept { return value_type(a) | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) noexcept { return value_type(a) & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) noexcept { return value_type(a) ^ value_type(b); } }; template ::store_type > +// cppcheck-suppress copyCtorAndEqOperator class FlagSet { public: @@ -155,66 +159,66 @@ private: // support truncated store_type ... : store_type bits_; - static MPT_CONSTEXPR11_FUN store_type store_from_value(value_type bits) { return static_cast(bits.as_bits()); } - static MPT_CONSTEXPR11_FUN value_type value_from_store(store_type bits) { return value_type::from_bits(static_cast(bits)); } + static MPT_CONSTEXPR11_FUN store_type store_from_value(value_type bits) noexcept { return static_cast(bits.as_bits()); } + static MPT_CONSTEXPR11_FUN value_type value_from_store(store_type bits) noexcept { return value_type::from_bits(static_cast(bits)); } - MPT_CONSTEXPR14_FUN FlagSet & store(value_type bits) { bits_ = store_from_value(bits); return *this; } - MPT_CONSTEXPR11_FUN value_type load() const { return value_from_store(bits_); } + MPT_CONSTEXPR14_FUN FlagSet & store(value_type bits) noexcept { bits_ = store_from_value(bits); return *this; } + MPT_CONSTEXPR11_FUN value_type load() const noexcept { return value_from_store(bits_); } public: // Default constructor (no flags set) - MPT_CONSTEXPR11_FUN FlagSet() : bits_(store_from_value(value_type())) + MPT_CONSTEXPR11_FUN FlagSet() noexcept : bits_(store_from_value(value_type())) + { + } + + // Copy constructor + MPT_CONSTEXPR11_FUN FlagSet(const FlagSet &flags) noexcept + : bits_(flags.bits_) { } // Value constructor - MPT_CONSTEXPR11_FUN FlagSet(value_type flags) : bits_(store_from_value(value_type(flags))) + MPT_CONSTEXPR11_FUN FlagSet(value_type flags) noexcept : bits_(store_from_value(value_type(flags))) { } - MPT_CONSTEXPR11_FUN FlagSet(enum_type flag) : bits_(store_from_value(value_type(flag))) + MPT_CONSTEXPR11_FUN FlagSet(enum_type flag) noexcept : bits_(store_from_value(value_type(flag))) { } - explicit MPT_CONSTEXPR11_FUN FlagSet(store_type flags) : bits_(store_from_value(value_type::from_bits(flags))) + explicit MPT_CONSTEXPR11_FUN FlagSet(store_type flags) noexcept : bits_(store_from_value(value_type::from_bits(flags))) { } - MPT_CONSTEXPR11_FUN operator bool () const + MPT_CONSTEXPR11_FUN explicit operator bool () const noexcept { return load(); } - // In order to catch undesired conversions to bool in integer contexts, - // add a deprecated conversion operator to store_type. - // C++11 explicit conversion cast operators ('explicit operator bool ();') - // would solve this in a better way and always fail at compile-time instead of this - // solution which just warns in some cases. - // The macro-based extended instrument fields writer in InstrumentExtensions.cpp currently needs this conversion, - // so it is not marked deprecated (for now). - /*MPT_DEPRECATED*/ MPT_CONSTEXPR11_FUN operator store_type () const + // The macro-based extended instrument fields writer in InstrumentExtensions.cpp currently needs this conversion. + /*MPT_DEPRECATED*/ MPT_CONSTEXPR11_FUN operator store_type () const noexcept { return load().as_bits(); } - MPT_CONSTEXPR11_FUN value_type value() const + MPT_CONSTEXPR11_FUN value_type value() const noexcept { return load(); } - MPT_CONSTEXPR11_FUN operator value_type () const + MPT_CONSTEXPR11_FUN operator value_type () const noexcept { return load(); } // Test if one or more flags are set. Returns true if at least one of the given flags is set. - MPT_CONSTEXPR11_FUN bool operator[] (value_type flags) const + MPT_CONSTEXPR11_FUN bool operator[] (value_type flags) const noexcept { return test(flags); } // String representation of flag set - std::string to_string() const + std::string to_string() const noexcept { std::string str(size_bits(), '0'); @@ -227,182 +231,182 @@ public: } // Set one or more flags. - MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags) + MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags) noexcept { return store(load() | flags); } // Set or clear one or more flags. - MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags, bool val) + MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags, bool val) noexcept { return store((val ? (load() | flags) : (load() & ~flags))); } // Clear or flags. - MPT_CONSTEXPR14_FUN FlagSet &reset() + MPT_CONSTEXPR14_FUN FlagSet &reset() noexcept { return store(value_type()); } // Clear one or more flags. - MPT_CONSTEXPR14_FUN FlagSet &reset(value_type flags) + MPT_CONSTEXPR14_FUN FlagSet &reset(value_type flags) noexcept { return store(load() & ~flags); } // Toggle all flags. - MPT_CONSTEXPR14_FUN FlagSet &flip() + MPT_CONSTEXPR14_FUN FlagSet &flip() noexcept { return store(~load()); } // Toggle one or more flags. - MPT_CONSTEXPR14_FUN FlagSet &flip(value_type flags) + MPT_CONSTEXPR14_FUN FlagSet &flip(value_type flags) noexcept { return store(load() ^ flags); } // Returns the size of the flag set in bytes - MPT_CONSTEXPR11_FUN std::size_t size() const + MPT_CONSTEXPR11_FUN std::size_t size() const noexcept { return sizeof(store_type); } // Returns the size of the flag set in bits - MPT_CONSTEXPR11_FUN std::size_t size_bits() const + MPT_CONSTEXPR11_FUN std::size_t size_bits() const noexcept { return size() * 8; } // Test if one or more flags are set. Returns true if at least one of the given flags is set. - MPT_CONSTEXPR11_FUN bool test(value_type flags) const + MPT_CONSTEXPR11_FUN bool test(value_type flags) const noexcept { return (load() & flags); } // Test if all specified flags are set. - MPT_CONSTEXPR11_FUN bool test_all(value_type flags) const + MPT_CONSTEXPR11_FUN bool test_all(value_type flags) const noexcept { return (load() & flags) == flags; } // Test if any but the specified flags are set. - MPT_CONSTEXPR11_FUN bool test_any_except(value_type flags) const + MPT_CONSTEXPR11_FUN bool test_any_except(value_type flags) const noexcept { return (load() & ~flags); } // Test if any flag is set. - MPT_CONSTEXPR11_FUN bool any() const + MPT_CONSTEXPR11_FUN bool any() const noexcept { return load(); } // Test if no flags are set. - MPT_CONSTEXPR11_FUN bool none() const + MPT_CONSTEXPR11_FUN bool none() const noexcept { return !load(); } - MPT_CONSTEXPR11_FUN store_type GetRaw() const + MPT_CONSTEXPR11_FUN store_type GetRaw() const noexcept { return bits_; } - MPT_CONSTEXPR14_FUN FlagSet & SetRaw(store_type flags) + MPT_CONSTEXPR14_FUN FlagSet & SetRaw(store_type flags) noexcept { bits_ = flags; return *this; } - MPT_CONSTEXPR14_FUN FlagSet &operator = (value_type flags) + MPT_CONSTEXPR14_FUN FlagSet &operator = (value_type flags) noexcept { return store(flags); } - MPT_CONSTEXPR14_FUN FlagSet &operator = (enum_type flag) + MPT_CONSTEXPR14_FUN FlagSet &operator = (enum_type flag) noexcept { return store(flag); } - MPT_CONSTEXPR14_FUN FlagSet &operator = (FlagSet flags) + MPT_CONSTEXPR14_FUN FlagSet &operator = (FlagSet flags) noexcept { return store(flags.load()); } - MPT_CONSTEXPR14_FUN FlagSet &operator &= (value_type flags) + MPT_CONSTEXPR14_FUN FlagSet &operator &= (value_type flags) noexcept { return store(load() & flags); } - MPT_CONSTEXPR14_FUN FlagSet &operator |= (value_type flags) + MPT_CONSTEXPR14_FUN FlagSet &operator |= (value_type flags) noexcept { return store(load() | flags); } - MPT_CONSTEXPR14_FUN FlagSet &operator ^= (value_type flags) + MPT_CONSTEXPR14_FUN FlagSet &operator ^= (value_type flags) noexcept { return store(load() ^ flags); } - friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) { return a.load() == b.load(); } - friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) { return a.load() != b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) noexcept { return a.load() == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) noexcept { return a.load() != b.load(); } - friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) { return a.load() == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) { return a.load() != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) noexcept { return a.load() == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) noexcept { return a.load() != value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) { return value_type(a) == b.load(); } - friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) { return value_type(a) != b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) noexcept { return value_type(a) == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) noexcept { return value_type(a) != b.load(); } - friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) { return a.load() == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) { return a.load() != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) noexcept { return a.load() == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) noexcept { return a.load() != value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) { return value_type(a) == b.load(); } - friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) { return value_type(a) != b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) noexcept { return value_type(a) == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) noexcept { return value_type(a) != b.load(); } - friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, Enum b) { return a.load() == value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, Enum b) { return a.load() != value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, Enum b) noexcept { return a.load() == value_type(b); } + friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, Enum b) noexcept { return a.load() != value_type(b); } - friend MPT_CONSTEXPR11_FUN bool operator == (Enum a, self_type b) { return value_type(a) == b.load(); } - friend MPT_CONSTEXPR11_FUN bool operator != (Enum a, self_type b) { return value_type(a) != b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator == (Enum a, self_type b) noexcept { return value_type(a) == b.load(); } + friend MPT_CONSTEXPR11_FUN bool operator != (Enum a, self_type b) noexcept { return value_type(a) != b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) { return a.load() | b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) { return a.load() & b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) { return a.load() ^ b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) noexcept { return a.load() | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) noexcept { return a.load() & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) noexcept { return a.load() ^ b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) { return a.load() | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) { return a.load() & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) { return a.load() ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) noexcept { return a.load() | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) noexcept { return a.load() & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) noexcept { return a.load() ^ value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) { return value_type(a) | b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) { return value_type(a) & b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) { return value_type(a) ^ b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) noexcept { return value_type(a) | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) noexcept { return value_type(a) & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) noexcept { return value_type(a) ^ b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) { return a.load() | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) { return a.load() & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) { return a.load() ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) noexcept { return a.load() | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) noexcept { return a.load() & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) noexcept { return a.load() ^ value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) { return value_type(a) | b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) { return value_type(a) & b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) { return value_type(a) ^ b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) noexcept { return value_type(a) | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) noexcept { return value_type(a) & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) noexcept { return value_type(a) ^ b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, Enum b) { return a.load() | value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, Enum b) { return a.load() & value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, Enum b) { return a.load() ^ value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, Enum b) noexcept { return a.load() | value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, Enum b) noexcept { return a.load() & value_type(b); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, Enum b) noexcept { return a.load() ^ value_type(b); } - friend MPT_CONSTEXPR11_FUN const value_type operator | (Enum a, self_type b) { return value_type(a) | b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator & (Enum a, self_type b) { return value_type(a) & b.load(); } - friend MPT_CONSTEXPR11_FUN const value_type operator ^ (Enum a, self_type b) { return value_type(a) ^ b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator | (Enum a, self_type b) noexcept { return value_type(a) | b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator & (Enum a, self_type b) noexcept { return value_type(a) & b.load(); } + friend MPT_CONSTEXPR11_FUN const value_type operator ^ (Enum a, self_type b) noexcept { return value_type(a) ^ b.load(); } }; // Declare typesafe logical operators for enum_t #define MPT_DECLARE_ENUM(enum_t) \ - MPT_CONSTEXPR11_FUN enum_value_type operator | (enum_t a, enum_t b) { return enum_value_type(a) | enum_value_type(b); } \ - MPT_CONSTEXPR11_FUN enum_value_type operator & (enum_t a, enum_t b) { return enum_value_type(a) & enum_value_type(b); } \ - MPT_CONSTEXPR11_FUN enum_value_type operator ^ (enum_t a, enum_t b) { return enum_value_type(a) ^ enum_value_type(b); } \ - MPT_CONSTEXPR11_FUN enum_value_type operator ~ (enum_t a) { return ~enum_value_type(a); } \ + MPT_CONSTEXPR11_FUN enum_value_type operator | (enum_t a, enum_t b) noexcept { return enum_value_type(a) | enum_value_type(b); } \ + MPT_CONSTEXPR11_FUN enum_value_type operator & (enum_t a, enum_t b) noexcept { return enum_value_type(a) & enum_value_type(b); } \ + MPT_CONSTEXPR11_FUN enum_value_type operator ^ (enum_t a, enum_t b) noexcept { return enum_value_type(a) ^ enum_value_type(b); } \ + MPT_CONSTEXPR11_FUN enum_value_type operator ~ (enum_t a) noexcept { return ~enum_value_type(a); } \ /**/ // backwards compatibility diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp index 5fcc59d81..722556a84 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp @@ -88,10 +88,10 @@ bool IsFacilityActive(const char *facility) #endif -void Logger::SendLogMessage(const Context &context, LogLevel level, const char *facility, const mpt::ustring &text) +void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text) { #ifdef MPT_LOG_IS_DISABLED - MPT_UNREFERENCED_PARAMETER(context); + MPT_UNREFERENCED_PARAMETER(loc); MPT_UNREFERENCED_PARAMETER(level); MPT_UNREFERENCED_PARAMETER(facility); MPT_UNREFERENCED_PARAMETER(text); @@ -109,10 +109,10 @@ void Logger::SendLogMessage(const Context &context, LogLevel level, const char * MPT_UNREFERENCED_PARAMETER(facility); #endif // MODPLUG_TRACKER // remove eol if already present and add log level prefix - const mpt::ustring message = LogLevelToString(level) + MPT_USTRING(": ") + mpt::String::RTrim(text, MPT_USTRING("\r\n")); - const mpt::ustring file = mpt::ToUnicode(mpt::CharsetASCII, context.file); - const mpt::ustring function = mpt::ToUnicode(mpt::CharsetASCII, context.function); - const mpt::ustring line = mpt::ufmt::dec(context.line); + const mpt::ustring message = LogLevelToString(level) + U_(": ") + mpt::String::RTrim(text, U_("\r\n")); + const mpt::ustring file = mpt::ToUnicode(mpt::CharsetASCII, loc.file_name() ? loc.file_name() : ""); + const mpt::ustring function = mpt::ToUnicode(mpt::CharsetASCII, loc.function_name() ? loc.function_name() : ""); + const mpt::ustring line = mpt::ufmt::dec(loc.line()); #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) #if MPT_OS_WINDOWS static uint64 s_lastlogtime = 0; @@ -125,30 +125,30 @@ void Logger::SendLogMessage(const Context &context, LogLevel level, const char * #endif if(mpt::log::FileEnabled) { - static FILE * s_logfile = nullptr; + static mpt::ofstream s_logfile; if(!s_logfile) { - s_logfile = mpt_fopen(MPT_PATHSTRING("mptrack.log"), "a"); + s_logfile.open(P_("mptrack.log"), std::ios::app); } if(s_logfile) { - fprintf(s_logfile, mpt::ToCharset(mpt::CharsetUTF8, mpt::format(MPT_USTRING("%1+%2 %3(%4): %5 [%6]\n")) - ( mpt::Date::ANSI::ToString(cur) - , mpt::ufmt::dec<6>(diff) + mpt::IO::WriteText(s_logfile, mpt::ToCharset(mpt::CharsetUTF8, mpt::format(U_("%1+%2 %3(%4): %5 [%6]\n")) + ( mpt::Date::ANSI::ToUString(cur) + , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) , file , line , message , function - )).c_str()); - fflush(s_logfile); + ))); + mpt::IO::Flush(s_logfile); } } if(mpt::log::DebuggerEnabled) { - OutputDebugStringW(mpt::ToWide(mpt::format(MPT_USTRING("%1(%2): +%3 %4 [%5]\n")) + OutputDebugStringW(mpt::ToWide(mpt::format(U_("%1(%2): +%3 %4 [%5]\n")) ( file , line - , mpt::ufmt::dec<6>(diff) + , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) , message , function )).c_str()); @@ -163,7 +163,7 @@ void Logger::SendLogMessage(const Context &context, LogLevel level, const char * } std::wstring consoletext = mpt::ToWide(message) + L"\r\n"; DWORD dummy = 0; - WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), consoletext.c_str(), consoletext.length(), &dummy, NULL); + WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), consoletext.c_str(), mpt::saturate_cast(consoletext.length()), &dummy, NULL); } #elif defined(MODPLUG_TRACKER) && defined(MPT_BUILD_WINESUPPORT) std::clog @@ -185,7 +185,7 @@ void Logger::SendLogMessage(const Context &context, LogLevel level, const char * void LegacyLogger::operator () (const AnyStringLocale &text) { - SendLogMessage(context, MPT_LEGACY_LOGLEVEL, "", text); + SendLogMessage(loc, MPT_LEGACY_LOGLEVEL, "", text); } void LegacyLogger::operator () (const char *format, ...) @@ -197,12 +197,12 @@ void LegacyLogger::operator () (const char *format, ...) vsnprintf(message, LOGBUF_SIZE, format, va); va_end(va); message[LOGBUF_SIZE - 1] = '\0'; - SendLogMessage(context, MPT_LEGACY_LOGLEVEL, "", mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, message)); + SendLogMessage(loc, MPT_LEGACY_LOGLEVEL, "", mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, message)); } void LegacyLogger::operator () (LogLevel level, const mpt::ustring &text) { - SendLogMessage(context, level, "", text); + SendLogMessage(loc, level, "", text); } @@ -230,9 +230,10 @@ struct Entry { const char * Function; const char * File; int Line; + Direction Direction; }; -inline bool operator < (const Entry &a, const Entry &b) +static MPT_FORCEINLINE bool operator < (const Entry &a, const Entry &b) noexcept { /* return false @@ -266,7 +267,7 @@ void Enable(std::size_t numEntries) Entries.clear(); Entries.resize(numEntries); NextIndex.store(0); - g_Enabled = true; + g_Enabled = (numEntries > 0); } void Disable() @@ -278,11 +279,12 @@ void Disable() g_Enabled = false; } -MPT_NOINLINE void Trace(const mpt::log::Context & context) +MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction) noexcept { // This will get called in realtime contexts and hot paths. // No blocking allowed here. const uint32 index = NextIndex.fetch_add(1); + const std::size_t numEntries = Entries.size(); #if 1 LARGE_INTEGER time; time.QuadPart = 0; @@ -294,13 +296,14 @@ MPT_NOINLINE void Trace(const mpt::log::Context & context) const uint64 timestamp = (static_cast(time.dwHighDateTime) << 32) | (static_cast(time.dwLowDateTime) << 0); #endif const uint32 threadid = static_cast(GetCurrentThreadId()); - mpt::log::Trace::Entry & entry = Entries[index % Entries.size()]; + mpt::log::Trace::Entry & entry = Entries[index % numEntries]; entry.Index = index; entry.ThreadId = threadid; entry.Timestamp = timestamp; - entry.Function = context.function; - entry.File = context.file; - entry.Line = context.line; + entry.Function = loc.function_name(); + entry.File = loc.file_name(); + entry.Line = loc.line(); + entry.Direction = direction; } void Seal() @@ -333,9 +336,9 @@ bool Dump(const mpt::PathString &filename) // sort according to index in case of overflows std::stable_sort(Entries.begin(), Entries.end()); - mpt::ofstream f(filename, std::ios::out); + mpt::ofstream f(filename); - f << "Build: OpenMPT " << MptVersion::GetVersionStringExtended() << std::endl; + f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetUTF8, Build::GetVersionStringExtended()) << std::endl; bool qpcValid = false; @@ -347,7 +350,7 @@ bool Dump(const mpt::PathString &filename) qpcValid = true; } - f << "Dump: " << mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToString(ftNow)) << std::endl; + f << "Dump: " << mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToUString(ftNow)) << std::endl; f << "Captured events: " << Entries.size() << std::endl; if(qpcValid && (Entries.size() > 0)) { @@ -357,15 +360,14 @@ bool Dump(const mpt::PathString &filename) f << "Events/second: " << mpt::fmt::fix(eventsPerSecond) << std::endl; } - for(std::size_t i = 0; i < Entries.size(); ++i) + for(auto &entry : Entries) { - mpt::log::Trace::Entry & entry = Entries[i]; if(!entry.Function) entry.Function = ""; if(!entry.File) entry.File = ""; std::string time; if(qpcValid) { - time = mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); + time = mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToUString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); } else { time = mpt::format("0x%1")(mpt::fmt::hex0<16>(entry.Timestamp)); @@ -387,6 +389,7 @@ bool Dump(const mpt::PathString &filename) { f << " " << mpt::fmt::hex0<8>(entry.ThreadId) << " "; } + f << (entry.Direction == mpt::log::Trace::Direction::Enter ? ">" : entry.Direction == mpt::log::Trace::Direction::Leave ? "<" : " ") << " "; f << entry.File << "(" << entry.Line << "): " << entry.Function; f << std::endl; } diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.h b/Frameworks/OpenMPT/OpenMPT/common/Logging.h index 86abd6da7..0d7c2e349 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Logging.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN @@ -42,12 +44,12 @@ CSoundfile::AddToLog. Logging a simple message: MPT_LOG(LogWarning, "sounddev", "some message"); -MPT_LOG(LogWarning, "sounddev", MPT_USTRING("some message")); +MPT_LOG(LogWarning, "sounddev", U_("some message")); Facility is some course grained code section identifier (more coarse grained than the current file name probably), useful to do some selective logging. Logging a more complex message: -MPT_LOG(LogWarning, "sounddev", mpt::format(MPT_USTRING("Some message: foo=%1, bar=0x%2"))(foo, mpt::ufmt::hex0<8>(bar))); +MPT_LOG(LogWarning, "sounddev", mpt::format(U_("Some message: foo=%1, bar=0x%2"))(foo, mpt::ufmt::hex0<8>(bar))); Note that even with full enabled logging and a runtime configurable logging level, the runtime overhead of a MPT_LOG(level, facility, text) call is just a @@ -72,13 +74,13 @@ inline mpt::ustring LogLevelToString(LogLevel level) { switch(level) { - case LogError: return MPT_USTRING("error"); break; - case LogWarning: return MPT_USTRING("warning"); break; - case LogNotification: return MPT_USTRING("notify"); break; - case LogInformation: return MPT_USTRING("info"); break; - case LogDebug: return MPT_USTRING("debug"); break; + case LogError: return U_("error"); break; + case LogWarning: return U_("warning"); break; + case LogNotification: return U_("notify"); break; + case LogInformation: return U_("info"); break; + case LogDebug: return U_("debug"); break; } - return MPT_USTRING("unknown"); + return U_("unknown"); } @@ -128,29 +130,6 @@ static MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) { retur #endif // !NO_LOGGING -struct Context -{ - const char * const file; - const int line; - const char * const function; - MPT_FORCEINLINE Context(const char *file, int line, const char *function) - : file(file) - , line(line) - , function(function) - { - return; - } - MPT_FORCEINLINE Context(const Context &c) - : file(c.file) - , line(c.line) - , function(c.function) - { - return; - } -}; // class Context - -#define MPT_LOG_CURRENTCONTEXT() mpt::log::Context( __FILE__ , __LINE__ , __FUNCTION__ ) - #ifndef NO_LOGGING @@ -159,13 +138,7 @@ class Logger { public: // facility:ASCII - void SendLogMessage(const Context &context, LogLevel level, const char *facility, const mpt::ustring &text); -public: - // facility:ASCII, text:ASCII (only string literals) - template MPT_FORCEINLINE void SendLogMessage(const Context &context, LogLevel level, const char *facility, const char (&text)[size]) - { - SendLogMessage(context, level, facility, mpt::ToUnicode(mpt::CharsetASCII, text)); - } + void SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text); }; #define MPT_LOG(level, facility, text) \ @@ -175,7 +148,7 @@ public: { \ MPT_MAYBE_CONSTANT_IF(mpt::log::IsFacilityActive(( facility ))) \ { \ - mpt::log::Logger().SendLogMessage( MPT_LOG_CURRENTCONTEXT() , ( level ), ( facility ), ( text )); \ + mpt::log::Logger().SendLogMessage( MPT_SOURCE_LOCATION_CURRENT() , ( level ), ( facility ), ( text )); \ } \ } \ } MPT_WHILE_0 \ @@ -187,15 +160,15 @@ public: class LegacyLogger : public Logger { private: - const Context context; + const mpt::source_location loc; public: - LegacyLogger(const Context &context) : context(context) {} + constexpr LegacyLogger(mpt::source_location loc) noexcept : loc(loc) {} /* MPT_DEPRECATED */ void MPT_PRINTF_FUNC(2,3) operator () (const char *format, ...); // migrate to type-safe MPT_LOG /* MPT_DEPRECATED */ void operator () (const AnyStringLocale &text); // migrate to properly namespaced MPT_LOG /* MPT_DEPRECATED */ void operator () (LogLevel level, const mpt::ustring &text); // migrate to properly namespaced MPT_LOG }; -#define Log MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < MPT_LEGACY_LOGLEVEL) { } else MPT_MAYBE_CONSTANT_IF(!mpt::log::IsFacilityActive("")) { } else mpt::log::LegacyLogger(MPT_LOG_CURRENTCONTEXT()) +#define Log MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < MPT_LEGACY_LOGLEVEL) { } else MPT_MAYBE_CONSTANT_IF(!mpt::log::IsFacilityActive("")) { } else mpt::log::LegacyLogger(MPT_SOURCE_LOCATION_CURRENT()) #else // !NO_LOGGING @@ -229,7 +202,14 @@ namespace Trace { extern bool volatile g_Enabled; static inline bool IsEnabled() { return g_Enabled; } -MPT_NOINLINE void Trace(const mpt::log::Context & contexxt); +enum class Direction : int8 +{ + Unknown = 0, + Enter = 1, + Leave = -1, +}; + +MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction = Direction::Unknown) noexcept; enum ThreadKind { ThreadKindGUI, @@ -247,12 +227,41 @@ uint32 GetThreadId(mpt::log::Trace::ThreadKind kind); void Seal(); bool Dump(const mpt::PathString &filename); -#define MPT_TRACE() MPT_DO { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_LOG_CURRENTCONTEXT()); } } MPT_WHILE_0 +class Scope +{ +private: + const mpt::source_location loc; +public: + MPT_FORCEINLINE Scope(mpt::source_location loc) noexcept + : loc(loc) + { + if(mpt::log::Trace::g_Enabled) + { + mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Enter); + } + } + MPT_FORCEINLINE ~Scope() noexcept + { + if(mpt::log::Trace::g_Enabled) + { + mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Leave); + } + } +}; + +#define MPT_TRACE_CONCAT_HELPER(x, y) x ## y +#define MPT_TRACE_CONCAT(x, y) MPT_TRACE_CONCAT_HELPER(x, y) + +#define MPT_TRACE_SCOPE() mpt::log::Trace::Scope MPT_TRACE_CONCAT(MPT_TRACE_VAR, __LINE__)(MPT_SOURCE_LOCATION_CURRENT()) + +#define MPT_TRACE() MPT_DO { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_SOURCE_LOCATION_CURRENT()); } } MPT_WHILE_0 } // namespace Trace #else // !MODPLUG_TRACKER +#define MPT_TRACE_SCOPE() MPT_DO { } MPT_WHILE_0 + #define MPT_TRACE() MPT_DO { } MPT_WHILE_0 #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp index 933097de4..037fd969e 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp @@ -112,7 +112,7 @@ std::string Profiler::DumpProfiles() case Profiler::Audio: cat = "Audio"; break; case Profiler::Notify: cat = "Notify"; break; } - ret += cat + " " + std::string(stats.profile.Name) + ": " + mpt::fmt::f("%6.3f", stats.usage * 100.0) + "%\r\n"; + ret += cat + " " + std::string(stats.profile.Name) + ": " + mpt::fmt::right(6, mpt::fmt::fix(stats.usage * 100.0, 3)) + "%\r\n"; } } ret += "\r\n"; diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.h b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h index fdde93b02..41a4b9008 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Profiler.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/mptMutex.h" #include diff --git a/Frameworks/OpenMPT/OpenMPT/common/StringFixer.h b/Frameworks/OpenMPT/OpenMPT/common/StringFixer.h deleted file mode 100644 index 00dfe71ac..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/StringFixer.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * StringFixer.h - * ------------- - * Purpose: Various functions for "fixing" char array strings for writing to or - * reading from module files, or for securing char arrays in general. - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - -#include -#include -#include - -OPENMPT_NAMESPACE_BEGIN - -namespace mpt { namespace String -{ - - -#if MPT_COMPILER_MSVC -#pragma warning(push) -#pragma warning(disable:4127) // conditional expression is constant -#endif // MPT_COMPILER_MSVC - - - // Sets last character to null in given char array. - // Size of the array must be known at compile time. - template - void SetNullTerminator(char (&buffer)[size]) - { - STATIC_ASSERT(size > 0); - buffer[size - 1] = 0; - } - - inline void SetNullTerminator(char *buffer, size_t size) - { - MPT_ASSERT(size > 0); - buffer[size - 1] = 0; - } - - template - void SetNullTerminator(wchar_t (&buffer)[size]) - { - STATIC_ASSERT(size > 0); - buffer[size - 1] = 0; - } - - inline void SetNullTerminator(wchar_t *buffer, size_t size) - { - MPT_ASSERT(size > 0); - buffer[size - 1] = 0; - } - - - // Remove any chars after the first null char - template - void FixNullString(char (&buffer)[size]) - { - STATIC_ASSERT(size > 0); - SetNullTerminator(buffer); - size_t pos = 0; - // Find the first null char. - while(pos < size && buffer[pos] != '\0') - { - pos++; - } - // Remove everything after the null char. - while(pos < size) - { - buffer[pos++] = '\0'; - } - } - - inline void FixNullString(std::string & str) - { - for(std::size_t i = 0; i < str.length(); ++i) - { - if(str[i] == '\0') - { - // if we copied \0 in the middle of the buffer, terminate std::string here - str.resize(i); - break; - } - } - } - - - enum ReadWriteMode - { - // Reading / Writing: Standard null-terminated string handling. - nullTerminated, - // Reading: Source string is not guaranteed to be null-terminated (if it fills the whole char array). - // Writing: Destination string is not guaranteed to be null-terminated (if it fills the whole char array). - maybeNullTerminated, - // Reading: String may contain null characters anywhere. They should be treated as spaces. - // Writing: A space-padded string is written. - spacePadded, - // Reading: String may contain null characters anywhere. The last character is ignored (it is supposed to be 0). - // Writing: A space-padded string with a trailing null is written. - spacePaddedNull - }; - - - namespace detail - { - static inline char NullToSpace(const char &c) - { - return (c != '\0') ? c : ' '; - } - } - - - // Copy a string from srcBuffer to destBuffer using a given read mode. - // Used for reading strings from files. - // Only use this version of the function if the size of the source buffer is variable. - template - void Read(std::string &dest, const Tbyte *srcBuffer, size_t srcSize) - { - - const char *src = mpt::byte_cast(srcBuffer); - - dest.clear(); - - if(mode == nullTerminated || mode == spacePaddedNull) - { - // We assume that the last character of the source buffer is null. - if(srcSize > 0) - { - srcSize -= 1; - } - } - - if(mode == nullTerminated || mode == maybeNullTerminated) - { - - // Copy null-terminated string, stopping at null. - try - { - dest.assign(src, std::find(src, src + srcSize, '\0')); - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); - } - - } else if(mode == spacePadded || mode == spacePaddedNull) - { - - try - { - // Copy string over. - dest.assign(src, src + srcSize); - - // Convert null characters to spaces. - std::transform(dest.begin(), dest.end(), dest.begin(), detail::NullToSpace); - - // Trim trailing spaces. - dest = mpt::String::RTrim(dest, std::string(" ")); - - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); - } - - } - } - - // Copy a charset encoded string from srcBuffer to destBuffer using a given read mode. - // Used for reading strings from files. - // Only use this version of the function if the size of the source buffer is variable. - template - void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte *srcBuffer, size_t srcSize) - { - std::string tmp; - Read(tmp, srcBuffer, srcSize); - dest = mpt::ToUnicode(charset, tmp); - } - - // Used for reading strings from files. - // Preferrably use this version of the function, it is safer. - template - void Read(std::string &dest, const Tbyte (&srcBuffer)[srcSize]) - { - STATIC_ASSERT(srcSize > 0); - Read(dest, srcBuffer, srcSize); - } - - // Used for reading charset encoded strings from files. - // Preferrably use this version of the function, it is safer. - template - void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte(&srcBuffer)[srcSize]) - { - std::string tmp; - Read(tmp, srcBuffer); - dest = mpt::ToUnicode(charset, tmp); - } - - // Copy a string from srcBuffer to destBuffer using a given read mode. - // Used for reading strings from files. - // Only use this version of the function if the size of the source buffer is variable. - template - void Read(char (&destBuffer)[destSize], const Tbyte *srcBuffer, size_t srcSize) - { - STATIC_ASSERT(destSize > 0); - - char *dst = destBuffer; - const char *src = mpt::byte_cast(srcBuffer); - - if(mode == nullTerminated || mode == spacePaddedNull) - { - // We assume that the last character of the source buffer is null. - if(srcSize > 0) - { - srcSize -= 1; - } - } - - if(mode == nullTerminated || mode == maybeNullTerminated) - { - - // Copy string and leave one character space in the destination buffer for null. - dst = std::copy(src, std::find(src, src + std::min(srcSize, destSize - 1), '\0'), dst); - - } else if(mode == spacePadded || mode == spacePaddedNull) - { - - // Copy string and leave one character space in the destination buffer for null. - // Convert nulls to spaces while copying. - dst = std::replace_copy(src, src + std::min(srcSize, destSize - 1), dst, '\0', ' '); - - // Rewind dst to the first of any trailing spaces. - while(dst - destBuffer > 0) - { - dst--; - char c = *dst; - if(c != ' ') - { - dst++; - break; - } - } - - } - - // Fill rest of string with nulls. - std::fill(dst, destBuffer + destSize, '\0'); - - } - - // Used for reading strings from files. - // Preferrably use this version of the function, it is safer. - template - void Read(char (&destBuffer)[destSize], const Tbyte (&srcBuffer)[srcSize]) - { - STATIC_ASSERT(destSize > 0); - STATIC_ASSERT(srcSize > 0); - Read(destBuffer, srcBuffer, srcSize); - } - - - // Copy a string from srcBuffer to destBuffer using a given write mode. - // You should only use this function if src and dest are dynamically sized, - // otherwise use one of the safer overloads below. - template - void Write(char *destBuffer, const size_t destSize, const char *srcBuffer, const size_t srcSize) - { - MPT_ASSERT(destSize > 0); - - const size_t maxSize = std::min(destSize, srcSize); - char *dst = destBuffer; - const char *src = srcBuffer; - - // First, copy over null-terminated string. - size_t pos = maxSize; - while(pos > 0) - { - if((*dst = *src) == '\0') - { - break; - } - pos--; - dst++; - src++; - } - - if(mode == nullTerminated || mode == maybeNullTerminated) - { - // Fill rest of string with nulls. - std::fill(dst, dst + destSize - maxSize + pos, '\0'); - } else if(mode == spacePadded || mode == spacePaddedNull) - { - // Fill the rest of the destination string with spaces. - std::fill(dst, dst + destSize - maxSize + pos, ' '); - } - - if(mode == nullTerminated || mode == spacePaddedNull) - { - // Make sure that destination is really null-terminated. - SetNullTerminator(destBuffer, destSize); - } - } - - // Copy a string from srcBuffer to a dynamically sized std::vector destBuffer using a given write mode. - // Used for writing strings to files. - // Only use this version of the function if the size of the source buffer is variable and the destination buffer also has variable size. - template - void Write(std::vector &destBuffer, const char *srcBuffer, const size_t srcSize) - { - MPT_ASSERT(destBuffer.size() > 0); - Write(destBuffer.data(), destBuffer.size(), srcBuffer, srcSize); - } - - // Copy a string from srcBuffer to destBuffer using a given write mode. - // Used for writing strings to files. - // Only use this version of the function if the size of the source buffer is variable. - template - void Write(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize) - { - STATIC_ASSERT(destSize > 0); - Write(destBuffer, destSize, srcBuffer, srcSize); - } - - // Copy a string from srcBuffer to destBuffer using a given write mode. - // Used for writing strings to files. - // Preferrably use this version of the function, it is safer. - template - void Write(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize]) - { - STATIC_ASSERT(destSize > 0); - STATIC_ASSERT(srcSize > 0); - Write(destBuffer, srcBuffer, srcSize); - } - - template - void Write(char *destBuffer, const size_t destSize, const std::string &src) - { - MPT_ASSERT(destSize > 0); - Write(destBuffer, destSize, src.c_str(), src.length()); - } - - template - void Write(std::vector &destBuffer, const std::string &src) - { - MPT_ASSERT(destBuffer.size() > 0); - Write(destBuffer, src.c_str(), src.length()); - } - - template - void Write(char (&destBuffer)[destSize], const std::string &src) - { - STATIC_ASSERT(destSize > 0); - Write(destBuffer, src.c_str(), src.length()); - } - - - // Copy from a char array to a fixed size char array. - template - void CopyN(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize = std::numeric_limits::max()) - { - const size_t copySize = std::min(destSize - 1u, srcSize); - std::strncpy(destBuffer, srcBuffer, copySize); - destBuffer[copySize] = '\0'; - } - - // Copy at most srcSize characters from srcBuffer to a std::string. - static inline void CopyN(std::string &dest, const char *srcBuffer, const size_t srcSize = std::numeric_limits::max()) - { - dest.assign(srcBuffer, srcBuffer + mpt::strnlen(srcBuffer, srcSize)); - } - - - // Copy from one fixed size char array to another one. - template - void Copy(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize]) - { - CopyN(destBuffer, srcBuffer, srcSize); - } - - // Copy from a std::string to a fixed size char array. - template - void Copy(char (&destBuffer)[destSize], const std::string &src) - { - CopyN(destBuffer, src.c_str(), src.length()); - } - - // Copy from a fixed size char array to a std::string. - template - void Copy(std::string &dest, const char (&srcBuffer)[srcSize]) - { - CopyN(dest, srcBuffer, srcSize); - } - - // Copy from a std::string to a std::string. - static inline void Copy(std::string &dest, const std::string &src) - { - dest.assign(src); - } - - -#if MPT_COMPILER_MSVC -#pragma warning(pop) -#endif // MPT_COMPILER_MSVC - - -} } // namespace mpt::String - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/WriteMemoryDump.h b/Frameworks/OpenMPT/OpenMPT/common/WriteMemoryDump.h deleted file mode 100644 index 7a48cd693..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/WriteMemoryDump.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * WriteMemoryDump.h - * ----------------- - * Purpose: Code for writing memory dumps to a file. - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - -#if MPT_COMPILER_MSVC -#pragma warning(push) -#pragma warning(disable:4091) // 'typedef ': ignored on left of '' when no variable is declared -#endif // MPT_COMPILER_MSVC -#include -#if MPT_COMPILER_MSVC -#pragma warning(pop) -#endif // MPT_COMPILER_MSVC - -OPENMPT_NAMESPACE_BEGIN - -typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, - CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam - ); - -static bool WriteMemoryDump(_EXCEPTION_POINTERS *pExceptionInfo, const WCHAR *filename, bool fullMemDump) -{ - bool result = false; - - HMODULE hDll = ::LoadLibraryW(L"DBGHELP.DLL"); - if (hDll) - { - MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump"); - if (pDump) - { - - HANDLE hFile = ::CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) - { - _MINIDUMP_EXCEPTION_INFORMATION ExInfo; - - if(pExceptionInfo) - { - ExInfo.ThreadId = ::GetCurrentThreadId(); - ExInfo.ExceptionPointers = pExceptionInfo; - ExInfo.ClientPointers = NULL; - } - - pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, - fullMemDump ? - (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithProcessThreadData | MiniDumpWithFullMemoryInfo -#if MPT_COMPILER_MSVC - | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation -#endif - ) - : - MiniDumpNormal, - pExceptionInfo ? &ExInfo : NULL, NULL, NULL); - ::CloseHandle(hFile); - - result = true; - } - } - ::FreeLibrary(hDll); - } - return result; -} - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp index a30b7fbfa..88319051a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp @@ -16,58 +16,41 @@ OPENMPT_NAMESPACE_BEGIN -#ifdef MODPLUG_TRACKER - -namespace Util -{ - - -#if MPT_OS_WINDOWS - - -#endif // MPT_OS_WINDOWS - - -} // namespace Util - -#endif // MODPLUG_TRACKER - - namespace Util { static const MPT_UCHAR_TYPE EncodeNibble[16] = { - MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), - MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), - MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('A'), MPT_UCHAR('B'), - MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F') }; + UC_('0'), UC_('1'), UC_('2'), UC_('3'), + UC_('4'), UC_('5'), UC_('6'), UC_('7'), + UC_('8'), UC_('9'), UC_('A'), UC_('B'), + UC_('C'), UC_('D'), UC_('E'), UC_('F') }; static inline bool DecodeByte(uint8 &byte, MPT_UCHAR_TYPE c1, MPT_UCHAR_TYPE c2) { byte = 0; - if(MPT_UCHAR('0') <= c1 && c1 <= MPT_UCHAR('9')) + if(UC_('0') <= c1 && c1 <= UC_('9')) { - byte += static_cast((c1 - MPT_UCHAR('0')) << 4); - } else if(MPT_UCHAR('A') <= c1 && c1 <= MPT_UCHAR('F')) + byte += static_cast((c1 - UC_('0')) << 4); + } else if(UC_('A') <= c1 && c1 <= UC_('F')) { - byte += static_cast((c1 - MPT_UCHAR('A') + 10) << 4); - } else if(MPT_UCHAR('a') <= c1 && c1 <= MPT_UCHAR('f')) + byte += static_cast((c1 - UC_('A') + 10) << 4); + } else if(UC_('a') <= c1 && c1 <= UC_('f')) { - byte += static_cast((c1 - MPT_UCHAR('a') + 10) << 4); + byte += static_cast((c1 - UC_('a') + 10) << 4); } else { return false; } - if(MPT_UCHAR('0') <= c2 && c2 <= MPT_UCHAR('9')) + if(UC_('0') <= c2 && c2 <= UC_('9')) { - byte += static_cast(c2 - MPT_UCHAR('0')); - } else if(MPT_UCHAR('A') <= c2 && c2 <= MPT_UCHAR('F')) + byte += static_cast(c2 - UC_('0')); + } else if(UC_('A') <= c2 && c2 <= UC_('F')) { - byte += static_cast(c2 - MPT_UCHAR('A') + 10); - } else if(MPT_UCHAR('a') <= c2 && c2 <= MPT_UCHAR('f')) + byte += static_cast(c2 - UC_('A') + 10); + } else if(UC_('a') <= c2 && c2 <= UC_('f')) { - byte += static_cast(c2 - MPT_UCHAR('a') + 10); + byte += static_cast(c2 - UC_('a') + 10); } else { return false; @@ -79,10 +62,10 @@ mpt::ustring BinToHex(mpt::const_byte_span src) { mpt::ustring result; result.reserve(src.size() * 2); - for(uint8 byte : src) + for(mpt::byte byte : src) { - result.push_back(EncodeNibble[(byte & 0xf0) >> 4]); - result.push_back(EncodeNibble[byte & 0x0f]); + result.push_back(EncodeNibble[(mpt::byte_cast(byte) & 0xf0) >> 4]); + result.push_back(EncodeNibble[mpt::byte_cast(byte) & 0x0f]); } return result; } @@ -98,7 +81,7 @@ std::vector HexToBin(const mpt::ustring &src) { return result; } - result.push_back(byte); + result.push_back(mpt::byte_cast(byte)); } return result; } diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.h b/Frameworks/OpenMPT/OpenMPT/common/misc_util.h index 9b357aa31..3415a508d 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/misc_util.h +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.h @@ -10,897 +10,39 @@ #pragma once +#include "BuildSettings.h" + +#include "mptAssert.h" +#include "mptBaseMacros.h" +#include "mptBaseTypes.h" +#include "mptBaseUtils.h" +#include "mptString.h" + +// old +#include "mptBaseUtils.h" +#include "mptSpan.h" +#include "mptMemory.h" +#include "mptExceptionText.h" +#include "mptStringFormat.h" #include "mptStringParse.h" #include "mptCPU.h" #include "mptOS.h" #include "mptTime.h" #include "mptLibrary.h" -#include "mptTypeTraits.h" -#include -#include -#include #include -#include #include -#include -#include #include OPENMPT_NAMESPACE_BEGIN -// cmath fixups -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 -#endif -#ifndef M_LN2 -#define M_LN2 0.69314718055994530942 -#endif - - - -namespace mpt { namespace String { - -// Combine a vector of values into a string, separated with the given separator. -// No escaping is performed. -template -mpt::ustring Combine(const std::vector &vals, const mpt::ustring &sep=MPT_USTRING(",")) -{ - mpt::ustring str; - for(std::size_t i = 0; i < vals.size(); ++i) - { - if(i > 0) - { - str += sep; - } - str += mpt::ufmt::val(vals[i]); - } - return str; -} -template -std::string Combine(const std::vector &vals, const std::string &sep=std::string(",")) -{ - std::string str; - for(std::size_t i = 0; i < vals.size(); ++i) - { - if(i > 0) - { - str += sep; - } - str += mpt::fmt::val(vals[i]); - } - return str; -} - -// Split the given string at separator positions into individual values returned as a vector. -// An empty string results in an empty vector. -// Leading or trailing separators result in a default-constructed element being inserted before or after the other elements. -template -std::vector Split(const mpt::ustring &str, const mpt::ustring &sep=MPT_USTRING(",")) -{ - std::vector vals; - std::size_t pos = 0; - while(str.find(sep, pos) != std::string::npos) - { - vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); - pos = str.find(sep, pos) + sep.length(); - } - if(!vals.empty() || (str.substr(pos).length() > 0)) - { - vals.push_back(ConvertStrTo(str.substr(pos))); - } - return vals; -} -template -std::vector Split(const std::string &str, const std::string &sep=std::string(",")) -{ - std::vector vals; - std::size_t pos = 0; - while(str.find(sep, pos) != std::string::npos) - { - vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); - pos = str.find(sep, pos) + sep.length(); - } - if(!vals.empty() || (str.substr(pos).length() > 0)) - { - vals.push_back(ConvertStrTo(str.substr(pos))); - } - return vals; -} - -} } // namespace mpt::String - - -namespace mpt { - -// GCC 4.5 and up provides templated overloads of std::abs that convert -// integer type narrower than int to double. -// As this is apparently valid by the current standard, Library Working Group -// Issue #2735 has been filed (see -// ). -// In any case, avoid this insanity and provide our own mpt::abs implementation -// for signed integer and floating point types. -// Note: We stick to a C++98-style implementation only overloading int and -// greater types in order to keep promotion rules consistent for narrower types, -// which a templated version returning the argument type would not do. OpenMPT -// probably assumes this semantic when calling abs(int8) in various places. -inline int abs(int x) -{ - return std::abs(x); -} -inline long abs(long x) -{ - return std::abs(x); -} -inline long long abs(long long x) -{ - return std::abs(x); -} -inline float abs(float x) -{ - return std::fabs(x); -} -inline double abs(double x) -{ - return std::fabs(x); -} -inline long double abs(long double x) -{ - return std::fabs(x); -} - -// Modulo with more intuitive behaviour for some contexts: -// Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range. -// For example, wrapping_modulo(-1, m) == (m - 1). -// Behaviour is undefined if m<=0. -template -MPT_CONSTEXPR11_FUN auto wrapping_modulo(T x, M m) -> decltype(x % m) -{ - return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m)); -} - -template -MPT_CONSTEXPR11_FUN auto wrapping_divide(T x, D d) -> decltype(x / d) -{ - return (x >= 0) ? (x / d) : (((x + 1) / d) - 1); -} - -} // namespace mpt - - -// Memset given object to zero. -template -inline void MemsetZero(T &a) -{ - static_assert(std::is_pointer::value == false, "Won't memset pointers."); -#if MPT_GCC_BEFORE(5,1,0) || MPT_CLANG_BEFORE(3,5,0) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) - MPT_STATIC_ASSERT(std::is_standard_layout::value); - MPT_STATIC_ASSERT(std::is_trivial::value); // approximation -#else // default - MPT_STATIC_ASSERT(std::is_standard_layout::value); - MPT_STATIC_ASSERT(std::is_trivially_copyable::value); // C++11, but not supported on most compilers we care about -#endif - std::memset(&a, 0, sizeof(T)); -} - - -#ifdef MODPLUG_TRACKER -// Copy given object to other location. -template -void MemCopy(T &destination, const T &source) -{ - static_assert(std::is_pointer::value == false, "Won't copy pointers."); -#if MPT_GCC_BEFORE(5,1,0) || MPT_CLANG_BEFORE(3,5,0) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) - MPT_STATIC_ASSERT(std::is_trivial::value); // approximation -#else // default - MPT_STATIC_ASSERT(std::is_trivially_copyable::value); // C++11, but not supported on most compilers we care about -#endif - std::memcpy(&destination, &source, sizeof(T)); -} -#endif // MODPLUG_TRACKER - - -namespace mpt { - - -// Simplified version of gsl::span. -// Non-owning read-only or read-write view into a contiguous block of T -// objects, i.e. equivalent to a (beg,end) or (data,size) tuple. -// Can eventually be replaced without further modifications with a full -// gsl::span. -template -class span -{ - -public: - - typedef std::size_t size_type; - - typedef T value_type; - typedef T & reference; - typedef T * pointer; - typedef const T * const_pointer; - typedef const T & const_reference; - - typedef pointer iterator; - typedef const_pointer const_iterator; - - typedef typename std::iterator_traits::difference_type difference_type; - -private: - - T * m_beg; - T * m_end; - -public: - - span() : m_beg(nullptr), m_end(nullptr) { } - - span(pointer beg, pointer end) : m_beg(beg), m_end(end) { } - - span(pointer data, size_type size) : m_beg(data), m_end(data + size) { } - - template span(U (&arr)[N]) : m_beg(arr), m_end(arr + N) { } - - template span(Cont &cont) : m_beg(cont.empty() ? nullptr : &(cont[0])), m_end(cont.empty() ? nullptr : &(cont[0]) + cont.size()) { } - - span(const span &other) : m_beg(other.begin()), m_end(other.end()) { } - - template span(const span &other) : m_beg(other.begin()), m_end(other.end()) { } - - span & operator = (span other) { m_beg = other.begin(); m_end = other.end(); return *this; } - - iterator begin() const { return iterator(m_beg); } - iterator end() const { return iterator(m_end); } - - const_iterator cbegin() const { return const_iterator(begin()); } - const_iterator cend() const { return const_iterator(end()); } - - operator bool () const throw() { return m_beg != nullptr; } - - reference operator[](size_type index) { return at(index); } - const_reference operator[](size_type index) const { return at(index); } - - bool operator==(span const & other) const throw() { return size() == other.size() && (m_beg == other.m_beg || std::equal(begin(), end(), other.begin())); } - bool operator!=(span const & other) const throw() { return !(*this == other); } - - reference at(size_type index) { return m_beg[index]; } - const_reference at(size_type index) const { return m_beg[index]; } - - pointer data() const throw() { return m_beg; } - - bool empty() const throw() { return size() == 0; } - - size_type size() const throw() { return std::distance(m_beg, m_end); } - size_type length() const throw() { return size(); } - -}; // class span - -template inline span as_span(T * beg, T * end) { return span(beg, end); } - -template inline span as_span(T * data, std::size_t size) { return span(data, size); } - -template inline span as_span(T (&arr)[N]) { return span(std::begin(arr), std::end(arr)); } - -template inline span as_span(std::vector & cont) { return span(cont); } - -template inline span as_span(const std::vector & cont) { return span(cont); } - -template inline span as_span(std::basic_string & str) { return span(&(str[0]), str.length()); } - -template inline span as_span(const std::basic_string & str) { return span(&(str[0]), str.length()); } - - -typedef mpt::span byte_span; -typedef mpt::span const_byte_span; - - - -template inline std::vector::type> make_vector(T * beg, T * end) { return std::vector::type>(beg, end); } - -template inline std::vector::type> make_vector(T * data, std::size_t size) { return std::vector::type>(data, data + size); } - -template inline std::vector::type> make_vector(mpt::span data) { return std::vector::type>(data.data(), data.data() + data.size()); } - -template inline std::vector::type> make_vector(T (&arr)[N]) { return std::vector::type>(std::begin(arr), std::end(arr)); } - -template inline std::vector::type> make_vector(const std::basic_string & str) { return std::vector::type>(str.begin(), str.end()); } - - - -template -struct byte_cast_impl -{ - inline Tdst operator () (Tsrc src) const - { - STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); - STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); - // not checking is_byte_castable here because we are actually - // doing a static_cast and converting the value - STATIC_ASSERT(std::is_integral::value); - STATIC_ASSERT(std::is_integral::value); - return static_cast(src); - } -}; -template -struct byte_cast_impl, mpt::span > -{ - inline mpt::span operator () (mpt::span src) const - { - STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); - STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(std::is_integral::value); - STATIC_ASSERT(std::is_integral::value); - return mpt::as_span(mpt::byte_cast_impl()(src.begin()), mpt::byte_cast_impl()(src.end())); - } -}; -template -struct byte_cast_impl -{ - inline Tdst* operator () (Tsrc* src) const - { - STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); - STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(std::is_integral::value); - STATIC_ASSERT(std::is_integral::value); - return reinterpret_cast(src); - } -}; - -template -struct void_cast_impl; - -template -struct void_cast_impl -{ - inline Tdst* operator () (void* src) const - { - STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(std::is_integral::value); - return reinterpret_cast(src); - } -}; -template -struct void_cast_impl -{ - inline Tdst* operator () (const void* src) const - { - STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(std::is_integral::value); - return reinterpret_cast(src); - } -}; -template -struct void_cast_impl -{ - inline void* operator () (Tsrc* src) const - { - STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(std::is_integral::value); - return reinterpret_cast(src); - } -}; -template -struct void_cast_impl -{ - inline const void* operator () (Tsrc* src) const - { - STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); - STATIC_ASSERT(mpt::is_byte_castable::value); - STATIC_ASSERT(std::is_integral::value); - return reinterpret_cast(src); - } -}; - -// casts between different byte (char) types or pointers to these types -template -inline Tdst byte_cast(Tsrc src) -{ - return byte_cast_impl()(src); -} - -// casts between pointers to void and pointers to byte -template -inline Tdst void_cast(Tsrc src) -{ - return void_cast_impl()(src); -} - - -// Saturate the value of src to the domain of Tdst -template -inline Tdst saturate_cast(Tsrc src) -{ - // This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting. - STATIC_ASSERT(std::numeric_limits::is_integer); - STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_CONSTANT_IF(std::numeric_limits::is_signed && std::numeric_limits::is_signed) - { - MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) - { - return static_cast(src); - } - return static_cast(std::max(static_cast(std::numeric_limits::min()), std::min(src, static_cast(std::numeric_limits::max())))); - } else MPT_CONSTANT_IF(!std::numeric_limits::is_signed && !std::numeric_limits::is_signed) - { - MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) - { - return static_cast(src); - } - return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); - } else MPT_CONSTANT_IF(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) - { - MPT_CONSTANT_IF(sizeof(Tdst) > sizeof(Tsrc)) - { - return static_cast(src); - } - MPT_CONSTANT_IF(sizeof(Tdst) == sizeof(Tsrc)) - { - return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); - } - return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); - } else // Tdst unsigned, Tsrc signed - { - MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) - { - return static_cast(std::max(0, src)); - } - return static_cast(std::max(0, std::min(src, static_cast(std::numeric_limits::max())))); - } -} - -template -inline Tdst saturate_cast(double src) -{ - if(src >= std::numeric_limits::max()) - { - return std::numeric_limits::max(); - } - if(src <= std::numeric_limits::min()) - { - return std::numeric_limits::min(); - } - return static_cast(src); -} - -template -inline Tdst saturate_cast(float src) -{ - if(src >= std::numeric_limits::max()) - { - return std::numeric_limits::max(); - } - if(src <= std::numeric_limits::min()) - { - return std::numeric_limits::min(); - } - return static_cast(src); -} - - -} // namespace mpt - - -#if defined(MODPLUG_TRACKER) -// Tracker code requires MIN/MAX to work in constexpr contexts. -// We could make MIN/MAX constexpr for supporting compilers, -// but that would just needlessly complicate the support matrix -// for now. -#ifndef MPT_MINMAX_MACROS -#define MPT_MINMAX_MACROS -#endif -#endif - -#if MPT_COMPILER_MSVC -// MSVC disables a bunch of type conversion warnings once a macro is involved. -// Replacing the macro with a template thus spews a TON OF WARNINGS for now. -#ifndef MPT_MINMAX_MACROS -#define MPT_MINMAX_MACROS -#endif -#endif - -#if defined(MPT_MINMAX_MACROS) - -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) - -#else - -namespace mpt { namespace Legacy { - -template -MPT_FORCEINLINE auto MAX(const Ta &a, const Tb &b) -> decltype((a>b)?a:b) -{ - return (a > b) ? a : b; -} - -template -MPT_FORCEINLINE auto MIN(const Ta &a, const Tb &b) -> decltype((a -struct ModIfNotZeroImpl -{ - template - inline Tval mod(Tval x) - { - STATIC_ASSERT(std::numeric_limits::is_integer); - STATIC_ASSERT(!std::numeric_limits::is_signed); - STATIC_ASSERT(std::numeric_limits::is_integer); - STATIC_ASSERT(!std::numeric_limits::is_signed); - return static_cast(x % m); - } -}; -template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; -template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; -template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; -template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; -} // namespace detail -// Returns x % m if m != 0, x otherwise. -// i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers -template -inline Tval ModIfNotZero(Tval x) -{ - return detail::ModIfNotZeroImpl().mod(x); -} - -// Returns true iff Tdst can represent the value val. -// Use as if(Util::TypeCanHoldValue(-1)). -template -inline bool TypeCanHoldValue(Tsrc val) -{ - return (static_cast(mpt::saturate_cast(val)) == val); -} - -// Grows x with an exponential factor suitable for increasing buffer sizes. -// Clamps the result at limit. -// And avoids integer overflows while doing its business. -// The growth factor is 1.5, rounding down, execpt for the initial x==1 case. -template -inline T ExponentialGrow(const T &x, const Tlimit &limit) -{ - MPT_ASSERT(x > 0); - MPT_ASSERT(limit > 0); - if(x == 1) - { - return 2; - } - T add = std::min(x >> 1, std::numeric_limits::max() - x); - return std::min(x + add, mpt::saturate_cast(limit)); -} - -template -inline T ExponentialGrow(const T &x) -{ - return Util::ExponentialGrow(x, std::numeric_limits::max()); -} - -} //namespace Util - - -namespace mpt -{ - -// C++17 clamp - -template -MPT_CONSTEXPR11_FUN const T & clamp(const T & v, const T & lo, const T & hi, Compare comp) -{ - return comp(v, lo) ? lo : comp(hi, v) ? hi : v; -} - -template -MPT_CONSTEXPR11_FUN const T & clamp(const T & v, const T & lo, const T & hi) -{ - return mpt::clamp(v, lo, hi, std::less()); -} - -} // namespace mpt - - -// Limits 'val' to given range. If 'val' is less than 'lowerLimit', 'val' is set to value 'lowerLimit'. -// Similarly if 'val' is greater than 'upperLimit', 'val' is set to value 'upperLimit'. -// If 'lowerLimit' > 'upperLimit', 'val' won't be modified. -template -inline void Limit(T& val, const C lowerLimit, const C upperLimit) -{ - if(lowerLimit > upperLimit) return; - if(val < lowerLimit) val = lowerLimit; - else if(val > upperLimit) val = upperLimit; -} - - -// Like Limit, but returns value -template -inline T Clamp(T val, const C lowerLimit, const C upperLimit) -{ - if(val < lowerLimit) return lowerLimit; - else if(val > upperLimit) return upperLimit; - else return val; -} - -// Check if val is in [lo,hi] without causing compiler warnings -// if theses checks are always true due to the domain of T. -// GCC does not warn if the type is templated. -template -inline bool IsInRange(T val, C lo, C hi) -{ - return lo <= val && val <= hi; -} - -// Like Limit, but with upperlimit only. -template -inline void LimitMax(T& val, const C upperLimit) -{ - if(val > upperLimit) - val = upperLimit; -} - - -// Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0) -template -int sgn(T value) -{ - return (value > T(0)) - (value < T(0)); -} - - - -// mpt::rshift_signed -// mpt::lshift_signed -// Shift a signed integer value in a well-defined manner. -// Does the same thing as MSVC would do. This is verified by the test suite. - -namespace mpt -{ - -template -MPT_FORCEINLINE auto rshift_signed_standard(T x, int y) -> decltype(x >> y) -{ - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::numeric_limits::is_signed); - typedef decltype(x >> y) result_type; - typedef typename std::make_unsigned::type unsigned_result_type; - const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); - result_type rx = x; - unsigned_result_type urx = static_cast(rx); - urx += roffset; - urx >>= y; - urx -= roffset >> y; - return static_cast(urx); -} - -template -MPT_FORCEINLINE auto lshift_signed_standard(T x, int y) -> decltype(x << y) -{ - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::numeric_limits::is_signed); - typedef decltype(x << y) result_type; - typedef typename std::make_unsigned::type unsigned_result_type; - const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); - result_type rx = x; - unsigned_result_type urx = static_cast(rx); - urx += roffset; - urx <<= y; - urx -= roffset << y; - return static_cast(urx); -} - -#if MPT_COMPILER_SHIFT_SIGNED - -template -MPT_FORCEINLINE auto rshift_signed_undefined(T x, int y) -> decltype(x >> y) -{ - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::numeric_limits::is_signed); - return x >> y; -} - -template -MPT_FORCEINLINE auto lshift_signed_undefined(T x, int y) -> decltype(x << y) -{ - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::numeric_limits::is_signed); - return x << y; -} - -template -MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) -{ - return mpt::rshift_signed_undefined(x, y); -} - -template -MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) -{ - return mpt::lshift_signed_undefined(x, y); -} - -#else - -template -MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) -{ - return mpt::rshift_signed_standard(x, y); -} - -template -MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) -{ - return mpt::lshift_signed_standard(x, y); -} - -#endif - -} // namespace mpt - - - -namespace Util -{ - - // Returns maximum value of given integer type. - template constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits::max)();} - - // The following MPT_MAX_* macros are useful as std::numeric_limits is not - // usable in constexpr-like contexts like static_assert in pre-C++11 - // compilers. - - // Returns the maximum value for the signed type or expression at compile time. - #define MPT_MAX_SIGNED_VALUE(integral_expression_or_type) ( ( 1ull << ( sizeof(integral_expression_or_type) * 8 - 1 ) ) - 1 ) - - // Returns the maximum value for the unsigned type or expression at compile time. - // Implemented in terms of MPT_MAX_SIGNED_VALUE in order to avoid overflow in left-shift. - #define MPT_MAX_UNSIGNED_VALUE(integral_expression_or_type) ( ( MPT_MAX_SIGNED_VALUE(integral_expression_or_type) << 1 ) | 1ull ) - - // Return the maximum value of an integral type at compile time. - #define MPT_MAX_VALUE_OF_TYPE(integral_type) ( std::numeric_limits::is_signed ? MPT_MAX_SIGNED_VALUE(integral_type) : MPT_MAX_UNSIGNED_VALUE(integral_type) ) - - /// Returns value rounded to nearest integer. -#if (MPT_OS_EMSCRIPTEN && MPT_OS_EMSCRIPTEN_ANCIENT) - // MSVC before 2013 does not support C99/C++11. - // Certain emscripten versions and/or combinations with nodejs (at least the following combination: emscripten 1.34.8, clang 3.7.0, nodejs 0.10.38) fail assert(std::round(1.5)==2.0). The work-around always works. - inline double Round(const double& val) {if(val >= 0.0) return std::floor(val + 0.5); else return std::ceil(val - 0.5);} - inline float Round(const float& val) {if(val >= 0.0f) return std::floor(val + 0.5f); else return std::ceil(val - 0.5f);} -#elif MPT_OS_ANDROID && defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION) - // NDK 12b gnustl_shared armeabi-v7a only provides round() in ::. - // NDK 12b gnustl_shared arm64-v8a has round() in std::. - // NDK 12b c++_shared armeabi-v7a has round() in std::. - // Just fallback to :: for Android gnustl_shared. - // This work-around can be removed once Android switches to LLVM libc++. - // Currently (ndk-r12b), libc++ has problems with exceptions. - inline double Round(const double& val) { return ::round(val); } - inline float Round(const float& val) { return ::roundf(val); } -#else - inline double Round(const double& val) { return std::round(val); } - inline float Round(const float& val) { return std::round(val); } -#endif - - /// Rounds given double value to nearest integer value of type T. - template inline T Round(const double& val) - { - static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); - const double valRounded = Round(val); - MPT_ASSERT(valRounded >= (std::numeric_limits::min)() && valRounded <= (std::numeric_limits::max)()); - const T intval = static_cast(valRounded); - return intval; - } - - template inline T Round(const float& val) - { - static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); - const float valRounded = Round(val); - MPT_ASSERT(valRounded >= (std::numeric_limits::min)() && valRounded <= (std::numeric_limits::max)()); - const T intval = static_cast(valRounded); - return intval; - } - - template - T Weight(T x) - { - STATIC_ASSERT(std::numeric_limits::is_integer); - T c; - for(c = 0; x; x >>= 1) - { - c += x & 1; - } - return c; - } - -} - -namespace Util { - - // Multiply two 32-bit integers, receive 64-bit result. - // MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul. - MPT_FORCEINLINE int64 mul32to64(int32 a, int32 b) - { -#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) - return __emul(a, b); -#else - return static_cast(a) * b; -#endif - } - - MPT_FORCEINLINE uint64 mul32to64_unsigned(uint32 a, uint32 b) - { -#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) - return __emulu(a, b); -#else - return static_cast(a) * b; -#endif - } - - MPT_FORCEINLINE int32 muldiv(int32 a, int32 b, int32 c) - { - return mpt::saturate_cast( mul32to64( a, b ) / c ); - } - - MPT_FORCEINLINE int32 muldivr(int32 a, int32 b, int32 c) - { - return mpt::saturate_cast( ( mul32to64( a, b ) + ( c / 2 ) ) / c ); - } - - // Do not use overloading because catching unsigned version by accident results in slower X86 code. - MPT_FORCEINLINE uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c) - { - return mpt::saturate_cast( mul32to64_unsigned( a, b ) / c ); - } - MPT_FORCEINLINE uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c) - { - return mpt::saturate_cast( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c ); - } - - MPT_FORCEINLINE int32 muldivrfloor(int64 a, uint32 b, uint32 c) - { - a *= b; - a += c / 2u; - return (a >= 0) ? mpt::saturate_cast(a / c) : mpt::saturate_cast((a - (c - 1)) / c); - } - - // rounds x up to multiples of target - template - inline T AlignUp(T x, T target) - { - return ((x + (target - 1)) / target) * target; - } - - // rounds x down to multiples of target - template - inline T AlignDown(T x, T target) - { - return (x / target) * target; - } - // Insert a range of items [insStart, insEnd], and possibly shift item fix to the left. template void InsertItem(const T insStart, const T insEnd, T &fix) @@ -969,49 +111,6 @@ namespace Util { } // namespace Util -namespace mpt -{ - - // Greatest Common Divisor. Always returns non-negative number. - // compatible with C++17 std::gcd - template - inline typename std::common_type::type gcd(A a_, B b_) - { - typename std::common_type::type a = a_; - typename std::common_type::type b = b_; - if(a < 0) - a = -a; - if(b < 0) - b = -b; - for(;;) - { - if(a == 0) - return b; - b %= a; - if(b == 0) - return a; - a %= b; - } - } - - // Least Common Multiple. Always returns non-negative number. - // compatible with C++17 std::lcm - template - inline typename std::common_type::type lcm(A a_, B b_) - { - typename std::common_type::type a = a_; - typename std::common_type::type b = b_; - if(a < 0) - a = -a; - if(b < 0) - b = -b; - if((a | b) == 0) - return 0; - return a / mpt::gcd(a, b) * b; - } - -} // namespace mpt - namespace Util { diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp new file mode 100644 index 000000000..431887f3a --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp @@ -0,0 +1,121 @@ +/* + * mptAlloc.cpp + * ------------ + * Purpose: Dynamic memory allocation. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + + +#include "stdafx.h" +#include "mptAlloc.h" + +#include "mptBaseTypes.h" + +#include +#include + +#include +#include + +#if MPT_COMPILER_MSVC +#include +#endif + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + + + +#if MPT_CXX_AT_LEAST(17) +#else +void* align(std::size_t alignment, std::size_t size, void* &ptr, std::size_t &space) noexcept +{ + std::size_t offset = static_cast(reinterpret_cast(ptr) & (alignment - 1)); + if(offset != 0) + { + offset = alignment - offset; + } + if((space < offset) || ((space - offset) < size)) + { + return nullptr; + } + ptr = static_cast(ptr) + offset; + space -= offset; + return ptr; +} +#endif + +aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std::size_t alignment) +{ + #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !MPT_OS_EMSCRIPTEN + std::size_t space = count * size; + void* mem = std::aligned_alloc(alignment, space); + if(!mem) + { + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + } + return aligned_raw_memory{mem, mem}; + #elif MPT_COMPILER_MSVC + std::size_t space = count * size; + void* mem = _aligned_malloc(space, alignment); + if(!mem) + { + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + } + return aligned_raw_memory{mem, mem}; + #else + if(alignment > alignof(mpt::max_align_t)) + { + std::size_t space = count * size + (alignment - 1); + void* mem = std::malloc(space); + if(!mem) + { + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + } + void* aligned_mem = mem; + void* aligned = mpt::align(alignment, size * count, aligned_mem, space); + if(!aligned) + { + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + } + return aligned_raw_memory{aligned, mem}; + } else + { + std::size_t space = count * size; + void* mem = std::malloc(space); + if(!mem) + { + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + } + return aligned_raw_memory{mem, mem}; + } + #endif +} + +void aligned_free(aligned_raw_memory raw) +{ + #if MPT_CXX_AT_LEAST(17) && (!MPT_COMPILER_MSVC && !MPT_GCC_BEFORE(8,1,0) && !MPT_CLANG_BEFORE(5,0,0)) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !MPT_OS_EMSCRIPTEN + std::free(raw.mem); + #elif MPT_COMPILER_MSVC + _aligned_free(raw.mem); + #else + std::free(raw.mem); + #endif +} + + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h new file mode 100644 index 000000000..1d616b4b5 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h @@ -0,0 +1,262 @@ +/* + * mptAlloc.h + * ---------- + * Purpose: Dynamic memory allocation. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptBaseMacros.h" +#include "mptMemory.h" +#include "mptSpan.h" + +#include +#include +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + +template inline span as_span(std::vector & cont) { return span(cont); } + +template inline span as_span(const std::vector & cont) { return span(cont); } + + + +template inline std::vector::type> make_vector(T * beg, T * end) { return std::vector::type>(beg, end); } + +template inline std::vector::type> make_vector(T * data, std::size_t size) { return std::vector::type>(data, data + size); } + +template inline std::vector::type> make_vector(mpt::span data) { return std::vector::type>(data.data(), data.data() + data.size()); } + +template inline std::vector::type> make_vector(T (&arr)[N]) { return std::vector::type>(std::begin(arr), std::end(arr)); } + + + +template +struct GetRawBytesFunctor> +{ + inline mpt::const_byte_span operator () (const std::vector & v) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); + } + inline mpt::byte_span operator () (std::vector & v) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); + } +}; + +template +struct GetRawBytesFunctor> +{ + inline mpt::const_byte_span operator () (const std::vector & v) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); + } +}; + + + +} // namespace mpt + + + +#if MPT_CXX_AT_LEAST(14) +namespace mpt { +using std::make_unique; +} // namespace mpt +#else +namespace mpt { +template +std::unique_ptr make_unique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} +} // namespace mpt +#endif + + + +namespace mpt +{ + + + +#if MPT_CXX_AT_LEAST(17) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) +using std::launder; +#else +template +MPT_NOINLINE T* launder(T* p) noexcept +{ + return p; +} +#endif + + +#if MPT_CXX_AT_LEAST(17) +using std::align; +#else +// pre-C++17, std::align does not support over-alignement +void* align(std::size_t alignment, std::size_t size, void* &ptr, std::size_t &space) noexcept; +#endif + + + +struct aligned_raw_memory +{ + void* aligned; + void* mem; +}; + +aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std::size_t alignment); + +template +inline aligned_raw_memory aligned_alloc(std::size_t size, std::size_t count) +{ + MPT_STATIC_ASSERT(alignment > 0); + MPT_CONSTEXPR14_ASSERT(mpt::weight(alignment) == 1); + return aligned_alloc_impl(size, count, alignment); +} + +void aligned_free(aligned_raw_memory raw); + +template +struct aligned_raw_buffer +{ + T* elements; + void* mem; +}; + +template +inline aligned_raw_buffer aligned_alloc(std::size_t count) +{ + MPT_STATIC_ASSERT(alignment >= alignof(T)); + aligned_raw_memory raw = aligned_alloc(sizeof(T), count); + return aligned_raw_buffer{mpt::launder(reinterpret_cast(raw.aligned)), raw.mem}; +} + +template +inline void aligned_free(aligned_raw_buffer buf) +{ + aligned_free(aligned_raw_memory{buf.elements, buf.mem}); +} + +template +struct aligned_raw_objects +{ + T* elements; + std::size_t count; + void* mem; +}; + +template +inline aligned_raw_objects aligned_new(std::size_t count, T init = T()) +{ + aligned_raw_buffer buf = aligned_alloc(count); + std::size_t constructed = 0; + try + { + for(std::size_t i = 0; i < count; ++i) + { + new(&(buf.elements[i])) T(init); + constructed++; + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + while(constructed--) + { + mpt::launder(&(buf.elements[constructed - 1]))->~T(); + } + aligned_free(buf); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(...) + { + while(constructed--) + { + mpt::launder(&(buf.elements[constructed - 1]))->~T(); + } + aligned_free(buf); + throw; + } + return aligned_raw_objects{mpt::launder(buf.elements), count, buf.mem}; +} + +template +inline void aligned_delete(aligned_raw_objects objs) +{ + if(objs.elements) + { + std::size_t constructed = objs.count; + while(constructed--) + { + objs.elements[constructed - 1].~T(); + } + } + aligned_free(aligned_raw_buffer{objs.elements, objs.mem}); +} + +template +class aligned_buffer +{ +private: + aligned_raw_objects objs; +public: + explicit aligned_buffer(std::size_t count = 0) + : objs(aligned_new(count)) + { + } + aligned_buffer(const aligned_buffer&) = delete; + aligned_buffer& operator=(const aligned_buffer&) = delete; + ~aligned_buffer() + { + aligned_delete(objs); + } +public: + void destructive_resize(std::size_t count) + { + aligned_raw_objects tmpobjs = aligned_new(count); + { + using namespace std; + swap(objs, tmpobjs); + } + aligned_delete(tmpobjs); + } +public: + T* begin() noexcept { return objs.elements; } + const T* begin() const noexcept { return objs.elements; } + T* end() noexcept { return objs.elements + objs.count; } + const T* end() const noexcept { return objs.elements + objs.count; } + const T* cbegin() const noexcept { return objs.elements; } + const T* cend() const noexcept { return objs.elements + objs.count; } + T& operator[](std::size_t i) noexcept { return objs.elements[i]; } + const T& operator[](std::size_t i) const noexcept { return objs.elements[i]; } + T* data() noexcept { return objs.elements; } + const T* data() const noexcept { return objs.elements; } + std::size_t size() const noexcept { return objs.count; } +}; + + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h b/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h new file mode 100644 index 000000000..dca2b3dfd --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h @@ -0,0 +1,153 @@ +/* + * mptAssert.h + * ----------- + * Purpose: assert and static_assert + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptBaseMacros.h" + + + +OPENMPT_NAMESPACE_BEGIN + + + +// Static code checkers might need to get the knowledge of our assertions transferred to them. +#define MPT_CHECKER_ASSUME_ASSERTIONS 1 +//#define MPT_CHECKER_ASSUME_ASSERTIONS 0 + +#ifdef MPT_BUILD_ANALYZED + +#if MPT_COMPILER_MSVC + +#if MPT_CHECKER_ASSUME_ASSERTIONS +#define MPT_CHECKER_ASSUME(x) __analysis_assume(!!(x)) +#endif + +#elif MPT_COMPILER_CLANG + +#if MPT_CHECKER_ASSUME_ASSERTIONS +#ifdef NDEBUG +#error "Builds for static analyzers depend on assert() being enabled, but the current build has #define NDEBUG. This makes no sense." +#endif +OPENMPT_NAMESPACE_END +#include +OPENMPT_NAMESPACE_BEGIN +#define MPT_CHECKER_ASSUME(x) assert(!!(x)) +#endif + +#endif // MPT_COMPILER + +#endif // MPT_BUILD_ANALYZED + +#ifndef MPT_CHECKER_ASSUME +#define MPT_CHECKER_ASSUME(x) MPT_DO { } MPT_WHILE_0 +#endif + + + +#if defined(_MFC_VER) && !defined(MPT_CPPCHECK_CUSTOM) + +#if !defined(ASSERT) +#error "MFC is expected to #define ASSERT" +#endif // !defined(ASERRT) +#define MPT_FRAMEWORK_ASSERT_IS_DEFINED + +#if defined(_DEBUG) + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 +#else // !_DEBUG + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 +#endif // _DEBUG + +// let MFC handle our asserts +#define MPT_ASSERT_USE_FRAMEWORK 1 + +#else // !_MFC_VER + +#if defined(ASSERT) +#define MPT_FRAMEWORK_ASSERT_IS_DEFINED +#if defined(_DEBUG) + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 +#else // !_DEBUG + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 +#endif // _DEBUG +#endif // !defined(ASERRT) + +// handle assert in our own way without relying on some platform-/framework-specific assert implementation +#define MPT_ASSERT_USE_FRAMEWORK 0 + +#endif // _MFC_VER + +#if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1) + +#define MPT_ASSERT_NOTREACHED() ASSERT(0) +#define MPT_ASSERT(expr) ASSERT((expr)) +#define MPT_ASSERT_MSG(expr, msg) ASSERT((expr) && (msg)) +#if (MPT_FRAMEWORK_ASSERT_IS_ACTIVE == 1) +#define MPT_ASSERT_ALWAYS(expr) ASSERT((expr)) +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) ASSERT((expr) && (msg)) +#else +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif +#endif + +#elif defined(NO_ASSERTS) + +#define MPT_ASSERT_NOTREACHED() MPT_CHECKER_ASSUME(0) +#define MPT_ASSERT(expr) MPT_CHECKER_ASSUME(expr) +#define MPT_ASSERT_MSG(expr, msg) MPT_CHECKER_ASSUME(expr) +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif + +#else // !NO_ASSERTS + +#define MPT_ASSERT_NOTREACHED() MPT_DO { MPT_CONSTANT_IF(!(0)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0 +#define MPT_ASSERT(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif + +#endif // NO_ASSERTS + + +#if defined(MPT_ASSERT_HANDLER_NEEDED) +// custom assert handler needed +MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg=nullptr); +#endif // MPT_ASSERT_HANDLER_NEEDED + + + +#define MPT_CONSTEXPR11_ASSERT MPT_STATIC_ASSERT +#if MPT_CXX_AT_LEAST(14) && !MPT_MSVC_BEFORE(2017,5) +#define MPT_CONSTEXPR14_ASSERT MPT_STATIC_ASSERT +#else +#define MPT_CONSTEXPR14_ASSERT MPT_ASSERT +#endif +#if MPT_CXX_AT_LEAST(17) && !MPT_MSVC_BEFORE(2017,5) +#define MPT_CONSTEXPR17_ASSERT MPT_STATIC_ASSERT +#else +#define MPT_CONSTEXPR17_ASSERT MPT_ASSERT +#endif + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h new file mode 100644 index 000000000..4425abcf4 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h @@ -0,0 +1,270 @@ +/* + * mptBaseMacros.h + * --------------- + * Purpose: Basic assorted compiler-related helpers. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include + +#include +#include + +#include +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +// Compile time assert. +#if (MPT_CXX >= 17) +#define MPT_STATIC_ASSERT static_assert +#else +#define MPT_STATIC_ASSERT(expr) static_assert((expr), "compile time assertion failed: " #expr) +#endif +// legacy +#define STATIC_ASSERT(x) MPT_STATIC_ASSERT(x) + + + +// Advanced inline attributes +#if MPT_COMPILER_MSVC +#define MPT_FORCEINLINE __forceinline +#define MPT_NOINLINE __declspec(noinline) +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#define MPT_FORCEINLINE __attribute__((always_inline)) inline +#define MPT_NOINLINE __attribute__((noinline)) +#else +#define MPT_FORCEINLINE inline +#define MPT_NOINLINE +#endif + + + +// constexpr +#define MPT_CONSTEXPR11_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR11_VAR constexpr +#if MPT_CXX_AT_LEAST(14) && !MPT_MSVC_BEFORE(2017,5) +#define MPT_CONSTEXPR14_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR14_VAR constexpr +#else +#define MPT_CONSTEXPR14_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR14_VAR const +#endif +#if MPT_CXX_AT_LEAST(17) && !MPT_MSVC_BEFORE(2017,5) +#define MPT_CONSTEXPR17_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR17_VAR constexpr +#else +#define MPT_CONSTEXPR17_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR17_VAR const +#endif + + + +// C++17 std::size +#if MPT_CXX_AT_LEAST(17) +namespace mpt { +using std::size; +} // namespace mpt +#else +namespace mpt { +template +MPT_CONSTEXPR11_FUN auto size(const T & v) -> decltype(v.size()) +{ + return v.size(); +} +template +MPT_CONSTEXPR11_FUN std::size_t size(const T(&)[N]) noexcept +{ + return N; +} +} // namespace mpt +#endif +// legacy +#if MPT_COMPILER_MSVC +OPENMPT_NAMESPACE_END +#include +#include +OPENMPT_NAMESPACE_BEGIN +#define MPT_ARRAY_COUNT(x) _countof(x) +#else +#define MPT_ARRAY_COUNT(x) (sizeof((x))/sizeof((x)[0])) +#endif +#define CountOf(x) MPT_ARRAY_COUNT(x) + + + +// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased. +#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#define MPT_RESTRICT __restrict +#else +#define MPT_RESTRICT +#endif + + + +// Some functions might be deprecated although they are still in use. +// Tag them with "MPT_DEPRECATED". +#if MPT_COMPILER_MSVC +#define MPT_DEPRECATED __declspec(deprecated) +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#define MPT_DEPRECATED __attribute__((deprecated)) +#else +#define MPT_DEPRECATED +#endif +#if defined(MODPLUG_TRACKER) +#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED +#define MPT_DEPRECATED_LIBOPENMPT +#elif defined(LIBOPENMPT_BUILD) +#define MPT_DEPRECATED_TRACKER +#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED +#else +#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED +#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED +#endif + + + +#if MPT_CXX_AT_LEAST(17) +#define MPT_CONSTANT_IF if constexpr +#endif + +#if MPT_COMPILER_MSVC +#if !defined(MPT_CONSTANT_IF) +#define MPT_CONSTANT_IF(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + if(x) \ + __pragma(warning(pop)) \ +/**/ +#endif +#define MPT_MAYBE_CONSTANT_IF(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + if(x) \ + __pragma(warning(pop)) \ +/**/ +#endif + +#if MPT_COMPILER_GCC +#define MPT_MAYBE_CONSTANT_IF(x) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \ + if(x) \ + _Pragma("GCC diagnostic pop") \ +/**/ +#endif + +#if MPT_COMPILER_CLANG +#define MPT_MAYBE_CONSTANT_IF(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \ + _Pragma("clang diagnostic ignored \"-Wtype-limits\"") \ + _Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \ + if(x) \ + _Pragma("clang diagnostic pop") \ +/**/ +#endif + +#if !defined(MPT_CONSTANT_IF) +// MPT_CONSTANT_IF disables compiler warnings for conditions that are either always true or always false for some reason (dependent on template arguments for example) +#define MPT_CONSTANT_IF(x) if(x) +#endif + +#if !defined(MPT_MAYBE_CONSTANT_IF) +// MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases). +#define MPT_MAYBE_CONSTANT_IF(x) if(x) +#endif + + + +#if MPT_COMPILER_MSVC +// MSVC warns for the well-known and widespread "do { } while(0)" idiom with warning level 4 ("conditional expression is constant"). +// It does not warn with "while(0,0)". However this again causes warnings with other compilers. +// Solve it with a macro. +#define MPT_DO do +#define MPT_WHILE_0 while(0,0) +#endif + +#ifndef MPT_DO +#define MPT_DO do +#endif +#ifndef MPT_WHILE_0 +#define MPT_WHILE_0 while(0) +#endif + + + +#if MPT_COMPILER_MSVC && defined(UNREFERENCED_PARAMETER) +#define MPT_UNREFERENCED_PARAMETER(x) UNREFERENCED_PARAMETER(x) +#else +#define MPT_UNREFERENCED_PARAMETER(x) (void)(x) +#endif + +#define MPT_UNUSED_VARIABLE(x) MPT_UNREFERENCED_PARAMETER(x) + + + +// Macro for marking intentional fall-throughs in switch statements - can be used for static analysis if supported. +#if (MPT_CXX >= 17) + #define MPT_FALLTHROUGH [[fallthrough]] +#elif MPT_COMPILER_MSVC + #define MPT_FALLTHROUGH __fallthrough +#elif MPT_COMPILER_CLANG + #define MPT_FALLTHROUGH [[clang::fallthrough]] +#elif MPT_COMPILER_GCC && MPT_GCC_AT_LEAST(7,1,0) + #define MPT_FALLTHROUGH __attribute__((fallthrough)) +#elif defined(__has_cpp_attribute) + #if __has_cpp_attribute(fallthrough) + #define MPT_FALLTHROUGH [[fallthrough]] + #else + #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0 + #endif +#else + #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0 +#endif + + + +#if MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) __attribute__((format(printf, formatstringindex, varargsindex))) +#else +#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) +#endif + + + +#if MPT_COMPILER_MSVC +// warning LNK4221: no public symbols found; archive member will be inaccessible +// There is no way to selectively disable linker warnings. +// #pragma warning does not apply and a command line option does not exist. +// Some options: +// 1. Macro which generates a variable with a unique name for each file (which means we have to pass the filename to the macro) +// 2. unnamed namespace containing any symbol (does not work for c++11 compilers because they actually have internal linkage now) +// 3. An unused trivial inline function. +// Option 3 does not actually solve the problem though, which leaves us with option 1. +// In any case, for optimized builds, the linker will just remove the useless symbol. +#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) x##y +#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT(x,y) MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) +#define MPT_MSVC_WORKAROUND_LNK4221(x) int MPT_MSVC_WORKAROUND_LNK4221_CONCAT(mpt_msvc_workaround_lnk4221_,x) = 0; +#endif + +#ifndef MPT_MSVC_WORKAROUND_LNK4221 +#define MPT_MSVC_WORKAROUND_LNK4221(x) +#endif + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h new file mode 100644 index 000000000..cdef4e10e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h @@ -0,0 +1,181 @@ +/* + * mptBaseTypes.h + * -------------- + * Purpose: Basic data type definitions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptBaseMacros.h" + +#include +#include + +#include +#include + +#if MPT_GCC_BEFORE(4,9,0) +#include +#endif +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +typedef std::int8_t int8; +typedef std::int16_t int16; +typedef std::int32_t int32; +typedef std::int64_t int64; +typedef std::uint8_t uint8; +typedef std::uint16_t uint16; +typedef std::uint32_t uint32; +typedef std::uint64_t uint64; + +constexpr int8 int8_min = std::numeric_limits::min(); +constexpr int16 int16_min = std::numeric_limits::min(); +constexpr int32 int32_min = std::numeric_limits::min(); +constexpr int64 int64_min = std::numeric_limits::min(); + +constexpr int8 int8_max = std::numeric_limits::max(); +constexpr int16 int16_max = std::numeric_limits::max(); +constexpr int32 int32_max = std::numeric_limits::max(); +constexpr int64 int64_max = std::numeric_limits::max(); + +constexpr uint8 uint8_max = std::numeric_limits::max(); +constexpr uint16 uint16_max = std::numeric_limits::max(); +constexpr uint32 uint32_max = std::numeric_limits::max(); +constexpr uint64 uint64_max = std::numeric_limits::max(); + + +typedef float float32; +MPT_STATIC_ASSERT(sizeof(float32) == 4); + +typedef double float64; +MPT_STATIC_ASSERT(sizeof(float64) == 8); + + +MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); + + +MPT_STATIC_ASSERT(std::numeric_limits::digits == 8); + +MPT_STATIC_ASSERT(sizeof(char) == 1); + +#if MPT_CXX_AT_LEAST(17) +namespace mpt { +using byte = std::byte; +} // namespace mpt +#define MPT_BYTE_IS_STD_BYTE 1 +#else +// In C++11 and C++14, a C++17 compatible definition of byte would not be required to be allowed to alias other types, +// thus just use a typedef for unsigned char which is guaranteed to be allowed to alias. +//enum class byte : unsigned char { }; +namespace mpt { +typedef unsigned char byte; +} // namespace mpt +#define MPT_BYTE_IS_STD_BYTE 0 +#endif +MPT_STATIC_ASSERT(sizeof(mpt::byte) == 1); +MPT_STATIC_ASSERT(alignof(mpt::byte) == 1); + + +namespace mpt { +#if MPT_GCC_BEFORE(4,9,0) +typedef ::max_align_t max_align_t; +#else +typedef std::max_align_t max_align_t; +#endif +} // namespace mpt + + +namespace mpt { +constexpr int arch_bits = sizeof(void*) * 8; +constexpr std::size_t pointer_size = sizeof(void*); +} // namespace mpt + +MPT_STATIC_ASSERT(mpt::arch_bits == static_cast(mpt::pointer_size) * 8); + + + +namespace mpt { + +template +struct limits +{ + static constexpr typename std::remove_cv::type min() noexcept { return std::numeric_limits::type>::min(); } + static constexpr typename std::remove_cv::type max() noexcept { return std::numeric_limits::type>::max(); } +}; + +} // namespace mpt + + + +namespace mpt +{ + +// compatible with std::experimental::source_location from Library Fundamentals TS v2. +struct source_location +{ +private: + const char* m_file_name; + const char* m_function_name; + uint32 m_line; + uint32 m_column; +public: + constexpr source_location() noexcept + : m_file_name("") + , m_function_name("") + , m_line(0) + , m_column(0) + { + } + constexpr source_location(const char* file, const char* function, uint32 line, uint32 column) noexcept + : m_file_name(file) + , m_function_name(function) + , m_line(line) + , m_column(column) + { + } + source_location(const source_location&) = default; + source_location(source_location&&) = default; + //static constexpr current() noexcept; // use MPT_SOURCE_LOCATION_CURRENT() + static constexpr source_location current(const char* file, const char* function, uint32 line, uint32 column) noexcept + { + return source_location(file, function, line, column); + } + constexpr uint32 line() const noexcept + { + return m_line; + } + constexpr uint32 column() const noexcept + { + return m_column; + } + constexpr const char* file_name() const noexcept + { + return m_file_name; + } + constexpr const char* function_name() const noexcept + { + return m_function_name; + } +}; + +#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current( __FILE__ , __FUNCTION__ , __LINE__ , 0 ) + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h new file mode 100644 index 000000000..78127526f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h @@ -0,0 +1,734 @@ +/* + * mptBaseUtils.h + * -------------- + * Purpose: Various useful utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "mptBaseMacros.h" +#include "mptBaseTypes.h" + +#include +#if MPT_CXX_AT_LEAST(20) +#include +#endif +#include +#include +#include + +#include +#include + +#include +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +// cmath fixups +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + + + + +namespace mpt { + +// GCC 4.5 and up provides templated overloads of std::abs that convert +// integer type narrower than int to double. +// This is fixed as of GCC 7.1. +// As this is apparently valid by the current standard, Library Working Group +// Issue #2735 has been filed (see +// ). +// In any case, avoid this insanity and provide our own mpt::abs implementation +// for signed integer and floating point types. +// Note: We stick to a C++98-style implementation only overloading int and +// greater types in order to keep promotion rules consistent for narrower types, +// which a templated version returning the argument type would not do. OpenMPT +// probably assumes this semantic when calling abs(int8) in various places. +inline int abs(int x) +{ + return std::abs(x); +} +inline long abs(long x) +{ + return std::abs(x); +} +inline long long abs(long long x) +{ + return std::abs(x); +} +inline float abs(float x) +{ + return std::fabs(x); +} +inline double abs(double x) +{ + return std::fabs(x); +} +inline long double abs(long double x) +{ + return std::fabs(x); +} + +// Modulo with more intuitive behaviour for some contexts: +// Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range. +// For example, wrapping_modulo(-1, m) == (m - 1). +// Behaviour is undefined if m<=0. +template +MPT_CONSTEXPR11_FUN auto wrapping_modulo(T x, M m) -> decltype(x % m) +{ + return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m)); +} + +template +MPT_CONSTEXPR11_FUN auto wrapping_divide(T x, D d) -> decltype(x / d) +{ + return (x >= 0) ? (x / d) : (((x + 1) / d) - 1); +} + +} // namespace mpt + + + +namespace mpt { + + + +// Saturate the value of src to the domain of Tdst +template +inline Tdst saturate_cast(Tsrc src) +{ + // This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting. + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_CONSTANT_IF(std::numeric_limits::is_signed && std::numeric_limits::is_signed) + { + MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(src); + } else + { + return static_cast(std::max(static_cast(std::numeric_limits::min()), std::min(src, static_cast(std::numeric_limits::max())))); + } + } else MPT_CONSTANT_IF(!std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + { + MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(src); + } else + { + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } + } else MPT_CONSTANT_IF(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + { + MPT_CONSTANT_IF(sizeof(Tdst) > sizeof(Tsrc)) + { + return static_cast(src); + } else MPT_CONSTANT_IF(sizeof(Tdst) == sizeof(Tsrc)) + { + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } else + { + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } + } else // Tdst unsigned, Tsrc signed + { + MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(std::max(0, src)); + } else + { + return static_cast(std::max(0, std::min(src, static_cast(std::numeric_limits::max())))); + } + } +} + +template +inline Tdst saturate_cast(double src) +{ + if(src >= std::numeric_limits::max()) + { + return std::numeric_limits::max(); + } + if(src <= std::numeric_limits::min()) + { + return std::numeric_limits::min(); + } + return static_cast(src); +} + +template +inline Tdst saturate_cast(float src) +{ + if(src >= std::numeric_limits::max()) + { + return std::numeric_limits::max(); + } + if(src <= std::numeric_limits::min()) + { + return std::numeric_limits::min(); + } + return static_cast(src); +} + + +template +MPT_CONSTEXPR14_FUN std::size_t weight(T val) noexcept +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type Tunsigned; + Tunsigned uval = static_cast(val); + std::size_t result = 0; + while(uval > 0) + { + if(uval & 0x1) + { + result++; + } + uval >>= 1; + } + return result; +} + +#if MPT_CXX_AT_LEAST(20) + +using std::ispow2; +using std::ceil2; +using std::floor2; +using std::log2p1; + +#else + +// C++20 header. +// Note that we do not use SFINAE here but instead rely on static_assert. +// Also note that for C++11 compilers, these functions are not constexpr. +// They could be implemented recursively to make them C++11 constexpr compatible if needed. + +template +MPT_CONSTEXPR14_FUN bool ispow2(T x) noexcept +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::is_unsigned::value); + return mpt::weight(x) == 1; +} + +template +MPT_CONSTEXPR14_FUN T ceil2(T x) noexcept +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::is_unsigned::value); + T result = 1; + while(result < x) + { + T newresult = result << 1; + if(newresult < result) + { + return 0; + } + result = newresult; + } + return result; +} + +template +MPT_CONSTEXPR14_FUN T floor2(T x) noexcept +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::is_unsigned::value); + if(x == 0) + { + return 0; + } + T result = 1; + do + { + T newresult = result << 1; + if(newresult < result) + { + return result; + } + result = newresult; + } while(result <= x); + return result >> 1; +} + +template +MPT_CONSTEXPR14_FUN T log2p1(T x) noexcept +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::is_unsigned::value); + T result = 0; + while(x > 0) + { + x >>= 1; + result += 1; + } + return result; +} + +#endif + +} // namespace mpt + + +#if defined(MODPLUG_TRACKER) +// Tracker code requires MIN/MAX to work in constexpr contexts. +// We could make MIN/MAX constexpr for supporting compilers, +// but that would just needlessly complicate the support matrix +// for now. +#ifndef MPT_MINMAX_MACROS +#define MPT_MINMAX_MACROS +#endif +#endif + +#if MPT_COMPILER_MSVC +// MSVC disables a bunch of type conversion warnings once a macro is involved. +// Replacing the macro with a template thus spews a TON OF WARNINGS for now. +#ifndef MPT_MINMAX_MACROS +#define MPT_MINMAX_MACROS +#endif +#endif + +#if defined(MPT_MINMAX_MACROS) + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#else + +namespace mpt { namespace Legacy { + +template +MPT_FORCEINLINE auto MAX(const Ta &a, const Tb &b) -> decltype((a>b)?a:b) +{ + return (a > b) ? a : b; +} + +template +MPT_FORCEINLINE auto MIN(const Ta &a, const Tb &b) -> decltype((a +struct ModIfNotZeroImpl +{ + template + inline Tval mod(Tval x) + { + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(!std::numeric_limits::is_signed); + STATIC_ASSERT(std::numeric_limits::is_integer); + STATIC_ASSERT(!std::numeric_limits::is_signed); + return static_cast(x % m); + } +}; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +} // namespace detail +// Returns x % m if m != 0, x otherwise. +// i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers +template +inline Tval ModIfNotZero(Tval x) +{ + return detail::ModIfNotZeroImpl().mod(x); +} + +// Returns true iff Tdst can represent the value val. +// Use as if(Util::TypeCanHoldValue(-1)). +template +inline bool TypeCanHoldValue(Tsrc val) +{ + return (static_cast(mpt::saturate_cast(val)) == val); +} + +// Grows x with an exponential factor suitable for increasing buffer sizes. +// Clamps the result at limit. +// And avoids integer overflows while doing its business. +// The growth factor is 1.5, rounding down, execpt for the initial x==1 case. +template +inline T ExponentialGrow(const T &x, const Tlimit &limit) +{ + MPT_ASSERT(x > 0); + MPT_ASSERT(limit > 0); + if(x == 1) + { + return 2; + } + T add = std::min(x >> 1, std::numeric_limits::max() - x); + return std::min(x + add, mpt::saturate_cast(limit)); +} + +template +inline T ExponentialGrow(const T &x) +{ + return Util::ExponentialGrow(x, std::numeric_limits::max()); +} + +} //namespace Util + + +namespace mpt +{ + +// C++17 clamp + +#if MPT_CXX_AT_LEAST(17) + +using std::clamp; + +#else + +template +MPT_CONSTEXPR11_FUN const T & clamp(const T & v, const T & lo, const T & hi, Compare comp) +{ + return comp(v, lo) ? lo : comp(hi, v) ? hi : v; +} + +template +MPT_CONSTEXPR11_FUN const T & clamp(const T & v, const T & lo, const T & hi) +{ + return mpt::clamp(v, lo, hi, std::less()); +} + +#endif + +} // namespace mpt + + +// Limits 'val' to given range. If 'val' is less than 'lowerLimit', 'val' is set to value 'lowerLimit'. +// Similarly if 'val' is greater than 'upperLimit', 'val' is set to value 'upperLimit'. +// If 'lowerLimit' > 'upperLimit', 'val' won't be modified. +template +inline void Limit(T& val, const C lowerLimit, const C upperLimit) +{ + if(lowerLimit > upperLimit) return; + if(val < lowerLimit) val = lowerLimit; + else if(val > upperLimit) val = upperLimit; +} + + +// Like Limit, but returns value +template +inline T Clamp(T val, const C lowerLimit, const C upperLimit) +{ + if(val < lowerLimit) return lowerLimit; + else if(val > upperLimit) return upperLimit; + else return val; +} + +// Check if val is in [lo,hi] without causing compiler warnings +// if theses checks are always true due to the domain of T. +// GCC does not warn if the type is templated. +template +inline bool IsInRange(T val, C lo, C hi) +{ + return lo <= val && val <= hi; +} + +// Like Limit, but with upperlimit only. +template +inline void LimitMax(T& val, const C upperLimit) +{ + if(val > upperLimit) + val = upperLimit; +} + + +// Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0) +template +int sgn(T value) +{ + return (value > T(0)) - (value < T(0)); +} + + + +// mpt::rshift_signed +// mpt::lshift_signed +// Shift a signed integer value in a well-defined manner. +// Does the same thing as MSVC would do. This is verified by the test suite. + +namespace mpt +{ + +template +MPT_FORCEINLINE auto rshift_signed_standard(T x, int y) -> decltype(x >> y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + typedef decltype(x >> y) result_type; + typedef typename std::make_unsigned::type unsigned_result_type; + const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); + result_type rx = x; + unsigned_result_type urx = static_cast(rx); + urx += roffset; + urx >>= y; + urx -= roffset >> y; + return static_cast(urx); +} + +template +MPT_FORCEINLINE auto lshift_signed_standard(T x, int y) -> decltype(x << y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + typedef decltype(x << y) result_type; + typedef typename std::make_unsigned::type unsigned_result_type; + const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); + result_type rx = x; + unsigned_result_type urx = static_cast(rx); + urx += roffset; + urx <<= y; + urx -= roffset << y; + return static_cast(urx); +} + +#if MPT_COMPILER_SHIFT_SIGNED + +template +MPT_FORCEINLINE auto rshift_signed_undefined(T x, int y) -> decltype(x >> y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + return x >> y; +} + +template +MPT_FORCEINLINE auto lshift_signed_undefined(T x, int y) -> decltype(x << y) +{ + MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + MPT_STATIC_ASSERT(std::numeric_limits::is_signed); + return x << y; +} + +template +MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) +{ + return mpt::rshift_signed_undefined(x, y); +} + +template +MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) +{ + return mpt::lshift_signed_undefined(x, y); +} + +#else + +template +MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) +{ + return mpt::rshift_signed_standard(x, y); +} + +template +MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) +{ + return mpt::lshift_signed_standard(x, y); +} + +#endif + +} // namespace mpt + + + +namespace Util +{ + + // Returns maximum value of given integer type. + template constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits::max)();} + +} + + +namespace mpt +{ + +#if MPT_OS_DJGPP + + inline double round(const double& val) { return ::round(val); } + inline float round(const float& val) { return ::roundf(val); } + +#else // !MPT_OS_DJGPP + + // C++11 std::round + using std::round; + +#endif // MPT_OS_DJGPP + + + // Rounds given double value to nearest integer value of type T. + // Out-of-range values are saturated to the specified integer type's limits. + template inline T saturate_round(double val) + { + static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); + return mpt::saturate_cast(mpt::round(val)); + } + + template inline T saturate_round(float val) + { + static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); + return mpt::saturate_cast(mpt::round(val)); + } + +} + + +namespace Util { + + // Multiply two 32-bit integers, receive 64-bit result. + // MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul. + MPT_FORCEINLINE int64 mul32to64(int32 a, int32 b) + { +#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) + return __emul(a, b); +#else + return static_cast(a) * b; +#endif + } + + MPT_FORCEINLINE uint64 mul32to64_unsigned(uint32 a, uint32 b) + { +#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) + return __emulu(a, b); +#else + return static_cast(a) * b; +#endif + } + + MPT_FORCEINLINE int32 muldiv(int32 a, int32 b, int32 c) + { + return mpt::saturate_cast( mul32to64( a, b ) / c ); + } + + MPT_FORCEINLINE int32 muldivr(int32 a, int32 b, int32 c) + { + return mpt::saturate_cast( ( mul32to64( a, b ) + ( c / 2 ) ) / c ); + } + + // Do not use overloading because catching unsigned version by accident results in slower X86 code. + MPT_FORCEINLINE uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c) + { + return mpt::saturate_cast( mul32to64_unsigned( a, b ) / c ); + } + MPT_FORCEINLINE uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c) + { + return mpt::saturate_cast( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c ); + } + + MPT_FORCEINLINE int32 muldivrfloor(int64 a, uint32 b, uint32 c) + { + a *= b; + a += c / 2u; + return (a >= 0) ? mpt::saturate_cast(a / c) : mpt::saturate_cast((a - (c - 1)) / c); + } + + // rounds x up to multiples of target + template + inline T AlignUp(T x, T target) + { + return ((x + (target - 1)) / target) * target; + } + + // rounds x down to multiples of target + template + inline T AlignDown(T x, T target) + { + return (x / target) * target; + } + +} // namespace Util + + + +namespace mpt +{ + +#if MPT_CXX_AT_LEAST(17) + +using std::gcd; +using std::lcm; + +#else + + // Greatest Common Divisor. Always returns non-negative number. + // compatible with C++17 std::gcd + template + inline typename std::common_type::type gcd(A a_, B b_) + { + typename std::common_type::type a = a_; + typename std::common_type::type b = b_; + if(a < 0) + a = -a; + if(b < 0) + b = -b; + for(;;) + { + if(a == 0) + return b; + b %= a; + if(b == 0) + return a; + a %= b; + } + } + + // Least Common Multiple. Always returns non-negative number. + // compatible with C++17 std::lcm + template + inline typename std::common_type::type lcm(A a_, B b_) + { + typename std::common_type::type a = a_; + typename std::common_type::type b = b_; + if(a < 0) + a = -a; + if(b < 0) + b = -b; + if((a | b) == 0) + return 0; + return a / mpt::gcd(a, b) * b; + } + +#endif + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h index 34bd45eaa..fb5cf369c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + #include #include #include @@ -35,7 +37,7 @@ OPENMPT_NAMESPACE_BEGIN namespace mpt { -#if MPT_COMPILER_MSVC +#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM class stringbuf : public std::stringbuf diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp index e5b7067ce..5fffb670c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp @@ -11,6 +11,8 @@ #include "stdafx.h" #include "mptCPU.h" +#include "mptStringBuffer.h" + OPENMPT_NAMESPACE_BEGIN @@ -21,6 +23,7 @@ OPENMPT_NAMESPACE_BEGIN uint32 RealProcSupport = 0; uint32 ProcSupport = 0; char ProcVendorID[16+1] = ""; +char ProcBrandID[4*4*3+1] = ""; uint16 ProcFamily = 0; uint8 ProcModel = 0; uint8 ProcStepping = 0; @@ -57,6 +60,27 @@ struct cpuid_result { result[8+3] = (c >>24) & 0xff; return std::string(result, result + 12); } + std::string as_string4() const + { + std::string result; + result.push_back(static_cast((a >> 0) & 0xff)); + result.push_back(static_cast((a >> 8) & 0xff)); + result.push_back(static_cast((a >> 16) & 0xff)); + result.push_back(static_cast((a >> 24) & 0xff)); + result.push_back(static_cast((b >> 0) & 0xff)); + result.push_back(static_cast((b >> 8) & 0xff)); + result.push_back(static_cast((b >> 16) & 0xff)); + result.push_back(static_cast((b >> 24) & 0xff)); + result.push_back(static_cast((c >> 0) & 0xff)); + result.push_back(static_cast((c >> 8) & 0xff)); + result.push_back(static_cast((c >> 16) & 0xff)); + result.push_back(static_cast((c >> 24) & 0xff)); + result.push_back(static_cast((d >> 0) & 0xff)); + result.push_back(static_cast((d >> 8) & 0xff)); + result.push_back(static_cast((d >> 16) & 0xff)); + result.push_back(static_cast((d >> 24) & 0xff)); + return result; + } }; @@ -90,35 +114,21 @@ static cpuid_result cpuidex(uint32 function_a, uint32 function_c) #endif -static MPT_NOINLINE bool has_cpuid() -{ - const size_t eflags_cpuid = 1 << 21; - size_t eflags_old = __readeflags(); - size_t eflags_flipped = eflags_old ^ eflags_cpuid; - __writeeflags(eflags_flipped); - size_t eflags_testchanged = __readeflags(); - __writeeflags(eflags_old); - return ((eflags_testchanged ^ eflags_old) & eflags_cpuid) != 0; -} - - void InitProcSupport() { RealProcSupport = 0; ProcSupport = 0; MemsetZero(ProcVendorID); + MemsetZero(ProcBrandID); ProcFamily = 0; ProcModel = 0; ProcStepping = 0; - if(has_cpuid()) { - ProcSupport |= PROCSUPPORT_CPUID; - cpuid_result VendorString = cpuid(0x00000000u); - std::strcpy(ProcVendorID, VendorString.as_string().c_str()); + mpt::String::WriteAutoBuf(ProcVendorID) = VendorString.as_string(); // Cyrix 6x86 and 6x86MX do not specify the value returned in eax. // They both support 0x00000001u however. @@ -163,9 +173,7 @@ void InitProcSupport() ProcModel = static_cast(BaseModel); } ProcStepping = static_cast(Stepping); - if(StandardFeatureFlags.d & (1<< 4)) ProcSupport |= PROCSUPPORT_TSC; if(StandardFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; - if(StandardFeatureFlags.d & (1<< 0)) ProcSupport |= PROCSUPPORT_FPU; if(StandardFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX; if(StandardFeatureFlags.d & (1<<25)) ProcSupport |= PROCSUPPORT_SSE; if(StandardFeatureFlags.d & (1<<26)) ProcSupport |= PROCSUPPORT_SSE2; @@ -175,6 +183,7 @@ void InitProcSupport() if(StandardFeatureFlags.c & (1<<20)) ProcSupport |= PROCSUPPORT_SSE4_2; } + bool canExtended = false; // 3DNow! manual recommends to just execute 0x80000000u. // It is totally unknown how earlier CPUs from other vendors // would behave. @@ -182,25 +191,24 @@ void InitProcSupport() // that we found it documented for and that actually supports 3DNow!. // We only need 0x80000000u in order to detect 3DNow!. // Thus, this is enough for us. - if((VendorString.as_string() == "AuthenticAMD") || (VendorString.as_string() == "AMDisbetter!")) + if(VendorString.as_string() == "GenuineIntel") + { // Intel + + // 5.9.x : Quark + // 6.11.x: P3-S (Tualatin) + if((ProcFamily > 6) || ((ProcFamily == 6) && (ProcModel >= 11)) || ((ProcFamily == 5) && (ProcModel >= 9))) + { + canExtended = true; + } + + } else if((VendorString.as_string() == "AuthenticAMD") || (VendorString.as_string() == "AMDisbetter!")) { // AMD if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 8))) { // >= K6-2 (K6 = Family 5, K6-2 = Model 8) // Not sure if earlier AMD CPUs support 0x80000000u. // AMD 5k86 and AMD K5 manuals do not mention it. - cpuid_result ExtendedVendorString = cpuid(0x80000000u); - if(ExtendedVendorString.a >= 0x80000001u) - { - cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); - if(ExtendedFeatureFlags.d & (1<< 4)) ProcSupport |= PROCSUPPORT_TSC; - if(ExtendedFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; - if(ExtendedFeatureFlags.d & (1<< 0)) ProcSupport |= PROCSUPPORT_FPU; - if(ExtendedFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX; - if(ExtendedFeatureFlags.d & (1<<22)) ProcSupport |= PROCSUPPORT_AMD_MMXEXT; - if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; - if(ExtendedFeatureFlags.d & (1<<30)) ProcSupport |= PROCSUPPORT_AMD_3DNOWEXT; - } + canExtended = true; } } else if(VendorString.as_string() == "CentaurHauls") @@ -211,12 +219,7 @@ void InitProcSupport() if(ProcModel >= 8) { // >= WinChip 2 - cpuid_result ExtendedVendorString = cpuid(0x80000000u); - if(ExtendedVendorString.a >= 0x80000001u) - { - cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); - if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; - } + canExtended = true; } } else if(ProcFamily >= 6) @@ -224,12 +227,7 @@ void InitProcSupport() if((ProcFamily >= 7) || ((ProcFamily == 6) && (ProcModel >= 7))) { // >= C3 Samuel 2 - cpuid_result ExtendedVendorString = cpuid(0x80000000u); - if(ExtendedVendorString.a >= 0x80000001u) - { - cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); - if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; - } + canExtended = true; } } @@ -247,12 +245,7 @@ void InitProcSupport() if((ProcFamily == 5) && (ProcModel >= 4)) { // Cyrix MediaGXm - cpuid_result ExtendedVendorString = cpuid(0x80000000u); - if(ExtendedVendorString.a >= 0x80000001u) - { - cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); - if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; - } + canExtended = true; } } else if(VendorString.as_string() == "Geode by NSC") @@ -260,25 +253,43 @@ void InitProcSupport() if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 5))) { // >= Geode GX2 - cpuid_result ExtendedVendorString = cpuid(0x80000000u); - if(ExtendedVendorString.a >= 0x80000001u) - { - cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); - if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; - } + canExtended = true; } + } else + { // unknown, which nowadays most likely means some virtualized CPU + + // we assume extended flags present in this case + canExtended = true; + } - } else - { - - ProcSupport |= PROCSUPPORT_FPU; // We assume FPU because we require it. + if(canExtended) + { + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) + { + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<<29)) ProcSupport |= PROCSUPPORT_LM; + if((VendorString.as_string() == "AuthenticAMD") || (VendorString.as_string() == "AMDisbetter!")) + { + if(ExtendedFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; + if(ExtendedFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX; + } + if(ExtendedFeatureFlags.d & (1<<22)) ProcSupport |= PROCSUPPORT_AMD_MMXEXT; + if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW; + if(ExtendedFeatureFlags.d & (1<<30)) ProcSupport |= PROCSUPPORT_AMD_3DNOWEXT; + } + if(ExtendedVendorString.a >= 0x80000004u) + { + mpt::String::WriteAutoBuf(ProcBrandID) = cpuid(0x80000002u).as_string4() + cpuid(0x80000003u).as_string4() + cpuid(0x80000004u).as_string4(); + } + } } // We do not have to check if SSE got enabled by the OS because we only do - // support Windows >= 98 SE which will always enable SSE if available. + // support Windows >= XP. Windows will always enable SSE since Windows 98 SE. RealProcSupport = ProcSupport; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h index 241e6b46e..e70dc1f17 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h @@ -10,16 +10,16 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN #ifdef ENABLE_ASM -#define PROCSUPPORT_CPUID 0x00001 // Processor supports CPUID instruction (i586) -#define PROCSUPPORT_TSC 0x00002 // Processor supports RDTSC instruction (i586) +#define PROCSUPPORT_LM 0x00001 // Processor supports long mode (amd64) #define PROCSUPPORT_CMOV 0x00004 // Processor supports conditional move instructions (i686) -#define PROCSUPPORT_FPU 0x00008 // Processor supports x87 instructions #define PROCSUPPORT_MMX 0x00010 // Processor supports MMX instructions #define PROCSUPPORT_AMD_MMXEXT 0x00020 // Processor supports AMD MMX extensions #define PROCSUPPORT_AMD_3DNOW 0x00040 // Processor supports AMD 3DNow! instructions @@ -31,16 +31,16 @@ OPENMPT_NAMESPACE_BEGIN #define PROCSUPPORT_SSE4_1 0x01000 // Processor supports SSE4.1 instructions #define PROCSUPPORT_SSE4_2 0x02000 // Processor supports SSE4.2 instructions -static const uint32 PROCSUPPORT_i486 = 0u | PROCSUPPORT_FPU ; -static const uint32 PROCSUPPORT_i586 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_FPU ; -static const uint32 PROCSUPPORT_i686 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU ; -static const uint32 PROCSUPPORT_x86_SSE = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU | PROCSUPPORT_SSE ; -static const uint32 PROCSUPPORT_x86_SSE2 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU | PROCSUPPORT_SSE | PROCSUPPORT_SSE2; -static const uint32 PROCSUPPORT_AMD64 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_SSE | PROCSUPPORT_SSE2; +static const uint32 PROCSUPPORT_i586 = 0u ; +static const uint32 PROCSUPPORT_i686 = 0u | PROCSUPPORT_CMOV ; +static const uint32 PROCSUPPORT_x86_SSE = 0u | PROCSUPPORT_CMOV | PROCSUPPORT_SSE ; +static const uint32 PROCSUPPORT_x86_SSE2 = 0u | PROCSUPPORT_CMOV | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 ; +static const uint32 PROCSUPPORT_AMD64 = 0u | PROCSUPPORT_CMOV | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 | PROCSUPPORT_LM; extern uint32 RealProcSupport; extern uint32 ProcSupport; extern char ProcVendorID[16+1]; +extern char ProcBrandID[4*4*3+1]; extern uint16 ProcFamily; extern uint8 ProcModel; extern uint8 ProcStepping; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h index c3e930995..498015b14 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN namespace mpt @@ -27,9 +29,9 @@ public: typedef T value_type; typedef uint8 byte_type; - static const std::size_t size_bytes = sizeof(value_type); - static const std::size_t size_bits = sizeof(value_type) * 8; - static const value_type top_bit = static_cast(1) << ((sizeof(value_type) * 8) - 1); + enum : std::size_t { size_bytes = sizeof(value_type) }; + enum : std::size_t { size_bits = sizeof(value_type) * 8 }; + enum : value_type { top_bit = static_cast(1) << ((sizeof(value_type) * 8) - 1) }; private: @@ -134,7 +136,7 @@ public: inline crc & process(char c) { - processByte(static_cast(c)); + processByte(mpt::byte_cast(c)); return *this; } @@ -146,10 +148,18 @@ public: inline crc & process(unsigned char c) { - processByte(static_cast(c)); + processByte(mpt::byte_cast(c)); return *this; } +#if MPT_BYTE_IS_STD_BYTE + inline crc & process(mpt::byte c) + { + processByte(mpt::byte_cast(c)); + return *this; + } +#endif + template crc & process(InputIt beg, InputIt end) { @@ -170,7 +180,7 @@ public: inline crc & operator () (char c) { - processByte(static_cast(c)); + processByte(mpt::byte_cast(c)); return *this; } @@ -182,10 +192,18 @@ public: inline crc & operator () (unsigned char c) { - processByte(static_cast(c)); + processByte(mpt::byte_cast(c)); return *this; } +#if MPT_BYTE_IS_STD_BYTE + inline crc & operator () (mpt::byte c) + { + processByte(mpt::byte_cast(c)); + return *this; + } +#endif + template crc & operator () (InputIt beg, InputIt end) { diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptException.h b/Frameworks/OpenMPT/OpenMPT/common/mptException.h new file mode 100644 index 000000000..59bf21139 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptException.h @@ -0,0 +1,56 @@ +/* + * mptException.h + * -------------- + * Purpose: Exception abstraction, in particular for bad_alloc. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptBaseMacros.h" + +#include +#if !defined(_MFC_VER) +#include +#endif // !_MFC_VER + +#if defined(_MFC_VER) +// cppcheck-suppress missingInclude +#include +#endif // _MFC_VER + + + +OPENMPT_NAMESPACE_BEGIN + + + +// Exception handling helpers, because MFC requires explicit deletion of the exception object, +// Thus, always call exactly one of MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() or MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e). + +#if defined(_MFC_VER) + +#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { AfxThrowMemoryException(); } MPT_WHILE_0 +#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( CMemoryException * e ) +#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e) MPT_DO { MPT_UNUSED_VARIABLE(e); throw; } MPT_WHILE_0 +#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { if(e) { e->Delete(); e = nullptr; } } MPT_WHILE_0 + +#else // !_MFC_VER + +#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { throw std::bad_alloc(); } MPT_WHILE_0 +#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( const std::bad_alloc & e ) +#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e) MPT_DO { MPT_UNUSED_VARIABLE(e); throw; } MPT_WHILE_0 +#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { MPT_UNUSED_VARIABLE(e); } MPT_WHILE_0 + +#endif // _MFC_VER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h b/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h new file mode 100644 index 000000000..3566463b4 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h @@ -0,0 +1,76 @@ +/* + * mptExceptionText.h + * ------------------ + * Purpose: Guess encoding of exception string + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptException.h" +#include "mptString.h" + +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + +template T get_exception_text_impl(const std::exception & e) noexcept +{ + if(e.what() && (std::strlen(e.what()) > 0)) + { + return T(e.what()); + } else if(typeid(e).name() && (std::strlen(typeid(e).name()) > 0)) + { + return T(typeid(e).name()); + } else + { + return T("unknown exception"); + } +} + +template inline T get_exception_text(const std::exception & e) noexcept +{ + return mpt::get_exception_text_impl(e); +} +template <> inline std::string get_exception_text(const std::exception & e) noexcept +{ + return mpt::get_exception_text_impl(e); +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template <> inline mpt::lstring get_exception_text(const std::exception & e) noexcept +{ + return mpt::ToLocale(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); +} +#endif +#if MPT_WSTRING_FORMAT +template <> inline std::wstring get_exception_text(const std::exception & e) noexcept +{ + return mpt::ToWide(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); +} +#endif +#if MPT_USTRING_MODE_UTF8 +template <> inline mpt::ustring get_exception_text(const std::exception & e) noexcept +{ + return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); +} +#endif + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp index ecaa58e83..4f957f361 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp @@ -18,6 +18,12 @@ #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER +#if defined(MPT_ENABLE_FILEIO) +#if MPT_COMPILER_MSVC +#include +#endif // MPT_COMPILER_MSVC +#endif // MPT_ENABLE_FILEIO + OPENMPT_NAMESPACE_BEGIN @@ -37,7 +43,7 @@ bool SetFilesystemCompression(HANDLE hFile) USHORT format = COMPRESSION_FORMAT_DEFAULT; DWORD dummy = 0; BOOL result = DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, (LPVOID)&format, sizeof(format), NULL, 0, &dummy /*required*/ , NULL); - return result ? true : false; + return result != FALSE; } bool SetFilesystemCompression(int fd) { @@ -53,24 +59,9 @@ bool SetFilesystemCompression(int fd) } return SetFilesystemCompression(hFile); } -#if defined(MPT_ENABLE_FILEIO_STDIO) -bool SetFilesystemCompression(FILE *file) -{ - if(!file) - { - return false; - } - int fd = _fileno(file); - if(fd == -1) - { - return false; - } - return SetFilesystemCompression(fd); -} -#endif // MPT_ENABLE_FILEIO_STDIO bool SetFilesystemCompression(const mpt::PathString &filename) { - DWORD attributes = GetFileAttributesW(filename.AsNativePrefixed().c_str()); + DWORD attributes = GetFileAttributes(filename.AsNativePrefixed().c_str()); if(attributes == INVALID_FILE_ATTRIBUTES) { return false; @@ -79,14 +70,13 @@ bool SetFilesystemCompression(const mpt::PathString &filename) { return true; } - HANDLE hFile = CreateFileW(filename.AsNativePrefixed().c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + HANDLE hFile = CreateFile(filename.AsNativePrefixed().c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if(hFile == INVALID_HANDLE_VALUE) { return false; } bool result = SetFilesystemCompression(hFile); CloseHandle(hFile); - hFile = INVALID_HANDLE_VALUE; return result; } #endif // MPT_OS_WINDOWS @@ -94,6 +84,163 @@ bool SetFilesystemCompression(const mpt::PathString &filename) +#ifdef MODPLUG_TRACKER + +namespace mpt { + +#if MPT_COMPILER_MSVC + +mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMode flushMode) +{ + mpt::tstring fopen_mode; + switch(mode & ~(std::ios_base::ate | std::ios_base::binary)) + { + case std::ios_base::in: + fopen_mode = _T("r"); + break; + case std::ios_base::out: + MPT_FALLTHROUGH; + case std::ios_base::out | std::ios_base::trunc: + fopen_mode = _T("w"); + break; + case std::ios_base::app: + MPT_FALLTHROUGH; + case std::ios_base::out | std::ios_base::app: + fopen_mode = _T("a"); + break; + case std::ios_base::out | std::ios_base::in: + fopen_mode = _T("r+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::trunc: + fopen_mode = _T("w+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::app: + MPT_FALLTHROUGH; + case std::ios_base::in | std::ios_base::app: + fopen_mode = _T("a+"); + break; + } + if(fopen_mode.empty()) + { + return fopen_mode; + } + if(mode & std::ios_base::binary) + { + fopen_mode += _T("b"); + } + if(flushMode == FlushMode::Full) + { + fopen_mode += _T("c"); // force commit on fflush (MSVC specific) + } + return fopen_mode; +} + +FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode) +{ + mpt::tstring fopen_mode = convert_mode(mode, flushMode); + if(fopen_mode.empty()) + { + return nullptr; + } + FILE *f = +#ifdef UNICODE + _wfopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) +#else + fopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) +#endif + ; + if(!f) + { + return nullptr; + } + if(mode & std::ios_base::ate) + { + if(fseek(f, 0, SEEK_END) != 0) + { + fclose(f); + f = nullptr; + return nullptr; + } + } + m_f = f; + return f; +} + +#endif // MPT_COMPILER_MSVC + +// cppcheck-suppress exceptThrowInDestructor +SafeOutputFile::~SafeOutputFile() noexcept(false) +{ + if(!stream()) + { + return; + } + if(!stream().rdbuf()) + { + return; + } +#if MPT_COMPILER_MSVC + if(!m_f) + { + return; + } +#endif // MPT_COMPILER_MSVC + bool errorOnFlush = false; + if(m_FlushMode != FlushMode::None) + { + try + { + if(stream().rdbuf()->pubsync() != 0) + { + errorOnFlush = true; + } + } catch(const std::exception &) + { + errorOnFlush = true; +#if MPT_COMPILER_MSVC + if(m_FlushMode != FlushMode::None) + { + if(fflush(m_f) != 0) + { + errorOnFlush = true; + } + } + if(fclose(m_f) != 0) + { + errorOnFlush = true; + } +#endif // MPT_COMPILER_MSVC + // ignore errorOnFlush here, and re-throw the earlier exception + throw; + } + } +#if MPT_COMPILER_MSVC + if(m_FlushMode != FlushMode::None) + { + if(fflush(m_f) != 0) + { + errorOnFlush = true; + } + } + if(fclose(m_f) != 0) + { + errorOnFlush = true; + } +#endif // MPT_COMPILER_MSVC + if(errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) + { + throw std::ios_base::failure(std::string("Error flushing file buffers.")); + } +} + +} // namespace mpt + +#endif // MODPLUG_TRACKER + + + +#ifdef MODPLUG_TRACKER + namespace mpt { LazyFileRef & LazyFileRef::operator = (const std::vector &data) @@ -170,6 +317,8 @@ LazyFileRef::operator std::string () const } // namespace mpt +#endif // MODPLUG_TRACKER + #ifdef MODPLUG_TRACKER @@ -184,7 +333,7 @@ CMappedFile::~CMappedFile() bool CMappedFile::Open(const mpt::PathString &filename) { - m_hFile = CreateFileW( + m_hFile = CreateFile( filename.AsNativePrefixed().c_str(), GENERIC_READ, FILE_SHARE_READ, diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h index 164ac57bb..53fec7e07 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h @@ -1,7 +1,7 @@ /* * mptFileIO.h * ----------- - * Purpose: A wrapper around std::fstream, fixing VS2008 charset conversion braindamage, and enforcing usage of mpt::PathString. + * Purpose: A wrapper around std::fstream, enforcing usage of mpt::PathString. * Notes : You should only ever use these wrappers instead of plain std::fstream classes. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + #if defined(MPT_ENABLE_FILEIO) #include "../common/mptString.h" @@ -21,6 +23,14 @@ #include #include +#if MPT_COMPILER_MSVC +#include +#endif // !MPT_COMPILER_MSVC + +#if MPT_COMPILER_MSVC +#include +#endif // !MPT_COMPILER_MSVC + #endif // MPT_ENABLE_FILEIO @@ -30,20 +40,6 @@ OPENMPT_NAMESPACE_BEGIN #if defined(MPT_ENABLE_FILEIO) -#if defined(MPT_ENABLE_FILEIO_STDIO) - -static inline FILE * mpt_fopen(const mpt::PathString &filename, const char *mode) -{ - #if MPT_OS_WINDOWS - return _wfopen(filename.AsNativePrefixed().c_str(), mode ? mpt::ToWide(mpt::CharsetASCII, mode).c_str() : L""); - #else // !MPT_OS_WINDOWS - return fopen(filename.AsNative().c_str(), mode); - #endif // MPT_OS_WINDOWS -} - -#endif // MPT_ENABLE_FILEIO_STDIO - - // Sets the NTFS compression attribute on the file or directory. // Requires read and write permissions for already opened files. // Returns true if the attribute has been set. @@ -52,9 +48,6 @@ static inline FILE * mpt_fopen(const mpt::PathString &filename, const char *mode #if MPT_OS_WINDOWS bool SetFilesystemCompression(HANDLE hFile); bool SetFilesystemCompression(int fd); -#if defined(MPT_ENABLE_FILEIO_STDIO) -bool SetFilesystemCompression(FILE *file); -#endif // MPT_ENABLE_FILEIO_STDIO bool SetFilesystemCompression(const mpt::PathString &filename); #endif // MPT_OS_WINDOWS #endif // MODPLUG_TRACKER @@ -63,22 +56,11 @@ bool SetFilesystemCompression(const mpt::PathString &filename); namespace mpt { -#if MPT_COMPILER_GCC - -#if MPT_OS_WINDOWS +#if MPT_COMPILER_GCC && MPT_OS_WINDOWS // GCC C++ library has no wchar_t overloads -#define MPT_FSTREAM_DO_CONVERSIONS #define MPT_FSTREAM_DO_CONVERSIONS_ANSI #endif -#endif - -#ifdef MPT_FSTREAM_DO_CONVERSIONS -#define MPT_FSTREAM_OPEN(filename, mode) detail::fstream_open(*this, (filename), (mode)) -#else -#define MPT_FSTREAM_OPEN(filename, mode) Tbase::open((filename), (mode)) -#endif - namespace detail { @@ -92,41 +74,17 @@ inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::io #endif } -#ifdef MPT_FSTREAM_DO_CONVERSIONS - -template -inline void fstream_open(Tbase & base, const std::wstring & filename, std::ios_base::openmode mode) -{ - detail::fstream_open(base, mpt::PathString::FromWide(filename), mode); -} - -template -inline void fstream_open(Tbase & base, const wchar_t * filename, std::ios_base::openmode mode) -{ - detail::fstream_open(base, mpt::PathString::FromWide(filename ? std::wstring(filename) : std::wstring()), mode); -} - -template -inline void fstream_open(Tbase & base, const std::string & filename, std::ios_base::openmode mode) -{ - detail::fstream_open(base, mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetLocale, filename)), mode); -} - -template -inline void fstream_open(Tbase & base, const char * filename, std::ios_base::openmode mode) -{ - detail::fstream_open(base, mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetLocale, filename ? std::string(filename) : std::string())), mode); -} - -#endif - } // namespace detail +class SafeOutputFile; + class fstream : public std::fstream { private: typedef std::fstream Tbase; +public: + friend SafeOutputFile; public: fstream() {} fstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) @@ -137,23 +95,11 @@ public: { detail::fstream_open(*this, filename, mode); } - MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename, mode); - } - MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename.c_str(), mode); - } + void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; + void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; #if MPT_OS_WINDOWS - MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename, mode); - } - MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename.c_str(), mode); - } + void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; + void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; #endif }; @@ -162,6 +108,8 @@ class ifstream { private: typedef std::ifstream Tbase; +public: + friend SafeOutputFile; public: ifstream() {} ifstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) @@ -172,23 +120,11 @@ public: { detail::fstream_open(*this, filename, mode); } - MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in) - { - MPT_FSTREAM_OPEN(filename, mode); - } - MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in) - { - MPT_FSTREAM_OPEN(filename.c_str(), mode); - } + void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; + void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; #if MPT_OS_WINDOWS - MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in) - { - MPT_FSTREAM_OPEN(filename, mode); - } - MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in) - { - MPT_FSTREAM_OPEN(filename.c_str(), mode); - } + void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; + void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; #endif }; @@ -197,40 +133,105 @@ class ofstream { private: typedef std::ofstream Tbase; +public: + friend SafeOutputFile; public: ofstream() {} ofstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) { detail::fstream_open(*this, filename, mode); } +#if MPT_COMPILER_MSVC +protected: + ofstream(FILE * file) + : std::ofstream(file) + { + } + +#endif // MPT_COMPILER_MSVC +public: void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) { detail::fstream_open(*this, filename, mode); } - MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename, mode); - } - MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename.c_str(), mode); - } + void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; + void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; #if MPT_OS_WINDOWS - MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename, mode); - } - MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out) - { - MPT_FSTREAM_OPEN(filename.c_str(), mode); - } + void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; + void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; #endif }; -#undef MPT_FSTREAM_OPEN +enum class FlushMode +{ + None = 0, // no explicit flushes at all + Single = 1, // explicitly flush higher-leverl API layers + Full = 2, // explicitly flush *all* layers, up to and including disk write caches +}; + +static inline FlushMode FlushModeFromBool(bool flush) +{ + return flush ? FlushMode::Full : FlushMode::None; +} + +#ifdef MODPLUG_TRACKER + +class SafeOutputFile +{ +private: + FlushMode m_FlushMode; +#if MPT_COMPILER_MSVC + FILE *m_f; +#endif // MPT_COMPILER_MSVC + mpt::ofstream m_s; +#if MPT_COMPILER_MSVC + static mpt::tstring convert_mode(std::ios_base::openmode mode, FlushMode flushMode); + FILE * internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode); +#endif // MPT_COMPILER_MSVC +public: + SafeOutputFile() = delete; + explicit SafeOutputFile(const mpt::PathString &filename, std::ios_base::openmode mode = std::ios_base::out, FlushMode flushMode = FlushMode::Full) + : m_FlushMode(flushMode) +#if MPT_COMPILER_MSVC + , m_s(internal_fopen(filename, mode | std::ios_base::out, flushMode)) +#else // !MPT_COMPILER_MSVC + , m_s(filename, mode) +#endif // MPT_COMPILER_MSVC + { + } + mpt::ofstream& stream() + { + return m_s; + } + operator mpt::ofstream& () + { + return stream(); + } + const mpt::ofstream& stream() const + { + return m_s; + } + operator const mpt::ofstream& () const + { + return stream(); + } + operator bool() const + { + return stream() ? true : false; + } + bool operator!() const + { + return stream().operator!(); + } + ~SafeOutputFile() noexcept(false); +}; + +#endif // MODPLUG_TRACKER +#ifdef MODPLUG_TRACKER + // LazyFileRef is a simple reference to an on-disk file by the means of a // filename which allows easy assignment of the whole file contents to and from // byte buffers. @@ -252,275 +253,7 @@ public: operator std::string () const; }; - - -#if defined(MPT_ENABLE_FILEIO_STDIO) - -// class FILE_ostream, FILE_output_streambuf and FILE_output_buffered_streambuf -// provide a portable way of wrapping a std::ostream around an FILE* opened for output. -// They offer similar functionality to the badly documented -// MSVC std::fstream(FILE*) constructor or GCC libstdc++ __gnu_cxx::stdio_sync_filebuf class, -// and, for other compilers, provide a race-free alternative to -// closing the FILE* and opening it again as a std::ofstream. -// -// Only output functionality is implemented because we have no need for an input wrapper. -// -// During the whole lifetime of the iostream wrappers, the FILE* object is assumend to be -// either -// - NULL -// or -// - valid -// - opened for writing in non-append mode -// - opened in binary mode -// - seekable -// Some of these preconditions cannot be verified, -// and even the others do not get verified. -// Behaviour in case of any unmet preconditions is undefined. -// -// The buffered streambuf and the ostream use a buffer of 64KiB by default. -// -// For FILE_output_streambuf, coherency with the underlying FILE* is always guaranteed. -// For FILE_ostream and FILE_output_buffered_streambuf, coherence is only -// guaranteed when flush() or pubsync() get called. -// The constructors and destructors take care to not violate coherency. -// When mixing FILE* and iostream I/O during the lifetime of the iostream objects, -// the user is responsible for providing coherency via the appropriate -// flush and sync functions. -// Behaviour in case of incoherent access is undefined. - - -class FILE_output_streambuf : public std::streambuf -{ -public: - typedef std::streambuf::char_type char_type; - typedef std::streambuf::traits_type traits_type; - typedef traits_type::int_type int_type; - typedef traits_type::pos_type pos_type; - typedef traits_type::off_type off_type; -protected: - FILE *f; -public: - FILE_output_streambuf(FILE *f) - : f(f) - { - return; - } - ~FILE_output_streambuf() - { - return; - } -protected: - virtual int_type overflow(int_type ch) - { - if(!mpt::IO::IsValid(f)) - { - return traits_type::eof(); - } - if(traits_type::eq_int_type(ch, traits_type::eof())) - { - return traits_type::eof(); - } - char_type c = traits_type::to_char_type(ch); - if(!mpt::IO::WriteRaw(f, &c, 1)) - { - return traits_type::eof(); - } - return ch; - } - virtual int sync() - { - if(!mpt::IO::IsValid(f)) - { - return -1; - } - if(!mpt::IO::Flush(f)) - { - return -1; - } - return 0; - } - virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which) - { - if(!mpt::IO::IsValid(f)) - { - return pos_type(off_type(-1)); - } - return seekoff(pos, std::ios_base::beg, which); - } - virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) - { - if(!mpt::IO::IsValid(f)) - { - return pos_type(off_type(-1)); - } - if(which & std::ios_base::in) - { - return pos_type(off_type(-1)); - } - if(!(which & std::ios_base::out)) - { - return pos_type(off_type(-1)); - } - mpt::IO::Offset oldpos = mpt::IO::TellWrite(f); - if(dir == std::ios_base::beg) - { - if(!mpt::IO::SeekAbsolute(f, off)) - { - mpt::IO::SeekAbsolute(f, oldpos); - return pos_type(off_type(-1)); - } - } else if(dir == std::ios_base::cur) - { - if(!mpt::IO::SeekRelative(f, off)) - { - mpt::IO::SeekAbsolute(f, oldpos); - return pos_type(off_type(-1)); - } - } else if(dir == std::ios_base::end) - { - if(!(mpt::IO::SeekEnd(f) && mpt::IO::SeekRelative(f, off))) - { - mpt::IO::SeekAbsolute(f, oldpos); - return pos_type(off_type(-1)); - } - } else - { - return pos_type(off_type(-1)); - } - mpt::IO::Offset newpos = mpt::IO::TellWrite(f); - if(!mpt::IO::OffsetFits(newpos)) - { - mpt::IO::SeekAbsolute(f, oldpos); - return pos_type(off_type(-1)); - } - return pos_type(static_cast(newpos)); - } -}; // class FILE_output_streambuf - - -class FILE_output_buffered_streambuf : public FILE_output_streambuf -{ -public: - typedef std::streambuf::char_type char_type; - typedef std::streambuf::traits_type traits_type; - typedef traits_type::int_type int_type; - typedef traits_type::pos_type pos_type; - typedef traits_type::off_type off_type; -private: - typedef FILE_output_streambuf Tparent; - std::vector buf; -public: - FILE_output_buffered_streambuf(FILE *f, std::size_t bufSize = 64*1024) - : FILE_output_streambuf(f) - , buf((bufSize > 0) ? bufSize : 1) - { - setp(buf.data(), buf.data() + buf.size()); - } - ~FILE_output_buffered_streambuf() - { - if(!mpt::IO::IsValid(f)) - { - return; - } - WriteOut(); - } -private: - bool IsDirty() const - { - return ((pptr() - pbase()) > 0); - } - bool WriteOut() - { - std::ptrdiff_t n = pptr() - pbase(); - std::ptrdiff_t left = n; - while(left > 0) - { - int backchunk = mpt::saturate_cast(-left); - pbump(backchunk); - left += backchunk; - } - return mpt::IO::WriteRaw(f, pbase(), n); - } -protected: - virtual int_type overflow(int_type ch) - { - if(!mpt::IO::IsValid(f)) - { - return traits_type::eof(); - } - if(traits_type::eq_int_type(ch, traits_type::eof())) - { - return traits_type::eof(); - } - if(!WriteOut()) - { - return traits_type::eof(); - } - char_type c = traits_type::to_char_type(ch); - *pptr() = c; - pbump(1); - return ch; - } - virtual int sync() - { - if(!mpt::IO::IsValid(f)) - { - return -1; - } - if(!WriteOut()) - { - return -1; - } - return Tparent::sync(); - } - virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which) - { - if(!mpt::IO::IsValid(f)) - { - return pos_type(off_type(-1)); - } - if(!WriteOut()) - { - return pos_type(off_type(-1)); - } - return Tparent::seekpos(pos, which); - } - virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) - { - if(!mpt::IO::IsValid(f)) - { - return pos_type(off_type(-1)); - } - if(!WriteOut()) - { - return pos_type(off_type(-1)); - } - return Tparent::seekoff(off, dir, which); - } -}; // class FILE_output_buffered_streambuf - - -class FILE_ostream : public std::ostream { -private: - FILE *f; - FILE_output_buffered_streambuf buf; -public: - FILE_ostream(FILE *f, std::size_t bufSize = 64*1024) - : std::ostream(&buf) - , f(f) - , buf(f, bufSize) - { - if(mpt::IO::IsValid(f)) mpt::IO::Flush(f); - } - ~FILE_ostream() - { - flush(); - buf.pubsync(); - if(mpt::IO::IsValid(f)) mpt::IO::Flush(f); - } -}; // class FILE_ostream - -#endif // MPT_ENABLE_FILEIO_STDIO +#endif // MODPLUG_TRACKER } // namespace mpt @@ -585,5 +318,6 @@ public: #endif // MPT_ENABLE_FILEIO + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp index ba42afaf6..e1c3e3de3 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp @@ -2,8 +2,7 @@ * mptIO.cpp * --------- * Purpose: Basic functions for reading/writing binary and endian safe data to/from files/streams. - * Notes : This is work-in-progress. - * Some useful functions for reading and writing are still missing. + * Notes : Some useful functions for reading and writing are still missing. * Authors: Joern Heusipp * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -18,14 +17,9 @@ #include #include #include -#if MPT_COMPILER_MSVC +#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM #include -#endif // MPT_COMPILER_MSVC - -#if defined(MPT_ENABLE_FILEIO_STDIO) -#include -#include -#endif // MPT_ENABLE_FILEIO_STDIO +#endif // MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM OPENMPT_NAMESPACE_BEGIN @@ -36,7 +30,7 @@ namespace mpt { namespace IO { -#if MPT_COMPILER_MSVC +#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM // MSVC std::stringbuf (and thereby std::ostringstream, std::istringstream and // std::stringstream) fail seekoff() when the stringbuf is currently empty. @@ -124,7 +118,7 @@ static bool StreamIsStringStreamAndValidAndEmpty(std::iostream & f) return dynamic_cast(f.rdbuf())->str().empty(); // slow } -#endif // MPT_COMPILER_MSVC +#endif // MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM //STATIC_ASSERT(sizeof(std::streamoff) == 8); // Assert 64bit file support. bool IsValid(std::ostream & f) { return !f.fail(); } @@ -140,7 +134,7 @@ IO::Offset TellWrite(std::ostream & f) } bool SeekBegin(std::ostream & f) { - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { // VS std::stringbuf fail seek when the internal buffer is empty. Work-around it in case the stream is not already in failed state. f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; @@ -150,7 +144,7 @@ bool SeekBegin(std::ostream & f) } bool SeekBegin(std::istream & f) { - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; @@ -160,7 +154,7 @@ bool SeekBegin(std::istream & f) } bool SeekBegin(std::iostream & f) { - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; @@ -170,7 +164,7 @@ bool SeekBegin(std::iostream & f) } bool SeekEnd(std::ostream & f) { - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; @@ -180,7 +174,7 @@ bool SeekEnd(std::ostream & f) } bool SeekEnd(std::istream & f) { - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; @@ -190,7 +184,7 @@ bool SeekEnd(std::istream & f) } bool SeekEnd(std::iostream & f) { - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; @@ -201,7 +195,7 @@ bool SeekEnd(std::iostream & f) bool SeekAbsolute(std::ostream & f, IO::Offset pos) { if(!OffsetFits(pos)) { return false; } - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { if(pos == 0) @@ -215,7 +209,7 @@ bool SeekAbsolute(std::ostream & f, IO::Offset pos) bool SeekAbsolute(std::istream & f, IO::Offset pos) { if(!OffsetFits(pos)) { return false; } - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { if(pos == 0) @@ -229,7 +223,7 @@ bool SeekAbsolute(std::istream & f, IO::Offset pos) bool SeekAbsolute(std::iostream & f, IO::Offset pos) { if(!OffsetFits(pos)) { return false; } - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { if(pos == 0) @@ -243,7 +237,7 @@ bool SeekAbsolute(std::iostream & f, IO::Offset pos) bool SeekRelative(std::ostream & f, IO::Offset off) { if(!OffsetFits(off)) { return false; } - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { if(off == 0) @@ -257,7 +251,7 @@ bool SeekRelative(std::ostream & f, IO::Offset off) bool SeekRelative(std::istream & f, IO::Offset off) { if(!OffsetFits(off)) { return false; } - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { if(off == 0) @@ -271,7 +265,7 @@ bool SeekRelative(std::istream & f, IO::Offset off) bool SeekRelative(std::iostream & f, IO::Offset off) { if(!OffsetFits(off)) { return false; } - #if MPT_COMPILER_MSVC + #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM if(StreamIsStringStreamAndValidAndEmpty(f)) { if(off == 0) @@ -289,50 +283,6 @@ bool Flush(std::ostream & f) { f.flush(); return !f.fail(); } -#if defined(MPT_ENABLE_FILEIO_STDIO) - -bool IsValid(FILE* & f) { return f != NULL; } - -#if MPT_COMPILER_MSVC - -IO::Offset TellRead(FILE* & f) { return _ftelli64(f); } -IO::Offset TellWrite(FILE* & f) { return _ftelli64(f); } -bool SeekBegin(FILE* & f) { return _fseeki64(f, 0, SEEK_SET) == 0; } -bool SeekEnd(FILE* & f) { return _fseeki64(f, 0, SEEK_END) == 0; } -bool SeekAbsolute(FILE* & f, IO::Offset pos) { return _fseeki64(f, pos, SEEK_SET) == 0; } -bool SeekRelative(FILE* & f, IO::Offset off) { return _fseeki64(f, off, SEEK_CUR) == 0; } - -#elif defined(_POSIX_SOURCE) && (_POSIX_SOURCE > 0) - -//STATIC_ASSERT(sizeof(off_t) == 8); -IO::Offset TellRead(FILE* & f) { return ftello(f); } -IO::Offset TellWrite(FILE* & f) { return ftello(f); } -bool SeekBegin(FILE* & f) { return fseeko(f, 0, SEEK_SET) == 0; } -bool SeekEnd(FILE* & f) { return fseeko(f, 0, SEEK_END) == 0; } -bool SeekAbsolute(FILE* & f, IO::Offset pos) { return OffsetFits(pos) && (fseek(f, mpt::saturate_cast(pos), SEEK_SET) == 0); } -bool SeekRelative(FILE* & f, IO::Offset off) { return OffsetFits(off) && (fseek(f, mpt::saturate_cast(off), SEEK_CUR) == 0); } - -#else - -//STATIC_ASSERT(sizeof(long) == 8); // Fails on 32bit non-POSIX systems for now. -IO::Offset TellRead(FILE* & f) { return ftell(f); } -IO::Offset TellWrite(FILE* & f) { return ftell(f); } -bool SeekBegin(FILE* & f) { return fseek(f, 0, SEEK_SET) == 0; } -bool SeekEnd(FILE* & f) { return fseek(f, 0, SEEK_END) == 0; } -bool SeekAbsolute(FILE* & f, IO::Offset pos) { return OffsetFits(pos) && (fseek(f, mpt::saturate_cast(pos), SEEK_SET) == 0); } -bool SeekRelative(FILE* & f, IO::Offset off) { return OffsetFits(off) && (fseek(f, mpt::saturate_cast(off), SEEK_CUR) == 0); } - -#endif - -IO::Offset ReadRawImpl(FILE * & f, mpt::byte * data, std::size_t size) { return fread(mpt::void_cast(data), 1, size, f); } -bool WriteRawImpl(FILE* & f, const mpt::byte * data, std::size_t size) { return fwrite(mpt::void_cast(data), 1, size, f) == size; } -bool IsEof(FILE * & f) { return feof(f) != 0; } -bool Flush(FILE* & f) { return fflush(f) == 0; } - -#endif // MPT_ENABLE_FILEIO_STDIO - - - } // namespace IO } // namespace mpt @@ -350,10 +300,6 @@ FileDataContainerSeekable::FileDataContainerSeekable(off_t streamLength) return; } -FileDataContainerSeekable::~FileDataContainerSeekable() -{ - return; -} void FileDataContainerSeekable::CacheStream() const { if(cached) @@ -461,11 +407,6 @@ FileDataContainerStdStreamSeekable::FileDataContainerStdStreamSeekable(std::istr return; } -FileDataContainerStdStreamSeekable::~FileDataContainerStdStreamSeekable() -{ - return; -} - IFileDataContainer::off_t FileDataContainerStdStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const { stream->clear(); // tellg needs eof and fail bits unset @@ -485,11 +426,6 @@ FileDataContainerUnseekable::FileDataContainerUnseekable() return; } -FileDataContainerUnseekable::~FileDataContainerUnseekable() -{ - return; -} - void FileDataContainerUnseekable::EnsureCacheBuffer(std::size_t requiredbuffersize) const { if(cache.size() >= cachesize + requiredbuffersize) @@ -627,11 +563,6 @@ FileDataContainerStdStream::FileDataContainerStdStream(std::istream *s) return; } -FileDataContainerStdStream::~FileDataContainerStdStream() -{ - return; -} - bool FileDataContainerStdStream::InternalEof() const { if(*stream) @@ -739,11 +670,6 @@ FileDataContainerCallbackStreamSeekable::FileDataContainerCallbackStreamSeekable return; } -FileDataContainerCallbackStreamSeekable::~FileDataContainerCallbackStreamSeekable() -{ - return; -} - IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const { if(!stream.read) @@ -779,11 +705,6 @@ FileDataContainerCallbackStream::FileDataContainerCallbackStream(CallbackStream return; } -FileDataContainerCallbackStream::~FileDataContainerCallbackStream() -{ - return; -} - bool FileDataContainerCallbackStream::InternalEof() const { return eof_reached; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptIO.h index 4899124b5..d6b059abd 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptIO.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.h @@ -2,8 +2,7 @@ * mptIO.h * ------- * Purpose: Basic functions for reading/writing binary and endian safe data to/from files/streams. - * Notes : This is work-in-progress. - * Some useful functions for reading and writing are still missing. + * Notes : Some useful functions for reading and writing are still missing. * Authors: Joern Heusipp * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -12,20 +11,16 @@ #pragma once +#include "BuildSettings.h" + -#include "../common/typedefs.h" -#include "../common/mptTypeTraits.h" #include "../common/Endianness.h" #include +#include #include #include #include -#if defined(MPT_ENABLE_FILEIO_STDIO) -#include -#include -#endif // MPT_ENABLE_FILEIO_STDIO - OPENMPT_NAMESPACE_BEGIN @@ -36,10 +31,10 @@ namespace IO { typedef int64 Offset; -static const std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage -static const std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap -static const std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O -static const std::size_t BUFFERSIZE_LARGE = 1024 * 1024; +static constexpr std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage +static constexpr std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap +static constexpr std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O +static constexpr std::size_t BUFFERSIZE_LARGE = 1024 * 1024; @@ -76,21 +71,21 @@ bool Flush(std::ostream & f); -#if defined(MPT_ENABLE_FILEIO_STDIO) +template class WriteBuffer; + +template bool IsValid(WriteBuffer & f) { return IsValid(f.file()); } +template IO::Offset TellRead(WriteBuffer & f) { return TellRead(f.file()); } +template IO::Offset TellWrite(WriteBuffer & f) { return TellWrite(f.file()); } +template bool SeekBegin(WriteBuffer & f) { return SeekBegin(f.file()); } +template bool SeekEnd(WriteBuffer & f) { return SeekEnd(f.file()); } +template bool SeekAbsolute(WriteBuffer & f, IO::Offset pos) { return SeekAbsolute(f.file(), pos); } +template bool SeekRelative(WriteBuffer & f, IO::Offset off) { return SeekRelative(f.file(), off); } +template IO::Offset ReadRawImpl(WriteBuffer & f, mpt::byte * data, std::size_t size) { return ReadRawImpl(f.file(), data, size); } +template bool WriteRawImpl(WriteBuffer & f, const mpt::byte * data, std::size_t size) { return f.Write(mpt::as_span(data, size)); } +template bool IsEof(WriteBuffer & f) { return IsEof(f.file()); } +template bool Flush(WriteBuffer & f) { return Flush(f.file()); } -bool IsValid(FILE* & f); -IO::Offset TellRead(FILE* & f); -IO::Offset TellWrite(FILE* & f); -bool SeekBegin(FILE* & f); -bool SeekEnd(FILE* & f); -bool SeekAbsolute(FILE* & f, IO::Offset pos); -bool SeekRelative(FILE* & f, IO::Offset off); -IO::Offset ReadRawImpl(FILE * & f, mpt::byte * data, std::size_t size); -bool WriteRawImpl(FILE* & f, const mpt::byte * data, std::size_t size); -bool IsEof(FILE * & f); -bool Flush(FILE* & f); -#endif // MPT_ENABLE_FILEIO_STDIO @@ -182,29 +177,65 @@ inline IO::Offset ReadRaw(Tfile & f, Tbyte * data, std::size_t size) return IO::ReadRawImpl(f, mpt::byte_cast(data), size); } +template +inline IO::Offset ReadRaw(Tfile & f, mpt::span data) +{ + return IO::ReadRawImpl(f, mpt::byte_cast(data.data()), data.size()); +} + template inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) { return IO::WriteRawImpl(f, mpt::byte_cast(data), size); } +template +inline bool WriteRaw(Tfile & f, mpt::span data) +{ + return IO::WriteRawImpl(f, mpt::byte_cast(data.data()), data.size()); +} + template inline bool Read(Tfile & f, Tbinary & v) { - return IO::ReadRaw(f, mpt::as_raw_memory(v), sizeof(Tbinary)) == sizeof(Tbinary); + return IO::ReadRaw(f, mpt::as_raw_memory(v)) == mpt::saturate_cast(mpt::as_raw_memory(v).size()); } template inline bool Write(Tfile & f, const Tbinary & v) { - return IO::WriteRaw(f, mpt::as_raw_memory(v), sizeof(Tbinary)); + return IO::WriteRaw(f, mpt::as_raw_memory(v)); +} + +template +inline bool Write(Tfile & f, const std::vector & v) +{ + STATIC_ASSERT(mpt::is_binary_safe::value); + return IO::WriteRaw(f, mpt::as_raw_memory(v)); } template inline bool WritePartial(Tfile & f, const T & v, size_t size = sizeof(T)) { MPT_ASSERT(size <= sizeof(T)); - return IO::WriteRaw(f, mpt::as_raw_memory(v), size); + return IO::WriteRaw(f, mpt::as_span(mpt::as_raw_memory(v).data(), size)); +} + +template +inline bool ReadByte(Tfile & f, mpt::byte & v) +{ + bool result = false; + mpt::byte byte = mpt::as_byte(0); + const IO::Offset readResult = IO::ReadRaw(f, &byte, sizeof(mpt::byte)); + if(readResult < 0) + { + result = false; + } else + { + result = (static_cast(readResult) == sizeof(mpt::byte)); + } + v = byte; + return result; } template @@ -212,7 +243,7 @@ inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) { bool result = false; MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - mpt::byte bytes[sizeof(T)]; + uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, std::min(size, sizeof(T))); if(readResult < 0) @@ -222,10 +253,9 @@ inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) { result = (static_cast(readResult) == std::min(size, sizeof(T))); } - #ifdef MPT_PLATFORM_BIG_ENDIAN - std::reverse(bytes, bytes + sizeof(T)); - #endif - std::memcpy(&v, bytes, sizeof(T)); + typename mpt::make_le::type val; + std::memcpy(&val, bytes, sizeof(T)); + v = val; return result; } @@ -234,7 +264,7 @@ inline bool ReadIntLE(Tfile & f, T & v) { bool result = false; STATIC_ASSERT(std::numeric_limits::is_integer); - mpt::byte bytes[sizeof(T)]; + uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); if(readResult < 0) @@ -244,9 +274,9 @@ inline bool ReadIntLE(Tfile & f, T & v) { result = (static_cast(readResult) == sizeof(T)); } - T val = 0; + typename mpt::make_le::type val; std::memcpy(&val, bytes, sizeof(T)); - v = SwapBytesLE(val); + v = val; return result; } @@ -255,7 +285,7 @@ inline bool ReadIntBE(Tfile & f, T & v) { bool result = false; STATIC_ASSERT(std::numeric_limits::is_integer); - mpt::byte bytes[sizeof(T)]; + uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); if(readResult < 0) @@ -265,9 +295,9 @@ inline bool ReadIntBE(Tfile & f, T & v) { result = (static_cast(readResult) == sizeof(T)); } - T val = 0; + typename mpt::make_be::type val; std::memcpy(&val, bytes, sizeof(T)); - v = SwapBytesBE(val); + v = val; return result; } @@ -275,17 +305,17 @@ template inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v) { bool result = true; - mpt::byte byte = 0; + uint8 byte = 0; std::size_t additionalBytes = 0; v = 0; byte = 0; - if(!IO::ReadIntLE(f, byte)) result = false; + if(!IO::ReadIntLE(f, byte)) result = false; additionalBytes = (byte & 0x01); v = byte >> 1; for(std::size_t i = 0; i < additionalBytes; ++i) { byte = 0; - if(!IO::ReadIntLE(f, byte)) result = false; + if(!IO::ReadIntLE(f, byte)) result = false; v |= (static_cast(byte) << (((i+1)*8) - 1)); } return result; @@ -295,17 +325,17 @@ template inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v) { bool result = true; - mpt::byte byte = 0; + uint8 byte = 0; std::size_t additionalBytes = 0; v = 0; byte = 0; - if(!IO::ReadIntLE(f, byte)) result = false; + if(!IO::ReadIntLE(f, byte)) result = false; additionalBytes = (byte & 0x03); v = byte >> 2; for(std::size_t i = 0; i < additionalBytes; ++i) { byte = 0; - if(!IO::ReadIntLE(f, byte)) result = false; + if(!IO::ReadIntLE(f, byte)) result = false; v |= (static_cast(byte) << (((i+1)*8) - 2)); } return result; @@ -315,17 +345,17 @@ template inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) { bool result = true; - mpt::byte byte = 0; + uint8 byte = 0; std::size_t additionalBytes = 0; v = 0; byte = 0; - if(!IO::ReadIntLE(f, byte)) result = false; + if(!IO::ReadIntLE(f, byte)) result = false; additionalBytes = (1 << (byte & 0x03)) - 1; v = byte >> 2; for(std::size_t i = 0; i < additionalBytes; ++i) { byte = 0; - if(!IO::ReadIntLE(f, byte)) result = false; + if(!IO::ReadIntLE(f, byte)) result = false; v |= (static_cast(byte) << (((i+1)*8) - 2)); } return result; @@ -362,32 +392,32 @@ template inline bool WriteIntLE(Tfile & f, const T v) { STATIC_ASSERT(std::numeric_limits::is_integer); - const T val = SwapBytesLE(v); - mpt::byte bytes[sizeof(T)]; - std::memcpy(bytes, &val, sizeof(T)); - return IO::WriteRaw(f, bytes, sizeof(T)); + return IO::Write(f, mpt::as_le(v)); } template inline bool WriteIntBE(Tfile & f, const T v) { STATIC_ASSERT(std::numeric_limits::is_integer); - const T val = SwapBytesBE(v); - mpt::byte bytes[sizeof(T)]; - std::memcpy(bytes, &val, sizeof(T)); - return IO::WriteRaw(f, bytes, sizeof(T)); + return IO::Write(f, mpt::as_be(v)); } template -inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t minSize = 0, std::size_t maxSize = 0) +inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t fixedSize = 0) { + std::size_t minSize = fixedSize; + std::size_t maxSize = fixedSize; MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2); MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2); MPT_ASSERT(maxSize == 0 || maxSize >= minSize); - if(v < 0x80 && minSize <= 1 && (1 <= maxSize || maxSize == 0)) + if(maxSize == 0) + { + maxSize = 2; + } + if(v < 0x80 && minSize <= 1 && 1 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 1) | 0x00); - } else if(v < 0x8000 && minSize <= 2 && (2 <= maxSize || maxSize == 0)) + } else if(v < 0x8000 && minSize <= 2 && 2 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 1) | 0x01); } else @@ -398,18 +428,24 @@ inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t minSize } template -inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t minSize = 0, std::size_t maxSize = 0) +inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSize = 0) { + std::size_t minSize = fixedSize; + std::size_t maxSize = fixedSize; MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4); MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4); MPT_ASSERT(maxSize == 0 || maxSize >= minSize); - if(v < 0x40 && minSize <= 1 && (1 <= maxSize || maxSize == 0)) + if(maxSize == 0) + { + maxSize = 4; + } + if(v < 0x40 && minSize <= 1 && 1 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 2) | 0x00); - } else if(v < 0x4000 && minSize <= 2 && (2 <= maxSize || maxSize == 0)) + } else if(v < 0x4000 && minSize <= 2 && 2 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 2) | 0x01); - } else if(v < 0x400000 && minSize <= 3 && (3 <= maxSize || maxSize == 0)) + } else if(v < 0x400000 && minSize <= 3 && 3 <= maxSize) { uint32 value = static_cast(v << 2) | 0x02; mpt::byte bytes[3]; @@ -417,7 +453,7 @@ inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t minSize bytes[1] = static_cast(value >> 8); bytes[2] = static_cast(value >> 16); return IO::WriteRaw(f, bytes, 3); - } else if(v < 0x40000000 && minSize <= 4 && (4 <= maxSize || maxSize == 0)) + } else if(v < 0x40000000 && minSize <= 4 && 4 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 2) | 0x03); } else @@ -428,21 +464,27 @@ inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t minSize } template -inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t minSize = 0, std::size_t maxSize = 0) +inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSize = 0) { + std::size_t minSize = fixedSize; + std::size_t maxSize = fixedSize; MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8); MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8); MPT_ASSERT(maxSize == 0 || maxSize >= minSize); - if(v < 0x40 && minSize <= 1 && (1 <= maxSize || maxSize == 0)) + if(maxSize == 0) + { + maxSize = 8; + } + if(v < 0x40 && minSize <= 1 && 1 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 2) | 0x00); - } else if(v < 0x4000 && minSize <= 2 && (2 <= maxSize || maxSize == 0)) + } else if(v < 0x4000 && minSize <= 2 && 2 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 2) | 0x01); - } else if(v < 0x40000000 && minSize <= 4 && (4 <= maxSize || maxSize == 0)) + } else if(v < 0x40000000 && minSize <= 4 && 4 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 2) | 0x02); - } else if(v < 0x4000000000000000ull && minSize <= 8 && (8 <= maxSize || maxSize == 0)) + } else if(v < 0x4000000000000000ull && minSize <= 8 && 8 <= maxSize) { return IO::WriteIntLE(f, static_cast(v << 2) | 0x03); } else @@ -523,6 +565,105 @@ inline bool WriteTextLF(Tfile &f, const std::string &s) return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f); } + + +// WriteBuffer class that avoids calling to the underlying file writing +// functions for every operation, which would involve rather slow un-inlinable +// virtual calls in the iostream and FILE* cases. It is the users responabiliy +// to call HasWriteError() to check for writeback errors at this buffering +// level. + +template +class WriteBuffer +{ +private: + mpt::byte_span buffer; + std::size_t size = 0; + Tfile & f; + bool writeError = false; +public: + WriteBuffer(const WriteBuffer &) = delete; + WriteBuffer & operator=(const WriteBuffer &) = delete; +public: + inline WriteBuffer(Tfile & f_, mpt::byte_span buffer_) + : buffer(buffer_) + , f(f_) + { + } + inline ~WriteBuffer() noexcept(false) + { + if(!writeError) + { + FlushLocal(); + } + } +public: + inline Tfile & file() const + { + if(IsDirty()) + { + FlushLocal(); + } + return f; + } +public: + inline bool HasWriteError() const + { + return writeError; + } + inline void ClearError() + { + writeError = false; + } + inline bool IsDirty() const + { + return size > 0; + } + inline bool IsClean() const + { + return size == 0; + } + inline bool IsFull() const + { + return size == buffer.size(); + } + inline bool Write(mpt::const_byte_span data) + { + bool result = true; + for(std::size_t i = 0; i < data.size(); ++i) + { + buffer[size] = data[i]; + size++; + if(IsFull()) + { + FlushLocal(); + } + } + return result; + } + inline void FlushLocal() + { + if(IsClean()) + { + return; + } + try + { + if(!mpt::IO::WriteRaw(f, mpt::as_span(buffer.data(), size))) + { + writeError = true; + } + } catch (const std::exception &) + { + writeError = true; + throw; + } + size = 0; + } +}; + + + } // namespace IO @@ -536,9 +677,11 @@ class IFileDataContainer { public: typedef std::size_t off_t; protected: - IFileDataContainer() { } + IFileDataContainer() = default; public: - virtual ~IFileDataContainer() { } + IFileDataContainer(const IFileDataContainer&) = default; + IFileDataContainer & operator=(const IFileDataContainer&) = default; + virtual ~IFileDataContainer() = default; public: virtual bool IsValid() const = 0; virtual bool HasFastGetLength() const = 0; @@ -547,6 +690,11 @@ public: virtual off_t GetLength() const = 0; virtual off_t Read(mpt::byte *dst, off_t pos, off_t count) const = 0; + virtual off_t Read(off_t pos, mpt::byte_span dst) const + { + return Read(dst.data(), pos, dst.size()); + } + virtual bool CanRead(off_t pos, off_t length) const { off_t dataLength = GetLength(); @@ -576,33 +724,32 @@ public: class FileDataContainerDummy : public IFileDataContainer { public: FileDataContainerDummy() { } - virtual ~FileDataContainerDummy() { } public: - bool IsValid() const + bool IsValid() const override { return false; } - bool HasFastGetLength() const + bool HasFastGetLength() const override { return true; } - bool HasPinnedView() const + bool HasPinnedView() const override { return true; } - const mpt::byte *GetRawData() const + const mpt::byte *GetRawData() const override { return nullptr; } - off_t GetLength() const + off_t GetLength() const override { return 0; } - off_t Read(mpt::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const + off_t Read(mpt::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const override { return 0; } @@ -617,27 +764,28 @@ private: const off_t dataLength; public: FileDataContainerWindow(std::shared_ptr src, off_t off, off_t len) : data(src), dataOffset(off), dataLength(len) { } - virtual ~FileDataContainerWindow() { } - bool IsValid() const + bool IsValid() const override { return data->IsValid(); } - bool HasFastGetLength() const + bool HasFastGetLength() const override { return data->HasFastGetLength(); } - bool HasPinnedView() const + bool HasPinnedView() const override { return data->HasPinnedView(); } - const mpt::byte *GetRawData() const { + const mpt::byte *GetRawData() const override + { return data->GetRawData() + dataOffset; } - off_t GetLength() const { + off_t GetLength() const override + { return dataLength; } - off_t Read(mpt::byte *dst, off_t pos, off_t count) const + off_t Read(mpt::byte *dst, off_t pos, off_t count) const override { if(pos >= dataLength) { @@ -645,7 +793,8 @@ public: } return data->Read(dst, dataOffset + pos, std::min(count, dataLength - pos)); } - bool CanRead(off_t pos, off_t length) const { + bool CanRead(off_t pos, off_t length) const override + { if((pos == dataLength) && (length == 0)) { return true; @@ -656,7 +805,7 @@ public: } return (length <= dataLength - pos); } - off_t GetReadableLength(off_t pos, off_t length) const + off_t GetReadableLength(off_t pos, off_t length) const override { if(pos >= dataLength) { @@ -679,7 +828,6 @@ private: protected: FileDataContainerSeekable(off_t length); - virtual ~FileDataContainerSeekable(); private: @@ -687,12 +835,12 @@ private: public: - bool IsValid() const; - bool HasFastGetLength() const; - bool HasPinnedView() const; - const mpt::byte *GetRawData() const; - off_t GetLength() const; - off_t Read(mpt::byte *dst, off_t pos, off_t count) const; + bool IsValid() const override; + bool HasFastGetLength() const override; + bool HasPinnedView() const override; + const mpt::byte *GetRawData() const override; + off_t GetLength() const override; + off_t Read(mpt::byte *dst, off_t pos, off_t count) const override; private: @@ -710,14 +858,13 @@ private: public: FileDataContainerStdStreamSeekable(std::istream *s); - virtual ~FileDataContainerStdStreamSeekable(); static bool IsSeekable(std::istream *stream); static off_t GetLength(std::istream *stream); private: - off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const; + off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const override; }; @@ -733,12 +880,13 @@ private: protected: FileDataContainerUnseekable(); - virtual ~FileDataContainerUnseekable(); private: - static const std::size_t QUANTUM_SIZE = mpt::IO::BUFFERSIZE_SMALL; - static const std::size_t BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL; + enum : std::size_t { + QUANTUM_SIZE = mpt::IO::BUFFERSIZE_SMALL, + BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL + }; void EnsureCacheBuffer(std::size_t requiredbuffersize) const; void CacheStream() const; @@ -750,14 +898,14 @@ private: public: - bool IsValid() const; - bool HasFastGetLength() const; - bool HasPinnedView() const; - const mpt::byte *GetRawData() const; - off_t GetLength() const; - off_t Read(mpt::byte *dst, off_t pos, off_t count) const; - bool CanRead(off_t pos, off_t length) const; - off_t GetReadableLength(off_t pos, off_t length) const; + bool IsValid() const override; + bool HasFastGetLength() const override; + bool HasPinnedView() const override; + const mpt::byte *GetRawData() const override; + off_t GetLength() const override; + off_t Read(mpt::byte *dst, off_t pos, off_t count) const override; + bool CanRead(off_t pos, off_t length) const override; + off_t GetReadableLength(off_t pos, off_t length) const override; private: @@ -776,12 +924,11 @@ private: public: FileDataContainerStdStream(std::istream *s); - virtual ~FileDataContainerStdStream(); private: - bool InternalEof() const; - off_t InternalRead(mpt::byte *dst, off_t count) const; + bool InternalEof() const override; + off_t InternalRead(mpt::byte *dst, off_t count) const override; }; @@ -791,9 +938,11 @@ private: struct CallbackStream { - static const int SeekSet = 0; - static const int SeekCur = 1; - static const int SeekEnd = 2; + enum : int { + SeekSet = 0, + SeekCur = 1, + SeekEnd = 2 + }; void *stream; std::size_t (*read)( void * stream, void * dst, std::size_t bytes ); int (*seek)( void * stream, int64 offset, int whence ); @@ -809,9 +958,8 @@ public: static bool IsSeekable(CallbackStream stream); static off_t GetLength(CallbackStream stream); FileDataContainerCallbackStreamSeekable(CallbackStream s); - virtual ~FileDataContainerCallbackStreamSeekable(); private: - off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const; + off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const override; }; @@ -822,10 +970,9 @@ private: mutable bool eof_reached; public: FileDataContainerCallbackStream(CallbackStream s); - virtual ~FileDataContainerCallbackStream(); private: - bool InternalEof() const; - off_t InternalRead(mpt::byte *dst, off_t count) const; + bool InternalEof() const override; + off_t InternalRead(mpt::byte *dst, off_t count) const override; }; @@ -841,6 +988,12 @@ class FileDataContainerMemory #endif { +#if defined(MPT_FILEREADER_STD_ISTREAM) +#define MPT_FILEDATACONTAINERMEMORY_OVERRIDE override +#else +#define MPT_FILEDATACONTAINERMEMORY_OVERRIDE +#endif + #if !defined(MPT_FILEREADER_STD_ISTREAM) public: typedef std::size_t off_t; @@ -854,39 +1007,35 @@ private: public: FileDataContainerMemory() : streamData(nullptr), streamLength(0) { } FileDataContainerMemory(mpt::const_byte_span data) : streamData(data.data()), streamLength(data.size()) { } -#if defined(MPT_FILEREADER_STD_ISTREAM) - virtual -#endif - ~FileDataContainerMemory() { } public: - bool IsValid() const + bool IsValid() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { return streamData != nullptr; } - bool HasFastGetLength() const + bool HasFastGetLength() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { return true; } - bool HasPinnedView() const + bool HasPinnedView() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { return true; } - const mpt::byte *GetRawData() const + const mpt::byte *GetRawData() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { return streamData; } - off_t GetLength() const + off_t GetLength() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { return streamLength; } - off_t Read(mpt::byte *dst, off_t pos, off_t count) const + off_t Read(mpt::byte *dst, off_t pos, off_t count) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { if(pos >= streamLength) { @@ -897,7 +1046,12 @@ public: return avail; } - bool CanRead(off_t pos, off_t length) const + off_t Read(off_t pos, mpt::byte_span dst) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + { + return Read(dst.data(), pos, dst.size()); + } + + bool CanRead(off_t pos, off_t length) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { if((pos == streamLength) && (length == 0)) { @@ -910,7 +1064,7 @@ public: return (length <= streamLength - pos); } - off_t GetReadableLength(off_t pos, off_t length) const + off_t GetReadableLength(off_t pos, off_t length) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE { if(pos >= streamLength) { diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp index 2bc01fe69..41b422782 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp @@ -119,10 +119,10 @@ public: switch(path.GetSearchPath()) { case mpt::LibrarySearchPathDefault: - hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); break; case mpt::LibrarySearchPathSystem: - hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); break; #if defined(MODPLUG_TRACKER) // Using restricted search paths applies to potential DLL dependencies @@ -136,20 +136,20 @@ public: const mpt::PathString dllPath = mpt::GetAppPath(); if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) { - hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str()); + hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str()); } } break; case mpt::LibrarySearchPathFullPath: - hModule = LoadLibraryW(path.GetFileName().AsNative().c_str()); + hModule = LoadLibrary(path.GetFileName().AsNative().c_str()); break; #else // For libopenmpt, do the safe thing. case mpt::LibrarySearchPathApplication: - hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); break; case mpt::LibrarySearchPathFullPath: - hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); + hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); break; #endif case mpt::LibrarySearchPathInvalid: @@ -161,14 +161,14 @@ public: switch(path.GetSearchPath()) { case mpt::LibrarySearchPathDefault: - hModule = LoadLibraryW(path.GetFileName().AsNative().c_str()); + hModule = LoadLibrary(path.GetFileName().AsNative().c_str()); break; case mpt::LibrarySearchPathApplication: { const mpt::PathString dllPath = mpt::GetAppPath(); if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) { - hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str()); + hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str()); } } break; @@ -177,12 +177,12 @@ public: const mpt::PathString dllPath = mpt::GetSystemPath(); if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) { - hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str()); + hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str()); } } break; case mpt::LibrarySearchPathFullPath: - hModule = LoadLibraryW(path.GetFileName().AsNative().c_str()); + hModule = LoadLibrary(path.GetFileName().AsNative().c_str()); break; case mpt::LibrarySearchPathInvalid: MPT_ASSERT_NOTREACHED(); @@ -432,15 +432,15 @@ mpt::PathString LibraryPath::GetFileName() const mpt::PathString LibraryPath::GetDefaultPrefix() { #if MPT_OS_WINDOWS - return MPT_PATHSTRING(""); + return P_(""); #elif MPT_OS_ANDROID - return MPT_PATHSTRING("lib"); + return P_("lib"); #elif defined(MPT_WITH_LTDL) - return MPT_PATHSTRING("lib"); + return P_("lib"); #elif defined(MPT_WITH_DL) - return MPT_PATHSTRING("lib"); + return P_("lib"); #else - return MPT_PATHSTRING("lib"); + return P_("lib"); #endif } @@ -448,13 +448,13 @@ mpt::PathString LibraryPath::GetDefaultPrefix() mpt::PathString LibraryPath::GetDefaultSuffix() { #if MPT_OS_WINDOWS - return MPT_PATHSTRING(".dll"); + return P_(".dll"); #elif MPT_OS_ANDROID - return MPT_PATHSTRING(".so"); + return P_(".so"); #elif defined(MPT_WITH_LTDL) - return MPT_PATHSTRING(""); // handled by libltdl + return P_(""); // handled by libltdl #elif defined(MPT_WITH_DL) - return MPT_PATHSTRING(".so"); + return P_(".so"); #else return mpt::PathString(); #endif @@ -479,7 +479,7 @@ LibraryPath LibraryPath::AppDataFullName(const mpt::PathString &fullname, const { if(appdata.empty()) { - return LibraryPath(mpt::LibrarySearchPathInvalid, MPT_PATHSTRING("")); + return LibraryPath(mpt::LibrarySearchPathInvalid, P_("")); } return LibraryPath(mpt::LibrarySearchPathFullPath, appdata.WithTrailingSlash() + fullname + GetDefaultSuffix()); } @@ -544,6 +544,12 @@ FuncPtr Library::GetProcAddress(const std::string &symbol) const } // namespace mpt +#else // !MPT_ENABLE_DYNBIND + + +MPT_MSVC_WORKAROUND_LNK4221(mptLibrary) + + #endif // MPT_ENABLE_DYNBIND diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h index 36a7148c4..d182b910b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h b/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h new file mode 100644 index 000000000..539c58dec --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h @@ -0,0 +1,370 @@ +/* + * mptMemory.h + * ----------- + * Purpose: Raw memory manipulation + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "mptAssert.h" +#include "mptBaseTypes.h" +#include "mptSpan.h" + +#if MPT_CXX_AT_LEAST(20) +#include +#endif +#include + +#include + +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + +typedef mpt::span byte_span; +typedef mpt::span const_byte_span; + + + +// Tell which types are safe for mpt::byte_cast. +// signed char is actually not allowed to alias into an object representation, +// which means that, if the actual type is not itself signed char but char or +// unsigned char instead, dereferencing the signed char pointer is undefined +// behaviour. +template struct is_byte_castable : public std::false_type { }; +template <> struct is_byte_castable : public std::true_type { }; +template <> struct is_byte_castable : public std::true_type { }; +#if MPT_BYTE_IS_STD_BYTE +template <> struct is_byte_castable : public std::true_type { }; +#endif +template <> struct is_byte_castable : public std::true_type { }; +template <> struct is_byte_castable : public std::true_type { }; +#if MPT_BYTE_IS_STD_BYTE +template <> struct is_byte_castable : public std::true_type { }; +#endif + + +template struct is_byte : public std::false_type { }; +template <> struct is_byte : public std::true_type { }; +template <> struct is_byte : public std::true_type { }; + + +// Tell which types are safe to binary write into files. +// By default, no types are safe. +// When a safe type gets defined, +// also specialize this template so that IO functions will work. +template struct is_binary_safe : public std::false_type { }; + +// Specialization for byte types. +template <> struct is_binary_safe : public std::true_type { }; +template <> struct is_binary_safe : public std::true_type { }; +template <> struct is_binary_safe : public std::true_type { }; +#if MPT_BYTE_IS_STD_BYTE +template <> struct is_binary_safe : public std::true_type { }; +#endif + +// Generic Specialization for arrays. +template struct is_binary_safe : public is_binary_safe { }; +template struct is_binary_safe : public is_binary_safe { }; +template struct is_binary_safe> : public is_binary_safe { }; +template struct is_binary_safe> : public is_binary_safe { }; + + +} // namespace mpt + +#define MPT_BINARY_STRUCT(type, size) \ + MPT_STATIC_ASSERT(sizeof( type ) == (size) ); \ + MPT_STATIC_ASSERT(alignof( type ) == 1); \ + MPT_STATIC_ASSERT(std::is_standard_layout< type >::value); \ + namespace mpt { \ + template <> struct is_binary_safe< type > : public std::true_type { }; \ + } \ +/**/ + + + +template +struct value_initializer +{ + inline void operator () (T & x) + { + x = T(); + } +}; + +template +struct value_initializer +{ + inline void operator () (T (& a)[N]) + { + for(auto & e : a) + { + value_initializer()(e); + } + } +}; + +template +inline void Clear(T & x) +{ + MPT_STATIC_ASSERT(!std::is_pointer::value); + value_initializer()(x); +} + + +// Memset given object to zero. +template +inline void MemsetZero(T &a) +{ + static_assert(std::is_pointer::value == false, "Won't memset pointers."); +#if MPT_GCC_BEFORE(5,1,0) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) + MPT_STATIC_ASSERT(std::is_standard_layout::value); + MPT_STATIC_ASSERT(std::is_trivial::value || mpt::is_binary_safe::value); // approximation +#else // default + MPT_STATIC_ASSERT(std::is_standard_layout::value); + MPT_STATIC_ASSERT((std::is_trivially_default_constructible::value && std::is_trivially_copyable::value) || mpt::is_binary_safe::value); // C++11, but not supported on most compilers we care about +#endif + std::memset(&a, 0, sizeof(T)); +} + + + +namespace mpt { + + + +#if MPT_CXX_AT_LEAST(20) +using std::bit_cast; +#else +// C++2a compatible bit_cast. +// See . +// Not implementing constexpr because this is not easily possible pre C++2a. +template +MPT_FORCEINLINE Tdst bit_cast(const Tsrc & src) noexcept +{ + MPT_STATIC_ASSERT(sizeof(Tdst) == sizeof(Tsrc)); +#if MPT_GCC_BEFORE(5,1,0) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) + MPT_STATIC_ASSERT(std::is_trivial::value); // approximation + MPT_STATIC_ASSERT(std::is_trivial::value); // approximation +#else // default + MPT_STATIC_ASSERT(std::is_trivially_copyable::value); + MPT_STATIC_ASSERT(std::is_trivially_copyable::value); +#endif + #if MPT_COMPILER_GCC || MPT_COMPILER_MSVC + // Compiler supports type-punning through unions. This is not stricly standard-conforming. + // For GCC, this is documented, for MSVC this is apparently not documented, but we assume it. + union { + Tsrc src; + Tdst dst; + } conv; + conv.src = src; + return conv.dst; + #else // MPT_COMPILER + // Compiler does not support type-punning through unions. std::memcpy is used instead. + // This is the safe fallback and strictly standard-conforming. + // Another standard-compliant alternative would be casting pointers to a character type pointer. + // This results in rather unreadable code and, + // in most cases, compilers generate better code by just inlining the memcpy anyway. + // (see ). + Tdst dst{}; + std::memcpy(&dst, &src, sizeof(Tdst)); + return dst; + #endif // MPT_COMPILER +} +#endif + + + +template +struct byte_cast_impl +{ + inline Tdst operator () (Tsrc src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + // not checking is_byte_castable here because we are actually + // doing a static_cast and converting the value + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + return static_cast(src); + } +}; +template +struct byte_cast_impl, mpt::span > +{ + inline mpt::span operator () (mpt::span src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + return mpt::as_span(mpt::byte_cast_impl()(src.begin()), mpt::byte_cast_impl()(src.end())); + } +}; +template +struct byte_cast_impl +{ + inline Tdst* operator () (Tsrc* src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + return reinterpret_cast(src); + } +}; + +template +struct void_cast_impl; + +template +struct void_cast_impl +{ + inline Tdst* operator () (void* src) const + { + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + return reinterpret_cast(src); + } +}; +template +struct void_cast_impl +{ + inline Tdst* operator () (const void* src) const + { + STATIC_ASSERT(sizeof(Tdst) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + return reinterpret_cast(src); + } +}; +template +struct void_cast_impl +{ + inline void* operator () (Tsrc* src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + return reinterpret_cast(src); + } +}; +template +struct void_cast_impl +{ + inline const void* operator () (Tsrc* src) const + { + STATIC_ASSERT(sizeof(Tsrc) == sizeof(mpt::byte)); + STATIC_ASSERT(mpt::is_byte_castable::value); + STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + return reinterpret_cast(src); + } +}; + +// casts between different byte (char) types or pointers to these types +template +inline Tdst byte_cast(Tsrc src) +{ + return byte_cast_impl()(src); +} + +// casts between pointers to void and pointers to byte +template +inline Tdst void_cast(Tsrc src) +{ + return void_cast_impl()(src); +} + + + +template +MPT_CONSTEXPR14_FUN mpt::byte as_byte(T src) noexcept +{ + MPT_STATIC_ASSERT(std::is_integral::value); + return static_cast(static_cast(src)); +} + + + +template +struct GetRawBytesFunctor +{ + inline mpt::const_byte_span operator () (const T & v) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(&v), sizeof(T)); + } + inline mpt::byte_span operator () (T & v) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(&v), sizeof(T)); + } +}; + +template +struct GetRawBytesFunctor +{ + inline mpt::const_byte_span operator () (const T (&v)[N]) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); + } + inline mpt::byte_span operator () (T (&v)[N]) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); + } +}; + +template +struct GetRawBytesFunctor +{ + inline mpt::const_byte_span operator () (const T (&v)[N]) const + { + STATIC_ASSERT(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); + } +}; + +// In order to be able to partially specialize it, +// as_raw_memory is implemented via a class template. +// Do not overload or specialize as_raw_memory directly. +// Using a wrapper (by default just around a cast to const mpt::byte *), +// allows for implementing raw memory access +// via on-demand generating a cached serialized representation. +template inline mpt::const_byte_span as_raw_memory(const T & v) +{ + return mpt::GetRawBytesFunctor()(v); +} +template inline mpt::byte_span as_raw_memory(T & v) +{ + return mpt::GetRawBytesFunctor()(v); +} + + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h index 26d0971a1..669a0c350 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + #include // some C++ header in order to have the C++ standard library version information available #if !MPT_PLATFORM_MULTITHREADED @@ -19,27 +21,27 @@ #define MPT_MUTEX_STD 0 #define MPT_MUTEX_PTHREAD 0 #define MPT_MUTEX_WIN32 0 -#elif MPT_COMPILER_GENERIC && !defined(MPT_QUIRK_NO_CPP_THREAD) +#elif MPT_COMPILER_GENERIC #define MPT_MUTEX_STD 1 #define MPT_MUTEX_PTHREAD 0 #define MPT_MUTEX_WIN32 0 -#elif MPT_COMPILER_MSVC && !defined(MPT_QUIRK_NO_CPP_THREAD) +#elif (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && defined(MPT_WITH_MINGWSTDTHREADS) #define MPT_MUTEX_STD 1 #define MPT_MUTEX_PTHREAD 0 #define MPT_MUTEX_WIN32 0 -#elif MPT_COMPILER_GCC && !MPT_OS_WINDOWS && !defined(MPT_QUIRK_NO_CPP_THREAD) +#elif (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) +#define MPT_MUTEX_STD 0 +#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_WIN32 1 +#elif MPT_COMPILER_MSVC #define MPT_MUTEX_STD 1 #define MPT_MUTEX_PTHREAD 0 #define MPT_MUTEX_WIN32 0 -#elif MPT_COMPILER_CLANG && defined(__GLIBCXX__) && !defined(MPT_QUIRK_NO_CPP_THREAD) +#elif MPT_COMPILER_GCC #define MPT_MUTEX_STD 1 #define MPT_MUTEX_PTHREAD 0 #define MPT_MUTEX_WIN32 0 -#elif (MPT_OS_MACOSX_OR_IOS || MPT_OS_FREEBSD) && MPT_COMPILER_CLANG && !defined(MPT_QUIRK_NO_CPP_THREAD) -#define MPT_MUTEX_STD 1 -#define MPT_MUTEX_PTHREAD 0 -#define MPT_MUTEX_WIN32 0 -#elif MPT_CLANG_AT_LEAST(3,6,0) && defined(_LIBCPP_VERSION) && !defined(MPT_QUIRK_NO_CPP_THREAD) +#elif MPT_COMPILER_CLANG #define MPT_MUTEX_STD 1 #define MPT_MUTEX_PTHREAD 0 #define MPT_MUTEX_WIN32 0 @@ -49,7 +51,7 @@ #define MPT_MUTEX_WIN32 1 #else #define MPT_MUTEX_STD 0 -#define MPT_MUTEX_PTHREAD 0 +#define MPT_MUTEX_PTHREAD 1 #define MPT_MUTEX_WIN32 0 #endif @@ -64,7 +66,11 @@ #endif #if MPT_MUTEX_STD +#if (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && defined(MPT_WITH_MINGWSTDTHREADS) +#include +#else #include +#endif #elif MPT_MUTEX_WIN32 #include #elif MPT_MUTEX_PTHREAD diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp index 89c15f6d4..9d75ab755 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp @@ -28,28 +28,10 @@ namespace Windows #if MPT_OS_WINDOWS -#if !MPT_OS_WINDOWS_WINRT - -static uint32 VersionDecimalTo_WIN32_WINNT(uint32 major, uint32 minor) -{ - // GetVersionEx returns decimal. - // _WIN32_WINNT macro uses BCD for the minor byte (see Windows 98 / ME). - // We use what _WIN32_WINNT does. - uint32 result = 0; - minor = mpt::clamp(minor, 0, 99); - result |= major; - result <<= 8; - result |= minor/10*0x10 + minor%10; - return result; -} - -#endif // !MPT_OS_WINDOWS_WINRT - - -static void GatherWindowsVersion(uint32 & SystemVersion) +static mpt::Windows::Version VersionFromNTDDI_VERSION() noexcept { // Initialize to used SDK version - SystemVersion = + mpt::Windows::Version::System System = #if NTDDI_VERSION >= 0x0A000000 // NTDDI_WIN10 mpt::Windows::Version::Win10 #elif NTDDI_VERSION >= 0x06030000 // NTDDI_WINBLUE @@ -70,7 +52,25 @@ static void GatherWindowsVersion(uint32 & SystemVersion) mpt::Windows::Version::WinNT4 #endif ; -#if !MPT_OS_WINDOWS_WINRT + return mpt::Windows::Version(System, mpt::Windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0); +} + + +static mpt::Windows::Version::System SystemVersionFrom_WIN32_WINNT() noexcept +{ + #if defined(_WIN32_WINNT) + return mpt::Windows::Version::System((static_cast(_WIN32_WINNT) & 0xff00u) >> 8, (static_cast(_WIN32_WINNT) & 0x00ffu) >> 0); + #else + return mpt::Windows::Version::System(); + #endif +} + + +static mpt::Windows::Version GatherWindowsVersion() noexcept +{ +#if MPT_OS_WINDOWS_WINRT + return VersionFromNTDDI_VERSION(); +#else // !MPT_OS_WINDOWS_WINRT OSVERSIONINFOEXW versioninfoex; MemsetZero(versioninfoex); versioninfoex.dwOSVersionInfoSize = sizeof(versioninfoex); @@ -78,19 +78,30 @@ static void GatherWindowsVersion(uint32 & SystemVersion) #pragma warning(push) #pragma warning(disable:4996) // 'GetVersionExW': was declared deprecated #endif // MPT_COMPILER_MSVC -#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#if MPT_COMPILER_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif // MPT_COMPILER_CLANG - GetVersionExW((LPOSVERSIONINFOW)&versioninfoex); + if(GetVersionExW((LPOSVERSIONINFOW)&versioninfoex) == FALSE) + { + return VersionFromNTDDI_VERSION(); + } #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC -#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#if MPT_COMPILER_CLANG #pragma clang diagnostic pop #endif // MPT_COMPILER_CLANG - SystemVersion = VersionDecimalTo_WIN32_WINNT(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion); -#endif // !MPT_OS_WINDOWS_WINRT + if(versioninfoex.dwPlatformId != VER_PLATFORM_WIN32_NT) + { + return VersionFromNTDDI_VERSION(); + } + return mpt::Windows::Version( + mpt::Windows::Version::System(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion), + mpt::Windows::Version::ServicePack(versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor), + versioninfoex.dwBuildNumber + ); +#endif // MPT_OS_WINDOWS_WINRT } @@ -99,19 +110,18 @@ static void GatherWindowsVersion(uint32 & SystemVersion) namespace { struct WindowsVersionCache { - uint32 SystemVersion; - WindowsVersionCache() - : SystemVersion(mpt::Windows::Version::WinNT4) + mpt::Windows::Version version; + WindowsVersionCache() noexcept + : version(GatherWindowsVersion()) { - GatherWindowsVersion(SystemVersion); } }; } -static void GatherWindowsVersionFromCache(uint32 & SystemVersion) +static mpt::Windows::Version GatherWindowsVersionFromCache() noexcept { static WindowsVersionCache gs_WindowsVersionCache; - SystemVersion = gs_WindowsVersionCache.SystemVersion; + return gs_WindowsVersionCache.version; } #endif // MODPLUG_TRACKER @@ -120,80 +130,183 @@ static void GatherWindowsVersionFromCache(uint32 & SystemVersion) #endif // MPT_OS_WINDOWS -Version::Version() - : SystemIsWindows(false) - , SystemVersion(mpt::Windows::Version::WinNT4) +Version::Version() noexcept + : m_SystemIsWindows(false) + , m_System() + , m_ServicePack() + , m_Build() { - return; } -mpt::Windows::Version Version::Current() +Version Version::NoWindows() noexcept +{ + return Version(); +} + + +Version::Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build) noexcept + : m_SystemIsWindows(true) + , m_System(system) + , m_ServicePack(servicePack) + , m_Build(build) +{ +} + + +mpt::Windows::Version Version::Current() noexcept { - mpt::Windows::Version result; #if MPT_OS_WINDOWS - result.SystemIsWindows = true; #ifdef MODPLUG_TRACKER - GatherWindowsVersionFromCache(result.SystemVersion); + return GatherWindowsVersionFromCache(); #else // !MODPLUG_TRACKER - GatherWindowsVersion(result.SystemVersion); + return GatherWindowsVersion(); #endif // MODPLUG_TRACKER + #else // !MPT_OS_WINDOWS + return mpt::Windows::Version::NoWindows(); #endif // MPT_OS_WINDOWS - return result; } -bool Version::IsWindows() const +bool Version::IsWindows() const noexcept { - return SystemIsWindows; + return m_SystemIsWindows; } -bool Version::IsBefore(mpt::Windows::Version::Number version) const +bool Version::IsBefore(mpt::Windows::Version::System version) const noexcept { - if(!SystemIsWindows) + if(!m_SystemIsWindows) { return false; } - return (SystemVersion < static_cast(version)); + return m_System < version; } -bool Version::IsAtLeast(mpt::Windows::Version::Number version) const +bool Version::IsBefore(mpt::Windows::Version::System version, mpt::Windows::Version::ServicePack servicePack) const noexcept { - if(!SystemIsWindows) + if(!m_SystemIsWindows) { return false; } - return (SystemVersion >= static_cast(version)); + if(m_System > version) + { + return false; + } + if(m_System < version) + { + return true; + } + return m_ServicePack < servicePack; } -static MPT_CONSTEXPR11_VAR struct { Version::Number version; const MPT_UCHAR_TYPE * name; } versionMap[] = +bool Version::IsBefore(mpt::Windows::Version::System version, mpt::Windows::Version::Build build) const noexcept { - { mpt::Windows::Version::WinNewer, MPT_ULITERAL("Windows 10 (or newer)") }, - { mpt::Windows::Version::Win10, MPT_ULITERAL("Windows 10") }, - { mpt::Windows::Version::Win81, MPT_ULITERAL("Windows 8.1") }, - { mpt::Windows::Version::Win8, MPT_ULITERAL("Windows 8") }, - { mpt::Windows::Version::Win7, MPT_ULITERAL("Windows 7") }, - { mpt::Windows::Version::WinVista, MPT_ULITERAL("Windows Vista") }, - { mpt::Windows::Version::WinXP64, MPT_ULITERAL("Windows XP x64 / Windows Server 2003") }, - { mpt::Windows::Version::WinXP, MPT_ULITERAL("Windows XP") }, - { mpt::Windows::Version::Win2000, MPT_ULITERAL("Windows 2000") }, - { mpt::Windows::Version::WinME, MPT_ULITERAL("Windows ME") }, - { mpt::Windows::Version::Win98, MPT_ULITERAL("Windows 98") }, - { mpt::Windows::Version::WinNT4, MPT_ULITERAL("Windows NT4") } + if(!m_SystemIsWindows) + { + return false; + } + if(m_System > version) + { + return false; + } + if(m_System < version) + { + return true; + } + return m_Build < build; +} + + +bool Version::IsAtLeast(mpt::Windows::Version::System version) const noexcept +{ + if(!m_SystemIsWindows) + { + return false; + } + return m_System >= version; +} + + +bool Version::IsAtLeast(mpt::Windows::Version::System version, mpt::Windows::Version::ServicePack servicePack) const noexcept +{ + if(!m_SystemIsWindows) + { + return false; + } + if(m_System < version) + { + return false; + } + if(m_System > version) + { + return true; + } + return m_ServicePack >= servicePack; +} + + +bool Version::IsAtLeast(mpt::Windows::Version::System version, mpt::Windows::Version::Build build) const noexcept +{ + if(!m_SystemIsWindows) + { + return false; + } + if(m_System < version) + { + return false; + } + if(m_System > version) + { + return true; + } + return m_Build >= build; +} + + +mpt::Windows::Version::System Version::GetSystem() const noexcept +{ + return m_System; +} + + +mpt::Windows::Version::ServicePack Version::GetServicePack() const noexcept +{ + return m_ServicePack; +} + + +mpt::Windows::Version::Build Version::GetBuild() const noexcept +{ + return m_Build; +} + + +static MPT_CONSTEXPR11_VAR struct { Version::System version; const MPT_UCHAR_TYPE * name; bool showDetails; } versionMap[] = +{ + { mpt::Windows::Version::WinNewer, UL_("Windows 10 (or newer)"), false }, + { mpt::Windows::Version::Win10, UL_("Windows 10"), true }, + { mpt::Windows::Version::Win81, UL_("Windows 8.1"), true }, + { mpt::Windows::Version::Win8, UL_("Windows 8"), true }, + { mpt::Windows::Version::Win7, UL_("Windows 7"), true }, + { mpt::Windows::Version::WinVista, UL_("Windows Vista"), true }, + { mpt::Windows::Version::WinXP64, UL_("Windows XP x64 / Windows Server 2003"), true }, + { mpt::Windows::Version::WinXP, UL_("Windows XP"), true }, + { mpt::Windows::Version::Win2000, UL_("Windows 2000"), true }, + { mpt::Windows::Version::WinNT4, UL_("Windows NT4"), true } }; -mpt::ustring Version::VersionToString(uint16 version) +mpt::ustring Version::VersionToString(mpt::Windows::Version::System version) { mpt::ustring result; for(const auto &v : versionMap) { if(version > v.version) { - result = MPT_USTRING("> ") + v.name; + result = U_("> ") + v.name; break; } else if(version == v.version) { @@ -203,30 +316,45 @@ mpt::ustring Version::VersionToString(uint16 version) } if(result.empty()) { - result = mpt::format(MPT_USTRING("0x%1"))(mpt::ufmt::dec0<4>(version)); + result = mpt::format(U_("0x%1"))(mpt::ufmt::hex0<16>(static_cast(version))); } return result; } - -mpt::ustring Version::VersionToString(Number version) -{ - return VersionToString(static_cast(version)); -} - - mpt::ustring Version::GetName() const { - mpt::ustring name = MPT_USTRING("Generic Windows NT"); + mpt::ustring name = U_("Generic Windows NT"); + bool showDetails = false; for(const auto &v : versionMap) { - if(mpt::Windows::Version::IsAtLeast(v.version)) + if(IsAtLeast(v.version)) { name = v.name; + showDetails = v.showDetails; break; } } + name += U_(" ("); + name += mpt::format(U_("Version %1.%2"))(m_System.Major, m_System.Minor); + if(showDetails) + { + if(m_ServicePack.HasServicePack()) + { + if(m_ServicePack.Minor) + { + name += mpt::format(U_(" Service Pack %1.%2"))(m_ServicePack.Major, m_ServicePack.Minor); + } else + { + name += mpt::format(U_(" Service Pack %1"))(m_ServicePack.Major); + } + } + if(m_Build != 0) + { + name += mpt::format(U_(" (Build %1)"))(m_Build); + } + } + name += U_(")"); mpt::ustring result = name; #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS if(mpt::Windows::IsWine()) @@ -234,13 +362,13 @@ mpt::ustring Version::GetName() const mpt::Wine::VersionContext v; if(v.Version().IsValid()) { - result = mpt::format(MPT_USTRING("Wine %1 (%2)"))( + result = mpt::format(U_("Wine %1 (%2)"))( v.Version().AsString() , name ); } else { - result = mpt::format(MPT_USTRING("Wine (unknown version: '%1') (%2)"))( + result = mpt::format(U_("Wine (unknown version: '%1') (%2)"))( mpt::ToUnicode(mpt::CharsetUTF8, v.RawVersion()) , name ); @@ -260,48 +388,237 @@ mpt::ustring Version::GetNameShort() const mpt::Wine::VersionContext v; if(v.Version().IsValid()) { - name = mpt::format(MPT_USTRING("wine-%1"))(v.Version().AsString()); + name = mpt::format(U_("wine-%1"))(v.Version().AsString()); } else if(v.RawVersion().length() > 0) { - name = MPT_USTRING("wine-") + Util::BinToHex(mpt::as_span(v.RawVersion())); + name = U_("wine-") + Util::BinToHex(mpt::as_span(v.RawVersion())); } else { - name = MPT_USTRING("wine-"); + name = U_("wine-"); } - name += MPT_USTRING("-") + Util::BinToHex(mpt::as_span(v.RawHostSysName())); + name += U_("-") + Util::BinToHex(mpt::as_span(v.RawHostSysName())); } else { - name = mpt::format(MPT_USTRING("%1.%2"))(mpt::ufmt::dec(SystemVersion >> 8), mpt::ufmt::HEX0<2>(SystemVersion & 0xFF)); + name = mpt::format(U_("%1.%2"))(mpt::ufmt::dec(m_System.Major), mpt::ufmt::dec0<2>(m_System.Minor)); } return name; } #endif // MODPLUG_TRACKER -mpt::Windows::Version::Number Version::GetMinimumKernelLevel() +mpt::Windows::Version::System Version::GetMinimumKernelLevel() noexcept { - uint16 minimumKernelVersion = 0; + uint64 minimumKernelVersion = 0; #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC #if !defined(MPT_BUILD_TARGET_XP) - minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinVista); + minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinVista); #else - minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinXP); + minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinXP); #endif #endif - return static_cast(minimumKernelVersion); + return mpt::Windows::Version::System(minimumKernelVersion); } -mpt::Windows::Version::Number Version::GetMinimumAPILevel() +mpt::Windows::Version::System Version::GetMinimumAPILevel() noexcept { - uint16 minimumApiVersion = 0; - #if MPT_OS_WINDOWS && defined(_WIN32_WINNT) - minimumApiVersion = std::max(minimumApiVersion, _WIN32_WINNT); - #endif - return static_cast(minimumApiVersion); + #if MPT_OS_WINDOWS + return SystemVersionFrom_WIN32_WINNT(); + #else // !MPT_OS_WINDOWS + return mpt::Windows::Version::System(); + #endif // MPT_OS_WINDOWS } +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + +#ifndef PROCESSOR_ARCHITECTURE_NEUTRAL +#define PROCESSOR_ARCHITECTURE_NEUTRAL 11 +#endif +#ifndef PROCESSOR_ARCHITECTURE_ARM64 +#define PROCESSOR_ARCHITECTURE_ARM64 12 +#endif +#ifndef PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 +#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13 +#endif +#ifndef PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 +#define PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 14 +#endif + + +struct OSArchitecture +{ + uint16 ProcessorArchitectur; + Architecture Host; + Architecture Process; +}; +static constexpr OSArchitecture architectures [] = { + { PROCESSOR_ARCHITECTURE_INTEL , Architecture::x86 , Architecture::x86 }, + { PROCESSOR_ARCHITECTURE_AMD64 , Architecture::amd64 , Architecture::amd64 }, + { PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 , Architecture::amd64 , Architecture::x86 }, + { PROCESSOR_ARCHITECTURE_ARM , Architecture::arm , Architecture::arm }, + { PROCESSOR_ARCHITECTURE_ARM64 , Architecture::arm64 , Architecture::arm64 }, + { PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64, Architecture::arm64 , Architecture::arm }, + { PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 , Architecture::arm64 , Architecture::x86 }, + { PROCESSOR_ARCHITECTURE_MIPS , Architecture::mips , Architecture::mips }, + { PROCESSOR_ARCHITECTURE_PPC , Architecture::ppc , Architecture::ppc }, + { PROCESSOR_ARCHITECTURE_SHX , Architecture::shx , Architecture::shx }, + { PROCESSOR_ARCHITECTURE_ALPHA , Architecture::alpha , Architecture::alpha }, + { PROCESSOR_ARCHITECTURE_ALPHA64 , Architecture::alpha64, Architecture::alpha64 }, + { PROCESSOR_ARCHITECTURE_IA64 , Architecture::ia64 , Architecture::ia64 }, + { PROCESSOR_ARCHITECTURE_MSIL , Architecture::unknown, Architecture::unknown }, + { PROCESSOR_ARCHITECTURE_NEUTRAL , Architecture::unknown, Architecture::unknown }, + { PROCESSOR_ARCHITECTURE_UNKNOWN , Architecture::unknown, Architecture::unknown } +}; + + +struct HostArchitecture +{ + Architecture Host; + Architecture Process; + EmulationLevel Emulation; +}; +static constexpr HostArchitecture hostArchitectureCanRun [] = { + { Architecture::x86 , Architecture::x86 , EmulationLevel::Native }, + { Architecture::amd64 , Architecture::amd64 , EmulationLevel::Native }, + { Architecture::amd64 , Architecture::x86 , EmulationLevel::Virtual }, + { Architecture::arm , Architecture::arm , EmulationLevel::Native }, + { Architecture::arm64 , Architecture::arm64 , EmulationLevel::Native }, + { Architecture::arm64 , Architecture::arm , EmulationLevel::Virtual }, + { Architecture::arm64 , Architecture::x86 , EmulationLevel::Software }, + { Architecture::mips , Architecture::mips , EmulationLevel::Native }, + { Architecture::ppc , Architecture::ppc , EmulationLevel::Native }, + { Architecture::shx , Architecture::shx , EmulationLevel::Native }, + { Architecture::alpha , Architecture::alpha , EmulationLevel::Native }, + { Architecture::alpha64, Architecture::alpha64, EmulationLevel::Native }, + { Architecture::alpha64, Architecture::alpha , EmulationLevel::Virtual }, + { Architecture::ia64 , Architecture::ia64 , EmulationLevel::Native }, + { Architecture::ia64 , Architecture::x86 , EmulationLevel::Hardware } +}; + + +struct ArchitectureInfo +{ + Architecture Arch; + int Bitness; + const MPT_UCHAR_TYPE * Name; +}; +static constexpr ArchitectureInfo architectureInfo [] = { + { Architecture::x86 , 32, UL_("x86") }, + { Architecture::amd64 , 64, UL_("amd64") }, + { Architecture::arm , 32, UL_("arm") }, + { Architecture::arm64 , 64, UL_("arm64") }, + { Architecture::mips , 32, UL_("mips") }, + { Architecture::ppc , 32, UL_("ppc") }, + { Architecture::shx , 32, UL_("shx") }, + { Architecture::alpha , 32, UL_("alpha") }, + { Architecture::alpha64, 64, UL_("alpha64") }, + { Architecture::ia64 , 64, UL_("ia64") } +}; + + +int Bitness(Architecture arch) noexcept +{ + for(const auto &info : architectureInfo) + { + if(arch == info.Arch) + { + return info.Bitness; + } + } + return 0; +} + + +mpt::ustring Name(Architecture arch) +{ + for(const auto &info : architectureInfo) + { + if(arch == info.Arch) + { + return info.Name; + } + } + return mpt::ustring(); +} + + +Architecture GetHostArchitecture() noexcept +{ + SYSTEM_INFO systemInfo; + MemsetZero(systemInfo); + GetNativeSystemInfo(&systemInfo); + for(const auto &arch : architectures) + { + if(systemInfo.wProcessorArchitecture == arch.ProcessorArchitectur) + { + return arch.Host; + } + } + return Architecture::unknown; +} + + +Architecture GetProcessArchitecture() noexcept +{ + SYSTEM_INFO systemInfo; + MemsetZero(systemInfo); + GetSystemInfo(&systemInfo); + for(const auto &arch : architectures) + { + if(systemInfo.wProcessorArchitecture == arch.ProcessorArchitectur) + { + return arch.Process; + } + } + return Architecture::unknown; +} + + +EmulationLevel HostCanRun(Architecture host, Architecture process) noexcept +{ + for(const auto & can : hostArchitectureCanRun) + { + if(can.Host == host && can.Process == process) + { + return can.Emulation; + } + } + return EmulationLevel::NA; +} + + +std::vector GetSupportedProcessArchitectures(Architecture host) +{ + std::vector result; + for(const auto & entry : hostArchitectureCanRun) + { + if(entry.Host == host) + { + result.push_back(entry.Process); + } + } + return result; +} + + +uint64 GetSystemMemorySize() +{ + MEMORYSTATUSEX memoryStatus; + MemsetZero(memoryStatus); + memoryStatus.dwLength = sizeof(MEMORYSTATUSEX); + if(GlobalMemoryStatusEx(&memoryStatus) == 0) + { + return 0; + } + return memoryStatus.ullTotalPhys; +} + + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + #if defined(MODPLUG_TRACKER) @@ -310,7 +627,7 @@ mpt::Windows::Version::Number Version::GetMinimumAPILevel() static bool GatherSystemIsWine() { bool SystemIsWine = false; - HMODULE hNTDLL = LoadLibraryW(L"ntdll.dll"); + HMODULE hNTDLL = LoadLibrary(TEXT("ntdll.dll")); if(hNTDLL) { SystemIsWine = (GetProcAddress(hNTDLL, "wine_get_version") != NULL); @@ -320,10 +637,6 @@ static bool GatherSystemIsWine() return SystemIsWine; } -#endif // MPT_OS_WINDOWS - -#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS - namespace { struct SystemIsWineCache { @@ -341,20 +654,17 @@ struct SystemIsWineCache }; } -#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS +#endif // MPT_OS_WINDOWS static bool SystemIsWine(bool allowDetection = true) { - #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + #if MPT_OS_WINDOWS static SystemIsWineCache gs_SystemIsWineCache = allowDetection ? SystemIsWineCache() : SystemIsWineCache(false); if(!allowDetection) { // catch too late calls of PreventWineDetection MPT_ASSERT(!gs_SystemIsWineCache.SystemIsWine); } return gs_SystemIsWineCache.SystemIsWine; - #elif MPT_OS_WINDOWS - MPT_UNREFERENCED_PARAMETER(allowDetection); - return GatherSystemIsWine(); #else MPT_UNREFERENCED_PARAMETER(allowDetection); return false; @@ -413,12 +723,12 @@ Version::Version(const mpt::ustring &rawVersion) { return; } - std::vector version = mpt::String::Split(rawVersion, MPT_USTRING(".")); + std::vector version = mpt::String::Split(rawVersion, U_(".")); if(version.size() < 2) { return; } - mpt::ustring parsedVersion = mpt::String::Combine(version, MPT_USTRING(".")); + mpt::ustring parsedVersion = mpt::String::Combine(version, U_(".")); std::size_t len = std::min(parsedVersion.length(), rawVersion.length()); if(len == 0) { @@ -464,7 +774,7 @@ bool Version::IsValid() const mpt::ustring Version::AsString() const { - return mpt::ufmt::dec(vmajor) + MPT_USTRING(".") + mpt::ufmt::dec(vminor) + MPT_USTRING(".") + mpt::ufmt::dec(vupdate); + return mpt::ufmt::dec(vmajor) + U_(".") + mpt::ufmt::dec(vminor) + U_(".") + mpt::ufmt::dec(vupdate); } @@ -498,15 +808,27 @@ bool Version::IsAtLeast(mpt::Wine::Version other) const } +uint8 Version::GetMajor() const +{ + return vmajor; +} + +uint8 Version::GetMinor() const +{ + return vminor; +} + +uint8 Version::GetUpdate() const +{ + return vupdate; +} + + mpt::Wine::Version GetMinimumWineVersion() { mpt::Wine::Version minimumWineVersion = mpt::Wine::Version(0,0,0); #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC - #if !defined(MPT_BUILD_TARGET_XP) - minimumWineVersion = mpt::Wine::Version(1,8,0); - #else - minimumWineVersion = mpt::Wine::Version(1,6,0); - #endif + minimumWineVersion = mpt::Wine::Version(1,8,0); #endif return minimumWineVersion; } @@ -523,7 +845,7 @@ VersionContext::VersionContext() { return; } - m_NTDLL = mpt::Library(mpt::LibraryPath::FullPath(MPT_PATHSTRING("ntdll.dll"))); + m_NTDLL = mpt::Library(mpt::LibraryPath::FullPath(P_("ntdll.dll"))); if(m_NTDLL.IsValid()) { const char * (__cdecl * wine_get_version)(void) = nullptr; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h index fe213d10e..851ca9699 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "mptLibrary.h" @@ -22,53 +24,113 @@ namespace mpt namespace Windows { + class Version { public: - enum Number + enum Number : uint64 { - - WinNT4 = 0x0400, - Win98 = 0x0410, - WinME = 0x0490, - Win2000 = 0x0500, - WinXP = 0x0501, - WinXP64 = 0x0502, - WinVista = 0x0600, - Win7 = 0x0601, - Win8 = 0x0602, - Win81 = 0x0603, - Win10 = 0x0a00, - - WinNewer = Win10 + 1 - + WinNT4 = 0x0000000400000000ull, + Win2000 = 0x0000000500000000ull, + WinXP = 0x0000000500000001ull, + WinXP64 = 0x0000000500000002ull, + WinVista = 0x0000000600000000ull, + Win7 = 0x0000000600000001ull, + Win8 = 0x0000000600000002ull, + Win81 = 0x0000000600000003ull, + Win10 = 0x0000000a00000000ull, + WinNewer = Win10 + 1ull }; - static mpt::ustring VersionToString(uint16 version); - static mpt::ustring VersionToString(Number version); + struct System + { + uint32 Major = 0; + uint32 Minor = 0; + System() = default; + constexpr System(Number number) noexcept + : Major(static_cast((static_cast(number) >> 32) & 0xffffffffu)) + , Minor(static_cast((static_cast(number) >> 0) & 0xffffffffu)) + { + } + explicit constexpr System(uint64 number) noexcept + : Major(static_cast((number >> 32) & 0xffffffffu)) + , Minor(static_cast((number >> 0) & 0xffffffffu)) + { + } + explicit constexpr System(uint32 major, uint32 minor) noexcept + : Major(major) + , Minor(minor) + { + } + constexpr operator uint64 () const noexcept + { + return (static_cast(Major) << 32) | (static_cast(Minor) << 0); + } + }; + + struct ServicePack + { + uint16 Major = 0; + uint16 Minor = 0; + ServicePack() = default; + explicit constexpr ServicePack(uint16 major, uint16 minor) noexcept + : Major(major) + , Minor(minor) + { + } + constexpr bool HasServicePack() const noexcept + { + return Major != 0 || Minor != 0; + } + constexpr operator uint32 () const noexcept + { + return (static_cast(Major) << 16) | (static_cast(Minor) << 0); + } + }; + + typedef uint32 Build; + + static mpt::ustring VersionToString(mpt::Windows::Version::System version); private: - bool SystemIsWindows; + bool m_SystemIsWindows; - uint32 SystemVersion; + System m_System; + ServicePack m_ServicePack; + Build m_Build; private: - Version(); + Version() noexcept; public: - static mpt::Windows::Version Current(); + static Version NoWindows() noexcept; + + Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build) noexcept; public: - bool IsWindows() const; + static mpt::Windows::Version Current() noexcept; - bool IsBefore(mpt::Windows::Version::Number version) const; - bool IsAtLeast(mpt::Windows::Version::Number version) const; +public: + + bool IsWindows() const noexcept; + + bool IsBefore(mpt::Windows::Version::System version) const noexcept; + bool IsBefore(mpt::Windows::Version::System version, mpt::Windows::Version::ServicePack servicePack) const noexcept; + bool IsBefore(mpt::Windows::Version::System version, mpt::Windows::Version::Build build) const noexcept; + + bool IsAtLeast(mpt::Windows::Version::System version) const noexcept; + bool IsAtLeast(mpt::Windows::Version::System version, mpt::Windows::Version::ServicePack servicePack) const noexcept; + bool IsAtLeast(mpt::Windows::Version::System version, mpt::Windows::Version::Build build) const noexcept; + + mpt::Windows::Version::System GetSystem() const noexcept; + mpt::Windows::Version::ServicePack GetServicePack() const noexcept; + mpt::Windows::Version::Build GetBuild() const noexcept; mpt::ustring GetName() const; #ifdef MODPLUG_TRACKER @@ -77,11 +139,57 @@ public: public: - static mpt::Windows::Version::Number GetMinimumKernelLevel(); - static mpt::Windows::Version::Number GetMinimumAPILevel(); + static mpt::Windows::Version::System GetMinimumKernelLevel() noexcept; + static mpt::Windows::Version::System GetMinimumAPILevel() noexcept; }; // class Version +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +enum class Architecture +{ + unknown = -1, + + x86 = 0x0401, + amd64 = 0x0801, + arm = 0x0402, + arm64 = 0x0802, + + mips = 0x0403, + ppc = 0x0404, + shx = 0x0405, + + alpha = 0x0406, + alpha64 = 0x0806, + + ia64 = 0x0807, +}; + +enum class EmulationLevel +{ + Native, + Virtual, + Hardware, + Software, + NA, +}; + +int Bitness(Architecture arch) noexcept; + +mpt::ustring Name(Architecture arch); + +Architecture GetHostArchitecture() noexcept; +Architecture GetProcessArchitecture() noexcept; + +EmulationLevel HostCanRun(Architecture host, Architecture process) noexcept; + +std::vector GetSupportedProcessArchitectures(Architecture host); + +uint64 GetSystemMemorySize(); + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + #if defined(MODPLUG_TRACKER) void PreventWineDetection(); @@ -123,6 +231,9 @@ private: public: bool IsBefore(mpt::Wine::Version other) const; bool IsAtLeast(mpt::Wine::Version other) const; + uint8 GetMajor() const; + uint8 GetMinor() const; + uint8 GetUpdate() const; }; mpt::Wine::Version GetMinimumWineVersion(); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp index 76d1b1a0f..541eb1944 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp @@ -19,6 +19,7 @@ #if defined(MODPLUG_TRACKER) #include #endif +#include #endif #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT @@ -26,18 +27,15 @@ // MinGW-w64 headers do not declare this for WinRT, which is wrong. extern "C" { WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart); +#ifndef GetFullPathName +#define GetFullPathName GetFullPathNameW +#endif } #endif #endif OPENMPT_NAMESPACE_BEGIN -#if MPT_OS_WINDOWS -#define MPT_PATHSTRING_LITERAL(x) ( L ## x ) -#else -#define MPT_PATHSTRING_LITERAL(x) ( x ) -#endif - #if MPT_OS_WINDOWS namespace mpt @@ -46,20 +44,20 @@ namespace mpt RawPathString PathString::AsNativePrefixed() const { - if(path.length() <= MAX_PATH || path.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\")) + if(path.length() <= MAX_PATH || path.substr(0, 4) == PL_("\\\\?\\")) { // Path is short enough or already in prefixed form return path; } - const RawPathString absPath = mpt::GetAbsolutePath(path).AsNative(); - if(absPath.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\")) + const RawPathString absPath = mpt::GetAbsolutePath(*this).AsNative(); + if(absPath.substr(0, 2) == PL_("\\\\")) { // Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar - return MPT_PATHSTRING_LITERAL("\\\\?\\UNC") + absPath.substr(1); + return PL_("\\\\?\\UNC") + absPath.substr(1); } else { // Regular file: C:\foo.bar -> \\?\C:\foo.bar - return MPT_PATHSTRING_LITERAL("\\\\?\\") + absPath; + return PL_("\\\\?\\") + absPath; } } @@ -68,7 +66,7 @@ RawPathString PathString::AsNativePrefixed() const int PathString::CompareNoCase(const PathString & a, const PathString & b) { - return lstrcmpiW(a.path.c_str(), b.path.c_str()); + return lstrcmpi(a.path.c_str(), b.path.c_str()); } #endif // !MPT_OS_WINDOWS_WINRT @@ -86,42 +84,42 @@ PathString PathString::Simplify() const std::vector components; RawPathString root; RawPathString::size_type startPos = 0; - if(path.size() >= 2 && path[1] == MPT_PATHSTRING_LITERAL(':')) + if(path.size() >= 2 && path[1] == PC_(':')) { // Drive letter - root = path.substr(0, 2) + MPT_PATHSTRING_LITERAL('\\'); + root = path.substr(0, 2) + PC_('\\'); startPos = 2; - } else if(path.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\")) + } else if(path.substr(0, 2) == PL_("\\\\")) { // Network share - root = MPT_PATHSTRING_LITERAL("\\\\"); + root = PL_("\\\\"); startPos = 2; - } else if(path.substr(0, 2) == MPT_PATHSTRING_LITERAL(".\\") || path.substr(0, 2) == MPT_PATHSTRING_LITERAL("./")) + } else if(path.substr(0, 2) == PL_(".\\") || path.substr(0, 2) == PL_("./")) { // Special case for relative paths - root = MPT_PATHSTRING_LITERAL(".\\"); + root = PL_(".\\"); startPos = 2; - } else if(path.size() >= 1 && (path[0] == MPT_PATHSTRING_LITERAL('\\') || path[0] == MPT_PATHSTRING_LITERAL('/'))) + } else if(path.size() >= 1 && (path[0] == PC_('\\') || path[0] == PC_('/'))) { // Special case for relative paths - root = MPT_PATHSTRING_LITERAL("\\"); + root = PL_("\\"); startPos = 1; } while(startPos < path.size()) { - auto pos = path.find_first_of(MPT_PATHSTRING_LITERAL("\\/"), startPos); + auto pos = path.find_first_of(PL_("\\/"), startPos); if(pos == RawPathString::npos) pos = path.size(); mpt::RawPathString dir = path.substr(startPos, pos - startPos); - if(dir == MPT_PATHSTRING_LITERAL("..")) + if(dir == PL_("..")) { // Go back one directory if(!components.empty()) { components.pop_back(); } - } else if(dir == MPT_PATHSTRING_LITERAL(".")) + } else if(dir == PL_(".")) { // nop } else if(!dir.empty()) @@ -135,11 +133,11 @@ PathString PathString::Simplify() const result.reserve(path.size()); for(const auto &component : components) { - result += component + MPT_PATHSTRING_LITERAL("\\"); + result += component + PL_("\\"); } if(!components.empty()) result.pop_back(); - return result; + return mpt::PathString(result); } } // namespace mpt @@ -168,25 +166,25 @@ void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname mpt::RawPathString p = path; // remove \\?\\ prefix - if(p.substr(0, 8) == MPT_PATHSTRING_LITERAL("\\\\?\\UNC\\")) + if(p.substr(0, 8) == PL_("\\\\?\\UNC\\")) { - p = MPT_PATHSTRING_LITERAL("\\\\") + p.substr(8); - } else if(p.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\")) + p = PL_("\\\\") + p.substr(8); + } else if(p.substr(0, 4) == PL_("\\\\?\\")) { p = p.substr(4); } if (p.length() >= 2 && ( - p.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\") - || p.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\/") - || p.substr(0, 2) == MPT_PATHSTRING_LITERAL("/\\") - || p.substr(0, 2) == MPT_PATHSTRING_LITERAL("//") + p.substr(0, 2) == PL_("\\\\") + || p.substr(0, 2) == PL_("\\/") + || p.substr(0, 2) == PL_("/\\") + || p.substr(0, 2) == PL_("//") )) { // UNC - mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(MPT_PATHSTRING_LITERAL("\\/")); + mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(PL_("\\/")); if(first_slash != mpt::RawPathString::npos) { - mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(MPT_PATHSTRING_LITERAL("\\/")); + mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(PL_("\\/")); if(second_slash != mpt::RawPathString::npos) { if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2 + first_slash + 1 + second_slash)); @@ -203,7 +201,7 @@ void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname } } else { // local - if(p.length() >= 2 && (p[1] == MPT_PATHSTRING_LITERAL(':'))) + if(p.length() >= 2 && (p[1] == PC_(':'))) { if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2)); p = p.substr(2); @@ -212,7 +210,7 @@ void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname if(drive) *drive = mpt::PathString(); } } - mpt::RawPathString::size_type last_slash = p.find_last_of(MPT_PATHSTRING_LITERAL("\\/")); + mpt::RawPathString::size_type last_slash = p.find_last_of(PL_("\\/")); if(last_slash != mpt::RawPathString::npos) { if(dir) *dir = mpt::PathString::FromNative(p.substr(0, last_slash + 1)); @@ -221,7 +219,7 @@ void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname { if(dir) *dir = mpt::PathString(); } - mpt::RawPathString::size_type last_dot = p.find_last_of(MPT_PATHSTRING_LITERAL(".")); + mpt::RawPathString::size_type last_dot = p.find_last_of(PL_(".")); if(last_dot == mpt::RawPathString::npos) { if(fname) *fname = mpt::PathString::FromNative(p); @@ -285,7 +283,7 @@ bool PathString::IsDirectory() const } DWORD dwAttrib = data.dwFileAttributes; #else // !MPT_OS_WINDOWS_WINRT - DWORD dwAttrib = ::GetFileAttributesW(path.c_str()); + DWORD dwAttrib = ::GetFileAttributes(path.c_str()); #endif // MPT_OS_WINDOWS_WINRT return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } @@ -301,7 +299,7 @@ bool PathString::IsFile() const } DWORD dwAttrib = data.dwFileAttributes; #else // !MPT_OS_WINDOWS_WINRT - DWORD dwAttrib = ::GetFileAttributesW(path.c_str()); + DWORD dwAttrib = ::GetFileAttributes(path.c_str()); #endif // MPT_OS_WINDOWS_WINRT return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } @@ -313,7 +311,7 @@ bool PathString::IsFile() const bool PathString::FileOrDirectoryExists() const { - return ::PathFileExistsW(path.c_str()) != FALSE; + return ::PathFileExists(path.c_str()) != FALSE; } #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS @@ -338,17 +336,17 @@ PathString PathString::SanitizeComponent() const // Convert an absolute path to a path that's relative to "&relativeTo". PathString PathString::AbsolutePathToRelative(const PathString &relativeTo) const { - mpt::PathString result = path; + mpt::PathString result = *this; if(path.empty()) { return result; } - if(!_wcsnicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length())) + if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length())) { // Path is OpenMPT's directory or a sub directory ("C:\OpenMPT\Somepath" => ".\Somepath") - result = MPT_PATHSTRING(".\\"); // ".\" + result = P_(".\\"); // ".\" result += mpt::PathString::FromNative(AsNative().substr(relativeTo.AsNative().length())); - } else if(!_wcsnicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), 2)) + } else if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), 2)) { // Path is on the same drive as OpenMPT ("C:\Somepath" => "\Somepath") result = mpt::PathString::FromNative(AsNative().substr(2)); @@ -360,17 +358,17 @@ PathString PathString::AbsolutePathToRelative(const PathString &relativeTo) cons // Convert a path that is relative to "&relativeTo" to an absolute path. PathString PathString::RelativePathToAbsolute(const PathString &relativeTo) const { - mpt::PathString result = path; + mpt::PathString result = *this; if(path.empty()) { return result; } - if(path.length() >= 2 && path.at(0) == MPT_PATHSTRING_LITERAL('\\') && path.at(1) != MPT_PATHSTRING_LITERAL('\\')) + if(path.length() >= 2 && path.at(0) == PC_('\\') && path.at(1) != PC_('\\')) { // Path is on the same drive as OpenMPT ("\Somepath\" => "C:\Somepath\"), but ignore network paths starting with "\\" result = mpt::PathString::FromNative(relativeTo.AsNative().substr(0, 2)); - result += path; - } else if(path.length() >= 2 && path.substr(0, 2) == MPT_PATHSTRING_LITERAL(".\\")) + result += mpt::PathString(path); + } else if(path.length() >= 2 && path.substr(0, 2) == PL_(".\\")) { // Path is OpenMPT's directory or a sub directory (".\Somepath\" => "C:\OpenMPT\Somepath\") result = relativeTo; // "C:\OpenMPT\" @@ -380,81 +378,56 @@ PathString PathString::RelativePathToAbsolute(const PathString &relativeTo) cons } -#if defined(_MFC_VER) - -mpt::PathString PathString::TunnelOutofCString(const CString &path) -{ - #ifdef UNICODE - return mpt::PathString::FromWide(path.GetString()); - #else - // Since MFC code can call into our code from a lot of places, we cannot assume - // that filenames we get from MFC are always encoded in our hacked UTF8-in-CString encoding. - // Instead, we use a rough heuristic: if the string is parseable as UTF8, we assume it is. - // This fails for CP_ACP strings, that are also valid UTF8. That's the trade-off here. - if(mpt::IsUTF8(path.GetString())) - { - // utf8 - return mpt::PathString::FromUTF8(path.GetString()); - } else - { - // ANSI - return mpt::PathString::FromWide(mpt::ToWide(path)); - } - #endif -} - - -CString PathString::TunnelIntoCString(const mpt::PathString &path) -{ - #ifdef UNICODE - return path.ToWide().c_str(); - #else - return path.ToUTF8().c_str(); - #endif -} - -#endif // MFC - #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS -} // namespace mpt +bool PathString::IsPathSeparator(RawPathString::value_type c) +{ +#if MPT_OS_WINDOWS + return (c == PC_('\\')) || (c == PC_('/')); +#else + return c == PC_('/'); +#endif +} + +RawPathString::value_type PathString::GetDefaultPathSeparator() +{ +#if MPT_OS_WINDOWS + return PC_('\\'); +#else + return PC_('/'); +#endif +} + + +} // namespace mpt namespace mpt { - -bool IsPathSeparator(mpt::RawPathString::value_type c) { -#if MPT_OS_WINDOWS - return (c == MPT_PATHSTRING_LITERAL('\\')) || (c == MPT_PATHSTRING_LITERAL('/')); -#else - return c == MPT_PATHSTRING_LITERAL('/'); -#endif -} - bool PathIsAbsolute(const mpt::PathString &path) { mpt::RawPathString rawpath = path.AsNative(); #if MPT_OS_WINDOWS - if(rawpath.substr(0, 8) == MPT_PATHSTRING_LITERAL("\\\\?\\UNC\\")) + if(rawpath.substr(0, 8) == PL_("\\\\?\\UNC\\")) { return true; } - if(rawpath.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\")) + if(rawpath.substr(0, 4) == PL_("\\\\?\\")) { return true; } - if(rawpath.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\")) + if(rawpath.substr(0, 2) == PL_("\\\\")) { return true; // UNC } - if(rawpath.substr(0, 2) == MPT_PATHSTRING_LITERAL("//")) + if(rawpath.substr(0, 2) == PL_("//")) { return true; // UNC } - return (rawpath.length()) >= 3 && (rawpath[1] == ':') && IsPathSeparator(rawpath[2]); + return (rawpath.length()) >= 3 && (rawpath[1] == ':') && mpt::PathString::IsPathSeparator(rawpath[2]); #else - return (rawpath.length() >= 1) && IsPathSeparator(rawpath[0]); + return (rawpath.length() >= 1) && mpt::PathString::IsPathSeparator(rawpath[0]); #endif } @@ -463,13 +436,13 @@ bool PathIsAbsolute(const mpt::PathString &path) { mpt::PathString GetAbsolutePath(const mpt::PathString &path) { - DWORD size = GetFullPathNameW(path.AsNative().c_str(), 0, nullptr, nullptr); + DWORD size = GetFullPathName(path.AsNative().c_str(), 0, nullptr, nullptr); if(size == 0) { return path; } - std::vector fullPathName(size, L'\0'); - if(GetFullPathNameW(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0) + std::vector fullPathName(size, TEXT('\0')); + if(GetFullPathName(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0) { return path; } @@ -484,7 +457,7 @@ bool DeleteWholeDirectoryTree(mpt::PathString path) { return false; } - if(PathIsRelativeW(path.AsNative().c_str()) == TRUE) + if(PathIsRelative(path.AsNative().c_str()) == TRUE) { return false; } @@ -498,15 +471,15 @@ bool DeleteWholeDirectoryTree(mpt::PathString path) } path.EnsureTrailingSlash(); HANDLE hFind = NULL; - WIN32_FIND_DATAW wfd; + WIN32_FIND_DATA wfd; MemsetZero(wfd); - hFind = FindFirstFileW((path + MPT_PATHSTRING("*.*")).AsNative().c_str(), &wfd); + hFind = FindFirstFile((path + P_("*.*")).AsNative().c_str(), &wfd); if(hFind != NULL && hFind != INVALID_HANDLE_VALUE) { do { mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName); - if(filename != MPT_PATHSTRING(".") && filename != MPT_PATHSTRING("..")) + if(filename != P_(".") && filename != P_("..")) { filename = path + filename; if(filename.IsDirectory()) @@ -517,16 +490,16 @@ bool DeleteWholeDirectoryTree(mpt::PathString path) } } else if(filename.IsFile()) { - if(DeleteFileW(filename.AsNative().c_str()) == 0) + if(DeleteFile(filename.AsNative().c_str()) == 0) { return false; } } } - } while(FindNextFileW(hFind, &wfd)); + } while(FindNextFile(hFind, &wfd)); FindClose(hFind); } - if(RemoveDirectoryW(path.AsNative().c_str()) == 0) + if(RemoveDirectory(path.AsNative().c_str()) == 0) { return false; } @@ -545,8 +518,8 @@ bool DeleteWholeDirectoryTree(mpt::PathString path) mpt::PathString GetAppPath() { - std::vector exeFileName(MAX_PATH); - while(GetModuleFileNameW(0, exeFileName.data(), mpt::saturate_cast(exeFileName.size())) >= exeFileName.size()) + std::vector exeFileName(MAX_PATH); + while(GetModuleFileName(0, exeFileName.data(), mpt::saturate_cast(exeFileName.size())) >= exeFileName.size()) { if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { @@ -566,13 +539,13 @@ mpt::PathString GetAppPath() mpt::PathString GetSystemPath() { - DWORD size = GetSystemDirectoryW(nullptr, 0); - std::vector path(size + 1); - if(!GetSystemDirectoryW(path.data(), size + 1)) + DWORD size = GetSystemDirectory(nullptr, 0); + std::vector path(size + 1); + if(!GetSystemDirectory(path.data(), size + 1)) { return mpt::PathString(); } - return mpt::PathString::FromNative(path.data()) + MPT_PATHSTRING("\\"); + return mpt::PathString::FromNative(path.data()) + P_("\\"); } #endif // !MPT_OS_WINDOWS_WINRT @@ -588,11 +561,11 @@ mpt::PathString GetSystemPath() mpt::PathString GetTempDirectory() { - DWORD size = GetTempPathW(0, nullptr); + DWORD size = GetTempPath(0, nullptr); if(size) { - std::vector tempPath(size + 1); - if(GetTempPathW(size + 1, tempPath.data())) + std::vector tempPath(size + 1); + if(GetTempPath(size + 1, tempPath.data())) { return mpt::PathString::FromNative(tempPath.data()); } @@ -604,9 +577,9 @@ mpt::PathString GetTempDirectory() mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension) { mpt::PathString filename = mpt::GetTempDirectory(); - filename += (!fileNamePrefix.empty() ? fileNamePrefix + MPT_PATHSTRING("_") : mpt::PathString()); + filename += (!fileNamePrefix.empty() ? fileNamePrefix + P_("_") : mpt::PathString()); filename += mpt::PathString::FromUnicode(mpt::UUID::GenerateLocalUseOnly().ToUString()); - filename += (!fileNameExtension.empty() ? MPT_PATHSTRING(".") + fileNameExtension : mpt::PathString()); + filename += (!fileNameExtension.empty() ? P_(".") + fileNameExtension : mpt::PathString()); return filename; } @@ -625,7 +598,7 @@ TempFileGuard::~TempFileGuard() { if(!filename.empty()) { - DeleteFileW(filename.AsNative().c_str()); + DeleteFile(filename.AsNative().c_str()); } } @@ -638,7 +611,7 @@ TempDirGuard::TempDirGuard(const mpt::PathString &dirname_) { return; } - if(::CreateDirectoryW(dirname.AsNative().c_str(), NULL) == 0) + if(::CreateDirectory(dirname.AsNative().c_str(), NULL) == 0) { // fail dirname = mpt::PathString(); } @@ -787,7 +760,7 @@ mpt::PathString FileType::AsFilterString(FlagSet format) const const auto extensions = GetExtensions(); if(format[FileTypeFormatShowExtensions]) { - filter += MPT_PATHSTRING(" ("); + filter += P_(" ("); bool first = true; for(const auto &ext : extensions) { @@ -796,14 +769,14 @@ mpt::PathString FileType::AsFilterString(FlagSet format) const first = false; } else { - filter += MPT_PATHSTRING(","); + filter += P_(","); } - filter += MPT_PATHSTRING("*."); + filter += P_("*."); filter += ext; } - filter += MPT_PATHSTRING(")"); + filter += P_(")"); } - filter += MPT_PATHSTRING("|"); + filter += P_("|"); { bool first = true; for(const auto &ext : extensions) @@ -813,13 +786,13 @@ mpt::PathString FileType::AsFilterString(FlagSet format) const first = false; } else { - filter += MPT_PATHSTRING(";"); + filter += P_(";"); } - filter += MPT_PATHSTRING("*."); + filter += P_("*."); filter += ext; } } - filter += MPT_PATHSTRING("|"); + filter += P_("|"); return filter; } @@ -837,9 +810,9 @@ mpt::PathString FileType::AsFilterOnlyString() const first = false; } else { - filter += MPT_PATHSTRING(";"); + filter += P_(";"); } - filter += MPT_PATHSTRING("*."); + filter += P_("*."); filter += ext; } } @@ -867,7 +840,7 @@ mpt::PathString ToFilterString(const std::vector &fileTypes, FlagSet &fileTypes, bool { filter += type.AsFilterOnlyString(); } - return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? MPT_PATHSTRING(";") : MPT_PATHSTRING("")) + filter; + return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? P_(";") : P_("")) + filter; } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h index 98461e707..8a5a6ec20 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include "FlagSet.h" @@ -27,7 +29,7 @@ namespace mpt { #if MPT_OS_WINDOWS -typedef std::wstring RawPathString; +typedef mpt::winstring RawPathString; #else // !MPT_OS_WINDOWS typedef std::string RawPathString; #endif // if MPT_OS_WINDOWS @@ -43,7 +45,7 @@ private: private: - PathString(const RawPathString & path) + explicit PathString(const RawPathString & path) : path(path) { return; @@ -135,6 +137,9 @@ public: #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + static bool IsPathSeparator(RawPathString::value_type c); + static RawPathString::value_type GetDefaultPathSeparator(); + #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS // Return the same path string with a different (or appended) extension (including "."), e.g. "foo.bar",".txt" -> "foo.txt" or "C:\OpenMPT\foo",".txt" -> "C:\OpenMPT\foo.txt" @@ -147,24 +152,18 @@ public: bool HasTrailingSlash() const { - if(empty()) + if(path.empty()) + { return false; + } RawPathString::value_type c = path[path.length() - 1]; -#if MPT_OS_WINDOWS - return (c == L'\\' || c == L'/'); -#else - return (c == '/'); -#endif + return IsPathSeparator(c); } mpt::PathString &EnsureTrailingSlash() { if(!path.empty() && !HasTrailingSlash()) { -#if MPT_OS_WINDOWS - path += L'\\'; -#else - path += '/'; -#endif + path += GetDefaultPathSeparator(); } return *this; } @@ -178,7 +177,7 @@ public: { return result; } - result = result.AsNative().substr(0, result.AsNative().length() - 1); + result = mpt::PathString(result.AsNative().substr(0, result.AsNative().length() - 1)); } return result; } @@ -208,37 +207,23 @@ public: MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::CharsetLocale, path); } #endif std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, path); } - std::wstring ToWide() const { return path; } + std::wstring ToWide() const { return mpt::ToWide(path); } mpt::ustring ToUnicode() const { return mpt::ToUnicode(path); } #if defined(MPT_ENABLE_CHARSET_LOCALE) - MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetLocale, path)); } - static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetLocale, path)); } + MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetLocale, path)); } + static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetLocale, path)); } #endif - static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetUTF8, path)); } - static PathString FromWide(const std::wstring &path) { return PathString(path); } - static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWide(path)); } + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetUTF8, path)); } + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToWin(path)); } + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWin(path)); } RawPathString AsNative() const { return path; } // Return native string, with possible \\?\ prefix if it exceeds MAX_PATH characters. RawPathString AsNativePrefixed() const; static PathString FromNative(const RawPathString &path) { return PathString(path); } #if defined(_MFC_VER) // CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE - MPT_DEPRECATED_PATH CString ToCString() const { return mpt::ToCString(path); } - MPT_DEPRECATED_PATH static PathString FromCString(const CString &path) { return PathString(mpt::ToWide(path)); } - // Non-warning-generating versions of the above. Use with extra care. - CString ToCStringSilent() const { return mpt::ToCString(path); } - static PathString FromCStringSilent(const CString &path) { return PathString(mpt::ToWide(path)); } - // really special purpose, if !UNICODE, encode unicode in CString as UTF8: - static mpt::PathString TunnelOutofCString(const CString &path); - static CString TunnelIntoCString(const mpt::PathString &path); - // CStringW -#ifdef UNICODE - MPT_DEPRECATED_PATH CString ToCStringW() const { return mpt::ToCString(path); } - MPT_DEPRECATED_PATH static PathString FromCStringW(const CString &path) { return PathString(mpt::ToWide(path)); } -#else - CStringW ToCStringW() const { return mpt::ToCStringW(path); } - static PathString FromCStringW(const CStringW &path) { return PathString(mpt::ToWide(path)); } -#endif + CString ToCString() const { return mpt::ToCString(path); } + static PathString FromCString(const CString &path) { return PathString(mpt::ToWin(path)); } #endif // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries @@ -270,7 +255,7 @@ public: std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetUTF8, path); } #endif mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetUTF8, path); } - static PathString FromUTF8(const std::string &path) { return path; } + static PathString FromUTF8(const std::string &path) { return PathString(path); } #if MPT_WSTRING_CONVERT static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); } #endif @@ -281,7 +266,7 @@ public: #endif // MPT_ENABLE_CHARSET_LOCALE // Convert a path to its simplified form (currently only implemented on Windows) - MPT_DEPRECATED mpt::PathString Simplify() const { return path; } + MPT_DEPRECATED mpt::PathString Simplify() const { return PathString(path); } #endif // MPT_OS_WINDOWS @@ -290,8 +275,16 @@ public: #if defined(MPT_ENABLE_CHARSET_LOCALE) +#if MPT_OS_WINDOWS +#ifdef UNICODE +MPT_DEPRECATED static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.ToUnicode()); } +#else +MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.AsNative()); } +#endif +#else MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.ToUnicode()); } #endif +#endif static inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); } #if MPT_WSTRING_FORMAT static inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); } @@ -301,20 +294,28 @@ static inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWid #if MPT_OS_WINDOWS +#ifdef UNICODE +#define MPT_PATHSTRING_LITERAL(x) ( L ## x ) #define MPT_PATHSTRING(x) mpt::PathString::FromNative( L ## x ) +#else +#define MPT_PATHSTRING_LITERAL(x) ( x ) +#define MPT_PATHSTRING(x) mpt::PathString::FromNative( x ) +#endif #else // !MPT_OS_WINDOWS +#define MPT_PATHSTRING_LITERAL(x) ( x ) #define MPT_PATHSTRING(x) mpt::PathString::FromNative( x ) #endif // MPT_OS_WINDOWS +#define PC_(x) MPT_PATHSTRING_LITERAL(x) +#define PL_(x) MPT_PATHSTRING_LITERAL(x) +#define P_(x) MPT_PATHSTRING(x) + namespace mpt { -bool IsPathSeparator(mpt::RawPathString::value_type c); - - bool PathIsAbsolute(const mpt::PathString &path); @@ -361,7 +362,7 @@ mpt::PathString GetSystemPath(); mpt::PathString GetTempDirectory(); // Returns a new unique absolute path. -mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix = mpt::PathString(), const mpt::PathString &fileNameExtension = MPT_PATHSTRING("tmp")); +mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix = mpt::PathString(), const mpt::PathString &fileNameExtension = P_("tmp")); @@ -465,7 +466,7 @@ public: } static FileType Any() { - return FileType().ShortName(MPT_USTRING("*")).Description(MPT_USTRING("All Files")).AddExtension(MPT_PATHSTRING("*")); + return FileType().ShortName(U_("*")).Description(U_("All Files")).AddExtension(P_("*")); } public: FileType& ShortName(const mpt::ustring &shortName) { m_ShortName = shortName; return *this; } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp index f074bbab9..e4ba915e2 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp @@ -14,16 +14,11 @@ #include "Endianness.h" #include "mptCRC.h" -#include +#include #include -#include #include -#if MPT_OS_WINDOWS -#include -#endif // MPT_OS_WINDOWS - OPENMPT_NAMESPACE_BEGIN @@ -89,30 +84,18 @@ static T generate_timeseed() #else // !MPT_BUILD_FUZZER { - #if MPT_OS_WINDOWS - FILETIME t; - MemsetZero(t); - GetSystemTimeAsFileTime(&t); - #else // !MPT_OS_WINDOWS - std::time_t t = std::time(nullptr); - #endif // MPT_OS_WINDOWS - mpt::byte bytes[sizeof(t)]; - std::memcpy(bytes, &t, sizeof(t)); - MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) - { - std::reverse(std::begin(bytes), std::end(bytes)); - } + uint64be time; + time = std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count(); + mpt::byte bytes[sizeof(time)]; + std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } { - std::clock_t c = std::clock(); - mpt::byte bytes[sizeof(c)]; - std::memcpy(bytes, &c, sizeof(c)); - MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) - { - std::reverse(std::begin(bytes), std::end(bytes)); - } + uint64be time; + time = std::chrono::duration_cast(std::chrono::high_resolution_clock().now().time_since_epoch()).count(); + mpt::byte bytes[sizeof(time)]; + std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } @@ -273,16 +256,16 @@ uint64 prng_random_device_seeder::generate_seed64() #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) static mpt::random_device *g_rd = nullptr; -static mpt::thread_safe_prng *g_best_prng = nullptr; +static mpt::thread_safe_prng *g_global_prng = nullptr; void set_global_random_device(mpt::random_device *rd) { g_rd = rd; } -void set_global_prng(mpt::thread_safe_prng *prng) +void set_global_prng(mpt::thread_safe_prng *prng) { - g_best_prng = prng; + g_global_prng = prng; } mpt::random_device & global_random_device() @@ -290,9 +273,9 @@ mpt::random_device & global_random_device() return *g_rd; } -mpt::thread_safe_prng & global_prng() +mpt::thread_safe_prng & global_prng() { - return *g_best_prng; + return *g_global_prng; } #else @@ -303,10 +286,10 @@ mpt::random_device & global_random_device() return g_rd; } -mpt::thread_safe_prng & global_prng() +mpt::thread_safe_prng & global_prng() { - static mpt::thread_safe_prng g_best_prng(mpt::make_prng(global_random_device())); - return g_best_prng; + static mpt::thread_safe_prng g_global_prng(mpt::make_prng(global_random_device())); + return g_global_prng; } #endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h index ec75928d1..1442e73ef 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "mptMutex.h" #include @@ -184,15 +186,15 @@ inline T random(Trng & rng, std::size_t required_entropy_bits) template struct float_traits { }; template <> struct float_traits { typedef uint32 mantissa_uint_type; - static const int mantissa_bits = 24; + enum : int { mantissa_bits = 24 }; }; template <> struct float_traits { typedef uint64 mantissa_uint_type; - static const int mantissa_bits = 53; + enum : int { mantissa_bits = 53 }; }; template <> struct float_traits { typedef uint64 mantissa_uint_type; - static const int mantissa_bits = 63; + enum : int { mantissa_bits = 63 }; }; template @@ -402,33 +404,33 @@ public: // List the ones we are likely to use. template <> struct engine_traits { - static const std::size_t seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size; + enum : std::size_t { seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size }; typedef std::mt19937 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { - mpt::seed_seq_values values(rd); - std::seed_seq seed(values.begin(), values.end()); + std::unique_ptr> values = mpt::make_unique>(rd); + std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } }; template <> struct engine_traits { - static const std::size_t seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size; + enum : std::size_t { seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size }; typedef std::mt19937_64 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { - mpt::seed_seq_values values(rd); - std::seed_seq seed(values.begin(), values.end()); + std::unique_ptr> values = mpt::make_unique>(rd); + std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } }; template <> struct engine_traits { - static const std::size_t seed_bits = std::ranlux24_base::word_size; + enum : std::size_t { seed_bits = std::ranlux24_base::word_size }; typedef std::ranlux24_base rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } @@ -441,7 +443,7 @@ template <> struct engine_traits { }; template <> struct engine_traits { - static const std::size_t seed_bits = std::ranlux48_base::word_size; + enum : std::size_t { seed_bits = std::ranlux48_base::word_size }; typedef std::ranlux48_base rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } @@ -454,7 +456,7 @@ template <> struct engine_traits { }; template <> struct engine_traits { - static const std::size_t seed_bits = std::ranlux24_base::word_size; + enum : std::size_t { seed_bits = std::ranlux24_base::word_size }; typedef std::ranlux24 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux24_base::word_size; } @@ -467,7 +469,7 @@ template <> struct engine_traits { }; template <> struct engine_traits { - static const std::size_t seed_bits = std::ranlux48_base::word_size; + enum : std::size_t { seed_bits = std::ranlux48_base::word_size }; typedef std::ranlux48 rng_type; typedef rng_type::result_type result_type; static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux48_base::word_size; } @@ -546,8 +548,7 @@ typedef mpt::prng_random_device random_device; // 2. Use fast PRNGs in order to not waste time fuzzing more complex PRNG // implementations. typedef mpt::rng::lcg_msvc fast_prng; -typedef mpt::rng::lcg_c99 main_prng; -typedef mpt::rng::lcg_musl best_prng; +typedef mpt::rng::lcg_musl good_prng; #else // !MPT_BUILD_FUZZER @@ -557,26 +558,12 @@ typedef mpt::sane_random_device random_device; // We cannot use std::minstd_rand here because it has not a power-of-2 sized // output domain which we rely upon. typedef mpt::rng::lcg_msvc fast_prng; // about 3 ALU operations, ~32bit of state, suited for inner loops -typedef std::mt19937 main_prng; -#if MPT_MSVC_AT_LEAST(2017,5) && defined(_MSC_FULL_VER) -#if (_MSC_FULL_VER < 191225831) -// work-around compiler crash -// c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\random(978): fatal error C1001: An internal error has occurred in the compiler. -// (compiler file 'f:\dd\vctools\compiler\utc\src\p2\main.c', line 258) -// reported at: https://developercommunity.visualstudio.com/content/problem/162089/random-engines-crashing-vs-155-optimizer.html?childToView=164098#comment-164098 -typedef std::mt19937_64 best_prng; -#else -typedef std::ranlux48 best_prng; -#endif -#else -typedef std::ranlux48 best_prng; -#endif +typedef std::ranlux48 good_prng; #endif // MPT_BUILD_FUZZER -typedef mpt::main_prng default_prng; -typedef mpt::main_prng prng; +typedef mpt::good_prng default_prng; template @@ -629,11 +616,11 @@ public: mpt::random_device & global_random_device(); -mpt::thread_safe_prng & global_prng(); +mpt::thread_safe_prng & global_prng(); #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) void set_global_random_device(mpt::random_device *rd); -void set_global_prng(mpt::thread_safe_prng *rng); +void set_global_prng(mpt::thread_safe_prng *rng); #endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h b/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h new file mode 100644 index 000000000..064c198e2 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h @@ -0,0 +1,121 @@ +/* + * mptSpan.h + * --------- + * Purpose: Various useful utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "mptBaseTypes.h" + +#include +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + + +namespace mpt { + + + +// Simplified version of gsl::span. +// Non-owning read-only or read-write view into a contiguous block of T +// objects, i.e. equivalent to a (beg,end) or (data,size) tuple. +// Can eventually be replaced without further modifications with a full C++20 +// std::span. +template +class span +{ + +public: + + typedef std::size_t size_type; + + typedef T value_type; + typedef T & reference; + typedef T * pointer; + typedef const T * const_pointer; + typedef const T & const_reference; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef typename std::iterator_traits::difference_type difference_type; + +private: + + T * m_beg; + T * m_end; + +public: + + span() : m_beg(nullptr), m_end(nullptr) { } + + span(pointer beg, pointer end) : m_beg(beg), m_end(end) { } + + span(pointer data, size_type size) : m_beg(data), m_end(data + size) { } + + template span(U (&arr)[N]) : m_beg(arr), m_end(arr + N) { } + + template span(Cont &cont) : m_beg(cont.empty() ? nullptr : &(cont[0])), m_end(cont.empty() ? nullptr : &(cont[0]) + cont.size()) { } + + span(const span &other) : m_beg(other.begin()), m_end(other.end()) { } + + template span(const span &other) : m_beg(other.begin()), m_end(other.end()) { } + + span & operator = (span other) { m_beg = other.begin(); m_end = other.end(); return *this; } + + iterator begin() const { return iterator(m_beg); } + iterator end() const { return iterator(m_end); } + + const_iterator cbegin() const { return const_iterator(begin()); } + const_iterator cend() const { return const_iterator(end()); } + + operator bool () const noexcept { return m_beg != nullptr; } + + reference operator[](size_type index) { return at(index); } + const_reference operator[](size_type index) const { return at(index); } + + bool operator==(span const & other) const noexcept { return size() == other.size() && (m_beg == other.m_beg || std::equal(begin(), end(), other.begin())); } + bool operator!=(span const & other) const noexcept { return !(*this == other); } + + reference at(size_type index) { return m_beg[index]; } + const_reference at(size_type index) const { return m_beg[index]; } + + pointer data() const noexcept { return m_beg; } + + bool empty() const noexcept { return size() == 0; } + + size_type size() const noexcept { return static_cast(std::distance(m_beg, m_end)); } + size_type length() const noexcept { return size(); } + +}; // class span + +template inline span as_span(T * beg, T * end) { return span(beg, end); } + +template inline span as_span(T * data, std::size_t size) { return span(data, size); } + +template inline span as_span(T (&arr)[N]) { return span(std::begin(arr), std::end(arr)); } + +template inline span as_span(std::array & cont) { return span(cont); } + +template inline span as_span(const std::array & cont) { return span(cont); } + + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp index dbf68221a..823b6785b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp @@ -72,6 +72,10 @@ List of string types UTF16 (on windows) or UTF32 (otherwise). Do not use unless there is an obvious reason to do so. + * mpt::lstring (OpenMPT) + OpenMPT locale string type. The encoding is always CP_ACP. Do not use unless + there is an obvious reason to do so. + * char* (OpenMPT, libopenmpt) C string of unspecified encoding. Use only for static literals or in performance critical inner loops where full control and avoidance of memory @@ -82,27 +86,29 @@ List of string types performance critical inner loops where full control and avoidance of memory allocation is required. + * mpt::winstring (OpenMPT) + OpenMPT type-safe string to interface with native WinAPI, either encoded in + locale/CP_ACP (if !UNICODE) or UTF16 (if UNICODE). + * CString (OpenMPT) MFC string type, either encoded in locale/CP_ACP (if !UNICODE) or UTF16 (if UNICODE). Specify literals with _T(""). Use in MFC GUI code. * CStringA (OpenMPT) - MFC ANSI string type. The encoding is always CP_ACP. Do not use unless there - is an obvious reason to do so. + MFC ANSI string type. The encoding is always CP_ACP. Do not use. * CStringW (OpenMPT) - MFC Unicode string type. Use in MFC GUI code when explicit Unicode support - is required. + MFC Unicode string type. Do not use. * mpt::PathString (OpenMPT, libopenmpt) String type representing paths and filenames. Always use for these in order - to avoid potentially lossy conversions. Use MPT_PATHSTRING("") macro for + to avoid potentially lossy conversions. Use P_("") macro for literals. * mpt::ustring (OpenMPT, libopenmpt) The default unicode string type. Can be encoded in UTF8 or UTF16 or UTF32, depending on MPT_USTRING_MODE_* and sizeof(wchar_t). Literals can written as - MPT_USTRING(""). Use as your default string type if no other string type is + U_(""). Use as your default string type if no other string type is a measurably better fit. * MPT_UTF8 (OpenMPT, libopenmpt) @@ -154,7 +160,7 @@ heuristic. * AnyString (OpenMPT, libopenmpt) Tries to do the smartest auto-magic we can do. - * AnyLocaleString (OpenMPT, libopenmpt) + * AnyStringLocale (OpenMPT, libopenmpt) char-based strings are assumed to be in locale encoding. * AnyStringUTF8orLocale (OpenMPT, libopenmpt) @@ -215,23 +221,15 @@ else else T = mpt::PathString fi + elif winapi interfacing code + T = mpt::winstring elif mfc/gui code - if directly interface with wide winapi - T = CStringW - elif needs unicode support - T = CStringW - else - T = CString - fi + T = CString else - if directly interfacing with wide winapi - T = std::wstring + if constexpr context or global data + T = MPT_UCHAR_TYPE* / MPT_ULITERAL else - if constexpr context or global data - T = MPT_UCHAR_TYPE* / MPT_ULITERAL - else - T = mpt::ustring - fi + T = mpt::ustring fi fi fi @@ -293,10 +291,14 @@ when converting between different Unicode encodings. Interfacing with WinAPI ----------------------- -When in MFC code, use CString or CStringW as appropriate. -When in non MFC code, either use std::wstring when directly interfacing with the -Unicode API, or use the TCHAR helper functions: ToTcharBuf, FromTcharBuf, -ToTcharStr, FromTcharStr. +When in MFC code, use CString. +When in non MFC code, either use std::wstring when directly interfacing with +APIs only available in WCHAR variants, or use mpt::winstring and +mpt::WinStringBuf helpers otherwise. +Specify TCHAR string literals with _T("foo") in mptrack/, and with TEXT("foo") +in common/ or sounddev/. _T() requires which is specific to the MSVC +runtime and not portable across compilers. TEXT() is from . We use +_T() in mptrack/ only because it is shorter. @@ -437,20 +439,28 @@ static const uint32 CharsetTableCP437AMS2[256] = { #undef C -#if MPT_COMPILER_MSVC -#pragma warning(disable:4428) // universal-character-name encountered in source -#endif -static std::wstring From8bit(const std::string &str, const uint32 (&table)[256], wchar_t replacement = L'\uFFFD') +#if defined(MPT_COMPILER_QUIRK_NO_WCHAR) +typedef char32_t widechar; +typedef std::u32string widestring; +static constexpr widechar wide_default_replacement = 0xFFFD; +#else // !MPT_COMPILER_QUIRK_NO_WCHAR +typedef wchar_t widechar; +typedef std::wstring widestring; +static constexpr widechar wide_default_replacement = L'\uFFFD'; +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR + + +static widestring From8bit(const std::string &str, const uint32 (&table)[256], widechar replacement = wide_default_replacement) { - std::wstring res; + widestring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { uint32 c = static_cast(static_cast(str[i])); if(c < mpt::size(table)) { - res.push_back(static_cast(static_cast(table[c]))); + res.push_back(static_cast(static_cast(table[c]))); } else { res.push_back(replacement); @@ -459,7 +469,7 @@ static std::wstring From8bit(const std::string &str, const uint32 (&table)[256], return res; } -static std::string To8bit(const std::wstring &str, const uint32 (&table)[256], char replacement = '?') +static std::string To8bit(const widestring &str, const uint32 (&table)[256], char replacement = '?') { std::string res; res.reserve(str.length()); @@ -502,16 +512,16 @@ static std::string To8bit(const std::wstring &str, const uint32 (&table)[256], c #if defined(MPT_CHARSET_CODECVTUTF8) || defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) -static std::wstring FromAscii(const std::string &str, wchar_t replacement = L'\uFFFD') +static widestring FromAscii(const std::string &str, widechar replacement = wide_default_replacement) { - std::wstring res; + widestring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { uint8 c = str[i]; if(c <= 0x7f) { - res.push_back(static_cast(static_cast(c))); + res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); @@ -520,7 +530,7 @@ static std::wstring FromAscii(const std::string &str, wchar_t replacement = L'\u return res; } -static std::string ToAscii(const std::wstring &str, char replacement = '?') +static std::string ToAscii(const widestring &str, char replacement = '?') { std::string res; res.reserve(str.length()); @@ -538,20 +548,20 @@ static std::string ToAscii(const std::wstring &str, char replacement = '?') return res; } -static std::wstring FromISO_8859_1(const std::string &str, wchar_t replacement = L'\uFFFD') +static widestring FromISO_8859_1(const std::string &str, widechar replacement = wide_default_replacement) { MPT_UNREFERENCED_PARAMETER(replacement); - std::wstring res; + widestring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { uint8 c = str[i]; - res.push_back(static_cast(static_cast(c))); + res.push_back(static_cast(static_cast(c))); } return res; } -static std::string ToISO_8859_1(const std::wstring &str, char replacement = '?') +static std::string ToISO_8859_1(const widestring &str, char replacement = '?') { std::string res; res.reserve(str.length()); @@ -569,18 +579,19 @@ static std::string ToISO_8859_1(const std::wstring &str, char replacement = '?') return res; } -#if defined(MPT_ENABLE_CHARSET_LOCALE) + +#if defined(MPT_ENABLE_CHARSET_LOCALE) && !defined(MPT_LOCALE_ASSUME_CHARSET) // Note: // // std::codecvt::out in LLVM libc++ does not advance in and out pointers when -// running into a non-convertible cahracter. This can happen when no locale is +// running into a non-convertible character. This can happen when no locale is // set on FreeBSD or MacOSX. This behaviour violates the C++ standard. // // We apply the following (albeit costly, even on other platforms) work-around: // If the conversion errors out and does not advance the pointers at all, we // retry the conversion with a space character prepended to the string. If it -// still does error our, we retry the whole conversion character by character. +// still does error out, we retry the whole conversion character by character. // This is costly even on other platforms in one single case: The first // character is an invalid Unicode code point or otherwise not convertible. Any // following non-convertible characters are not a problem. @@ -815,7 +826,7 @@ static std::string ToLocale(const std::wstring &str, char replacement = '?') return String::ToAscii(str, replacement); // fallback } -#endif +#endif // MPT_ENABLE_CHARSET_LOCALE && !MPT_LOCALE_ASSUME_CHARSET #endif // MPT_CHARSET_CODECVTUTF8 || MPT_CHARSET_INTERNAL || MPT_CHARSET_WIN32 @@ -839,11 +850,11 @@ static std::string ToUTF8(const std::wstring &str, char replacement = '?') #if defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) -static std::wstring FromUTF8(const std::string &str, wchar_t replacement = L'\uFFFD') +static widestring FromUTF8(const std::string &str, widechar replacement = wide_default_replacement) { const std::string &in = str; - std::wstring out; + widestring out; // state: std::size_t charsleft = 0; @@ -882,7 +893,7 @@ static std::wstring FromUTF8(const std::string &str, wchar_t replacement = L'\uF charsleft--; if ( charsleft == 0 ) { - MPT_CONSTANT_IF ( sizeof( wchar_t ) == 2 ) { + MPT_CONSTANT_IF ( sizeof( widechar ) == 2 ) { if ( ucs4 > 0x1fffff ) { out.push_back( replacement ); ucs4 = 0; @@ -898,7 +909,7 @@ static std::wstring FromUTF8(const std::string &str, wchar_t replacement = L'\uF out.push_back( lo_sur ); } } else { - out.push_back( static_cast( ucs4 ) ); + out.push_back( static_cast( ucs4 ) ); } ucs4 = 0; } @@ -917,9 +928,9 @@ static std::wstring FromUTF8(const std::string &str, wchar_t replacement = L'\uF } -static std::string ToUTF8(const std::wstring &str, char replacement = '?') +static std::string ToUTF8(const widestring &str, char replacement = '?') { - const std::wstring &in = str; + const widestring &in = str; std::string out; @@ -928,7 +939,7 @@ static std::string ToUTF8(const std::wstring &str, char replacement = '?') wchar_t wc = in[i]; uint32 ucs4 = 0; - MPT_CONSTANT_IF ( sizeof( wchar_t ) == 2 ) { + MPT_CONSTANT_IF ( sizeof( widechar ) == 2 ) { uint16 c = static_cast( wc ); if ( i + 1 < in.length() ) { // check for surrogate pair @@ -1107,6 +1118,7 @@ static const char * Charset_wchar_t() { return "UTF-16BE"; } + return "UTF-16"; } else if(sizeof(wchar_t) == 4) { // "UTF-32" generates BOM @@ -1118,8 +1130,8 @@ static const char * Charset_wchar_t() { return "UTF-32BE"; } + return "UTF-32"; } - return ""; #endif // !MPT_ICONV_NO_WCHAR | MPT_ICONV_NO_WCHAR } @@ -1128,14 +1140,15 @@ static const char * Charset_wchar_t() #if !defined(MPT_CHARSET_ICONV) template -Tdststring EncodeImplFallback(Charset charset, const std::wstring &src); +static Tdststring EncodeImplFallback(Charset charset, const widestring &src); #endif // !MPT_CHARSET_ICONV // templated on 8bit strings because of type-safe variants template -Tdststring EncodeImpl(Charset charset, const std::wstring &src) +static Tdststring EncodeImpl(Charset charset, const widestring &src) { - STATIC_ASSERT(sizeof(typename Tdststring::value_type) == sizeof(char)); + MPT_STATIC_ASSERT(sizeof(typename Tdststring::value_type) == sizeof(char)); + MPT_STATIC_ASSERT((std::is_same::value)); if(charset == CharsetCP437AMS || charset == CharsetCP437AMS2) { std::string out; @@ -1162,9 +1175,16 @@ Tdststring EncodeImpl(Charset charset, const std::wstring &src) { return Tdststring(); } - std::vector encoded_string(required_size); - WideCharToMultiByte(codepage, 0, src.c_str(), -1, encoded_string.data(), required_size, nullptr, nullptr); - return reinterpret_cast(encoded_string.data()); + #if MPT_CXX_AT_LEAST(17) + Tdststring encoded_string(required_size, char()); + WideCharToMultiByte(codepage, 0, src.c_str(), -1, encoded_string.data(), required_size, nullptr, nullptr); + encoded_string.resize(encoded_string.size() - 1); // remove \0 + return encoded_string; + #else + std::vector encoded_string(required_size); + WideCharToMultiByte(codepage, 0, src.c_str(), -1, encoded_string.data(), required_size, nullptr, nullptr); + return reinterpret_cast(encoded_string.data()); + #endif #elif defined(MPT_CHARSET_ICONV) iconv_t conv = iconv_t(); conv = iconv_open(CharsetToStringTranslit(charset), Charset_wchar_t()); @@ -1176,18 +1196,18 @@ Tdststring EncodeImpl(Charset charset, const std::wstring &src) throw std::runtime_error("iconv conversion not working"); } } - std::vector wide_string(src.c_str(), src.c_str() + src.length() + 1); + std::vector wide_string(src.c_str(), src.c_str() + src.length() + 1); std::vector encoded_string(wide_string.size() * 8); // large enough char * inbuf = reinterpret_cast(wide_string.data()); - size_t inbytesleft = wide_string.size() * sizeof(wchar_t); + size_t inbytesleft = wide_string.size() * sizeof(widechar); char * outbuf = encoded_string.data(); size_t outbytesleft = encoded_string.size(); while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) { if(errno == EILSEQ || errno == EILSEQ) { - inbuf += sizeof(wchar_t); - inbytesleft -= sizeof(wchar_t); + inbuf += sizeof(widechar); + inbytesleft -= sizeof(widechar); outbuf[0] = '?'; outbuf++; outbytesleft--; @@ -1210,13 +1230,25 @@ Tdststring EncodeImpl(Charset charset, const std::wstring &src) #if !defined(MPT_CHARSET_ICONV) template -Tdststring EncodeImplFallback(Charset charset, const std::wstring &src) +static Tdststring EncodeImplFallback(Charset charset, const widestring &src) { std::string out; +#if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == CharsetLocale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } + #endif +#endif switch(charset) { #if defined(MPT_ENABLE_CHARSET_LOCALE) +#if defined(MPT_LOCALE_ASSUME_CHARSET) + case CharsetLocale: MPT_ASSERT_NOTREACHED(); break; +#else case CharsetLocale: out = String::ToLocale(src); break; +#endif #endif case CharsetUTF8: out = String::ToUTF8(src); break; case CharsetASCII: out = String::ToAscii(src); break; @@ -1234,18 +1266,19 @@ Tdststring EncodeImplFallback(Charset charset, const std::wstring &src) #if !defined(MPT_CHARSET_ICONV) template -std::wstring DecodeImplFallback(Charset charset, const Tsrcstring &src); +static widestring DecodeImplFallback(Charset charset, const Tsrcstring &src); #endif // !MPT_CHARSET_ICONV // templated on 8bit strings because of type-safe variants template -std::wstring DecodeImpl(Charset charset, const Tsrcstring &src) +static widestring DecodeImpl(Charset charset, const Tsrcstring &src) { - STATIC_ASSERT(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + MPT_STATIC_ASSERT(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + MPT_STATIC_ASSERT((std::is_same::value)); if(charset == CharsetCP437AMS || charset == CharsetCP437AMS2) { std::string in(src.begin(), src.end()); - std::wstring out; + widestring out; if(charset == CharsetCP437AMS ) out = String::From8bit(in, CharsetTableCP437AMS ); if(charset == CharsetCP437AMS2) out = String::From8bit(in, CharsetTableCP437AMS2); return out; @@ -1267,11 +1300,18 @@ std::wstring DecodeImpl(Charset charset, const Tsrcstring &src) int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, nullptr, 0); if(required_size <= 0) { - return std::wstring(); + return widestring(); } - std::vector decoded_string(required_size); - MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, decoded_string.data(), required_size); - return decoded_string.data(); + #if MPT_CXX_AT_LEAST(17) + widestring decoded_string(required_size, widechar()); + MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, decoded_string.data(), required_size); + decoded_string.resize(decoded_string.size() - 1); // remove \0 + return decoded_string; + #else + std::vector decoded_string(required_size); + MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, decoded_string.data(), required_size); + return decoded_string.data(); + #endif #elif defined(MPT_CHARSET_ICONV) iconv_t conv = iconv_t(); conv = iconv_open(Charset_wchar_t(), CharsetToString(charset)); @@ -1280,43 +1320,31 @@ std::wstring DecodeImpl(Charset charset, const Tsrcstring &src) throw std::runtime_error("iconv conversion not working"); } std::vector encoded_string(reinterpret_cast(src.c_str()), reinterpret_cast(src.c_str()) + src.length() + 1); - std::vector wide_string(encoded_string.size() * 8); // large enough + std::vector wide_string(encoded_string.size() * 8); // large enough char * inbuf = encoded_string.data(); size_t inbytesleft = encoded_string.size(); char * outbuf = reinterpret_cast(wide_string.data()); - size_t outbytesleft = wide_string.size() * sizeof(wchar_t); + size_t outbytesleft = wide_string.size() * sizeof(widechar); while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) { if(errno == EILSEQ || errno == EILSEQ) { inbuf++; inbytesleft--; - for(std::size_t i = 0; i < sizeof(wchar_t); ++i) + for(std::size_t i = 0; i < sizeof(widechar); ++i) { outbuf[i] = 0; } - #if defined(MPT_PLATFORM_LITTLE_ENDIAN) - outbuf[1] = uint8(0xff); outbuf[0] = uint8(0xfd); - #elif defined(MPT_PLATFORM_BIG_ENDIAN) - outbuf[sizeof(wchar_t)-1 - 1] = uint8(0xff); outbuf[sizeof(wchar_t)-1 - 0] = uint8(0xfd); - #else - MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) - { - outbuf[1] = uint8(0xff); outbuf[0] = uint8(0xfd); - } - MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) - { - outbuf[sizeof(wchar_t)-1 - 1] = uint8(0xff); outbuf[sizeof(wchar_t)-1 - 0] = uint8(0xfd); - } - #endif - outbuf += sizeof(wchar_t); - outbytesleft -= sizeof(wchar_t); + widechar tmp = 0xfffd; + std::memcpy(outbuf, &tmp, sizeof(widechar)); + outbuf += sizeof(widechar); + outbytesleft -= sizeof(widechar); iconv(conv, NULL, NULL, NULL, NULL); // reset state } else { iconv_close(conv); conv = iconv_t(); - return std::wstring(); + return widestring(); } } iconv_close(conv); @@ -1329,14 +1357,26 @@ std::wstring DecodeImpl(Charset charset, const Tsrcstring &src) #if !defined(MPT_CHARSET_ICONV) template -std::wstring DecodeImplFallback(Charset charset, const Tsrcstring &src) +static widestring DecodeImplFallback(Charset charset, const Tsrcstring &src) { std::string in(src.begin(), src.end()); - std::wstring out; + widestring out; +#if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == CharsetLocale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } + #endif +#endif switch(charset) { #if defined(MPT_ENABLE_CHARSET_LOCALE) +#if defined(MPT_LOCALE_ASSUME_CHARSET) + case CharsetLocale: MPT_ASSERT_NOTREACHED(); break; +#else case CharsetLocale: out = String::FromLocale(in); break; +#endif #endif case CharsetUTF8: out = String::FromUTF8(in); break; case CharsetASCII: out = String::FromAscii(in); break; @@ -1354,7 +1394,7 @@ std::wstring DecodeImplFallback(Charset charset, const Tsrcstring &src) // templated on 8bit strings because of type-safe variants template -Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) +static Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) { STATIC_ASSERT(sizeof(typename Tdststring::value_type) == sizeof(char)); STATIC_ASSERT(sizeof(typename Tsrcstring::value_type) == sizeof(char)); @@ -1411,6 +1451,7 @@ Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) } + } // namespace String @@ -1425,6 +1466,12 @@ std::wstring ToWide(Charset from, const std::string &str) { return String::DecodeImpl(from, str); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::wstring ToWide(const mpt::lstring &str) +{ + return String::DecodeImpl(CharsetLocale, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE #endif #if MPT_WSTRING_CONVERT @@ -1437,6 +1484,56 @@ std::string ToCharset(Charset to, Charset from, const std::string &str) { return String::ConvertImpl(to, from, str); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::string ToCharset(Charset to, const mpt::lstring &str) +{ + return String::ConvertImpl(to, CharsetLocale, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +#if MPT_WSTRING_CONVERT +mpt::lstring ToLocale(const std::wstring &str) +{ + return String::EncodeImpl(CharsetLocale, str); +} +#endif +mpt::lstring ToLocale(Charset from, const std::string &str) +{ + return String::ConvertImpl(CharsetLocale, from, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS +#if MPT_WSTRING_CONVERT +mpt::winstring ToWin(const std::wstring &str) +{ + #ifdef UNICODE + return str; + #else + return ToLocale(str); + #endif +} +#endif +mpt::winstring ToWin(Charset from, const std::string &str) +{ + #ifdef UNICODE + return ToWide(from, str); + #else + return ToLocale(from, str); + #endif +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::winstring ToWin(const mpt::lstring &str) +{ + #ifdef UNICODE + return ToWide(str); + #else + return str; + #endif +} +#endif // MPT_ENABLE_CHARSET_LOCALE +#endif // MPT_OS_WINDOWS #if defined(_MFC_VER) @@ -1473,39 +1570,30 @@ std::string ToCharset(Charset to, const CString &str) return ToCharset(to, CharsetLocale, str.GetString()); #endif } - -#ifdef UNICODE -// inline -#else // !UNICODE -CStringW ToCStringW(const CString &str) +#if defined(MPT_ENABLE_CHARSET_LOCALE) +CString ToCString(const mpt::lstring &str) { - return ToWide(str).c_str(); + #ifdef UNICODE + return ToWide(str).c_str(); + #else + return str.c_str(); + #endif } -CStringW ToCStringW(const std::wstring &str) +mpt::lstring ToLocale(const CString &str) { - return str.c_str(); + #ifdef UNICODE + return String::EncodeImpl(CharsetLocale, str.GetString()); + #else + return str.GetString(); + #endif } -CStringW ToCStringW(Charset from, const std::string &str) -{ - return ToWide(from, str).c_str(); -} -CStringW ToCStringW(const CStringW &str) -{ - return str; -} -std::wstring ToWide(const CStringW &str) +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const CString &str) { return str.GetString(); } -std::string ToCharset(Charset to, const CStringW &str) -{ - return ToCharset(to, str.GetString()); -} -CString ToCString(const CStringW &str) -{ - return ToCharset(CharsetLocale, str).c_str(); -} -#endif // UNICODE +#endif // MPT_OS_WINDOWS #endif // MFC @@ -1523,6 +1611,12 @@ mpt::ustring ToUnicode(Charset from, const std::string &str) { return String::ConvertImpl(mpt::CharsetUTF8, from, str); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::ustring ToUnicode(const mpt::lstring &str) +{ + return String::ConvertImpl(mpt::CharsetUTF8, mpt::CharsetLocale, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE #if defined(_MFC_VER) mpt::ustring ToUnicode(const CString &str) { @@ -1532,12 +1626,6 @@ mpt::ustring ToUnicode(const CString &str) return String::ConvertImpl(mpt::CharsetUTF8, mpt::CharsetLocale, str.GetString()); #endif // UNICODE } -#ifndef UNICODE -mpt::ustring ToUnicode(const CStringW &str) -{ - return String::EncodeImpl(mpt::CharsetUTF8, str.GetString()); -} -#endif // !UNICODE #endif // MFC #endif // MPT_USTRING_MODE_WIDE @@ -1554,6 +1642,22 @@ std::string ToCharset(Charset to, const mpt::ustring &str) { return String::ConvertImpl(to, mpt::CharsetUTF8, str); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::lstring ToLocale(const mpt::ustring &str) +{ + return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const mpt::ustring &str) +{ + #ifdef UNICODE + return String::DecodeImpl(mpt::CharsetUTF8, str); + #else + return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str); + #endif +} +#endif // MPT_OS_WINDOWS #if defined(_MFC_VER) CString ToCString(const mpt::ustring &str) { @@ -1570,6 +1674,94 @@ CString ToCString(const mpt::ustring &str) +static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, bool * isFallback = nullptr) +{ + mpt::Charset result = fallback; + switch(codepage) + { + case 65001: + result = mpt::CharsetUTF8; + if(isFallback) *isFallback = false; + break; + case 20127: + result = mpt::CharsetASCII; + if(isFallback) *isFallback = false; + break; + case 28591: + result = mpt::CharsetISO8859_1; + if(isFallback) *isFallback = false; + break; + case 28605: + result = mpt::CharsetISO8859_15; + if(isFallback) *isFallback = false; + break; + case 437: + result = mpt::CharsetCP437; + if(isFallback) *isFallback = false; + break; + case 1252: + result = mpt::CharsetWindows1252; + if(isFallback) *isFallback = false; + break; + default: + result = fallback; + if(isFallback) *isFallback = true; + break; + } + return result; +} + +#if MPT_OS_WINDOWS + +static bool TestCodePage(uint16 codepage) +{ + return IsValidCodePage(codepage) ? true : false; +} + +static mpt::ustring FromCodePageDirect(uint16 codepage, const std::string & src) +{ + int required_size = MultiByteToWideChar(codepage, 0, src.c_str(), -1, nullptr, 0); + if(required_size <= 0) + { + return mpt::ustring(); + } + #if MPT_CXX_AT_LEAST(17) + std::wstring decoded_string(required_size, wchar_t()); + MultiByteToWideChar(codepage, 0, src.c_str(), -1, decoded_string.data(), required_size); + decoded_string.resize(decoded_string.size() - 1); // remove \0 + return mpt::ToUnicode(decoded_string); + #else + std::vector decoded_string(required_size); + MultiByteToWideChar(codepage, 0, src.c_str(), -1, decoded_string.data(), required_size); + return mpt::ToUnicode(std::wstring(decoded_string.data())); + #endif +} + +#endif // MPT_OS_WINDOWS + +mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str) +{ + #if MPT_OS_WINDOWS + mpt::ustring result; + bool noCharsetMatch = true; + mpt::Charset fileCharset = mpt::CharsetFromCodePage(codepage, fallback, &noCharsetMatch); + if(noCharsetMatch && TestCodePage(codepage)) + { + result = mpt::FromCodePageDirect(codepage, str); + } else + { + result = mpt::ToUnicode(fileCharset, str); + } + return result; + #else // !MPT_OS_WINDOWS + return mpt::ToUnicode(mpt::CharsetFromCodePage(codepage, fallback), str); + #endif // MPT_OS_WINDOWS +} + + + + + char ToLowerCaseAscii(char c) { if('A' <= c && c <= 'Z') @@ -1650,9 +1842,9 @@ mpt::ustring ToLowerCase(const mpt::ustring &s) tmp.MakeLower(); return mpt::ToUnicode(tmp); #else // !UNICODE - CStringW tmp = mpt::ToCStringW(s); + CStringW tmp = mpt::ToWide(s).c_str(); tmp.MakeLower(); - return mpt::ToUnicode(tmp); + return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE #else // !_MFC_VER std::wstring ws = mpt::ToWide(s); @@ -1669,9 +1861,9 @@ mpt::ustring ToUpperCase(const mpt::ustring &s) tmp.MakeUpper(); return mpt::ToUnicode(tmp); #else // !UNICODE - CStringW tmp = mpt::ToCStringW(s); + CStringW tmp = mpt::ToWide(s).c_str(); tmp.MakeUpper(); - return mpt::ToUnicode(tmp); + return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE #else // !_MFC_VER std::wstring ws = mpt::ToWide(s); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.h b/Frameworks/OpenMPT/OpenMPT/common/mptString.h index ea7bbaad7..d998f088b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptString.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.h @@ -10,7 +10,11 @@ #pragma once -#include "mptTypeTraits.h" +#include "BuildSettings.h" + +#include "mptAlloc.h" +#include "mptBaseTypes.h" +#include "mptSpan.h" #include #include @@ -26,21 +30,94 @@ namespace mpt { + +template inline span as_span(std::basic_string & str) { return span(&(str[0]), str.length()); } + +template inline span as_span(const std::basic_string & str) { return span(&(str[0]), str.length()); } + + + +template inline std::vector::type> make_vector(const std::basic_string & str) { return std::vector::type>(str.begin(), str.end()); } + + + +// string_traits abstract the API of underlying string classes, in particular they allow adopting to CString without having to specialize for CString explicitly + +template +struct string_traits +{ + + typedef Tstring string_type; + typedef typename string_type::size_type size_type; + typedef typename string_type::value_type char_type; + + static inline std::size_t length(const string_type &str) { return str.length(); } + + static inline void reserve(string_type &str, std::size_t size) { str.reserve(size); } + + static inline string_type& append(string_type &str, const string_type &a) { return str.append(a); } + static inline string_type& append(string_type &str, string_type &&a) { return str.append(std::move(a)); } + static inline string_type& append(string_type &str, std::size_t count, char_type c) { return str.append(count, c); } + + static inline string_type pad(string_type str, std::size_t left, std::size_t right) + { + str.insert(str.begin(), left, char_type(' ')); + str.insert(str.end(), right, char_type(' ')); + return str; + } + +}; + +#if defined(_MFC_VER) +template <> +struct string_traits +{ + + typedef CString string_type; + typedef int size_type; + typedef typename CString::XCHAR char_type; + + static inline size_type length(const string_type &str) { return str.GetLength(); } + + static inline void reserve(string_type &str, size_type size) { str.Preallocate(size); } + + static inline string_type& append(string_type &str, const string_type &a) { str += a; return str; } + static inline string_type& append(string_type &str, size_type count, char_type c) { while(count--) str.AppendChar(c); return str; } + + static inline string_type pad(const string_type &str, size_type left, size_type right) + { + CString tmp; + while(left--) tmp.AppendChar(char_type(' ')); + tmp += str; + while(right--) tmp.AppendChar(char_type(' ')); + return tmp; + } + +}; +#endif + + + namespace String { template struct Traits { - static const char * GetDefaultWhitespace() { return " \n\r\t"; } + static MPT_FORCEINLINE const char * GetDefaultWhitespace() noexcept { return " \n\r\t"; } + static MPT_FORCEINLINE bool IsLineEnding(char c) noexcept { return c == '\r' || c == '\n'; } }; template <> struct Traits { - static const char * GetDefaultWhitespace() { return " \n\r\t"; } + static MPT_FORCEINLINE const char * GetDefaultWhitespace() noexcept { return " \n\r\t"; } + static MPT_FORCEINLINE bool IsLineEnding(char c) noexcept { return c == '\r' || c == '\n'; } }; +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct Traits { - static const wchar_t * GetDefaultWhitespace() { return L" \n\r\t"; } + static MPT_FORCEINLINE const wchar_t * GetDefaultWhitespace() noexcept { return L" \n\r\t"; } + static MPT_FORCEINLINE bool IsLineEnding(wchar_t c) noexcept { return c == L'\r' || c == L'\n'; } }; +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR // Remove whitespace at start of string @@ -123,9 +200,6 @@ static inline std::size_t strnlen(const char *str, std::size_t n) enum Charset { -#if defined(MPT_ENABLE_CHARSET_LOCALE) - CharsetLocale, // CP_ACP on windows, current C locale otherwise -#endif CharsetUTF8, @@ -140,6 +214,10 @@ enum Charset { CharsetWindows1252, +#if defined(MPT_ENABLE_CHARSET_LOCALE) + CharsetLocale, // CP_ACP on windows, current C locale otherwise +#endif // MPT_ENABLE_CHARSET_LOCALE + }; @@ -173,14 +251,38 @@ bool IsUTF8(const std::string &str); #define MPT_WSTRING(x) std::wstring( L ## x ) -#if MPT_ENABLE_U8STRING - template struct charset_char_traits : std::char_traits { static mpt::Charset charset() { return charset_tag; } }; #define MPT_ENCODED_STRING_TYPE(charset) std::basic_string< char, mpt::charset_char_traits< charset > > + +#if defined(MPT_ENABLE_CHARSET_LOCALE) + +typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetLocale) lstring; + +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS + +template struct windows_char_traits { }; +template <> struct windows_char_traits { typedef mpt::lstring string_type; }; +template <> struct windows_char_traits { typedef std::wstring string_type; }; + +#ifdef UNICODE +typedef windows_char_traits::string_type tstring; +#else +typedef windows_char_traits::string_type tstring; +#endif + +typedef mpt::tstring winstring; + +#endif // MPT_OS_WINDOWS + + +#if MPT_ENABLE_U8STRING + typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetUTF8) u8string; #define MPT_U8CHAR_TYPE char @@ -214,6 +316,9 @@ static inline std::wstring ToWide(const std::wstring &str) { return str; } static inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } std::wstring ToWide(Charset from, const std::string &str); static inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::wstring ToWide(const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE #endif // Convert to a string encoded in the 'to'-specified character set. @@ -228,6 +333,31 @@ static inline std::string ToCharset(Charset to, const wchar_t * str) { return To #endif std::string ToCharset(Charset to, Charset from, const std::string &str); static inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::string ToCharset(Charset to, const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +#if MPT_WSTRING_CONVERT +mpt::lstring ToLocale(const std::wstring &str); +static inline mpt::lstring ToLocale(const wchar_t * str) { return ToLocale(str ? std::wstring(str): std::wstring()); } +#endif +mpt::lstring ToLocale(Charset from, const std::string &str); +static inline mpt::lstring ToLocale(Charset from, const char * str) { return ToLocale(from, str ? std::string(str): std::string()); } +static inline mpt::lstring ToLocale(const mpt::lstring &str) { return str; } +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS +#if MPT_WSTRING_CONVERT +mpt::winstring ToWin(const std::wstring &str); +static inline mpt::winstring ToWin(const wchar_t * str) { return ToWin(str ? std::wstring(str): std::wstring()); } +#endif +mpt::winstring ToWin(Charset from, const std::string &str); +static inline mpt::winstring ToWin(Charset from, const char * str) { return ToWin(from, str ? std::string(str): std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::winstring ToWin(const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#endif // MPT_OS_WINDOWS #if defined(_MFC_VER) @@ -243,6 +373,13 @@ CString ToCString(const std::wstring &str); static inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); } CString ToCString(Charset from, const std::string &str); static inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +CString ToCString(const mpt::lstring &str); +mpt::lstring ToLocale(const CString &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const CString &str); +#endif // MPT_OS_WINDOWS // Convert from a MFC CString. The CString encoding depends on UNICODE. // This should also be used when converting from TCHAR strings. @@ -250,22 +387,6 @@ static inline CString ToCString(Charset from, const char * str) { return ToCStri std::wstring ToWide(const CString &str); std::string ToCharset(Charset to, const CString &str); -#ifdef UNICODE -MPT_DEPRECATED static inline CString ToCStringW(const CString &str) { return ToCString(str); } -MPT_DEPRECATED static inline CString ToCStringW(const std::wstring &str) { return ToCString(str); } -MPT_DEPRECATED static inline CString ToCStringW(Charset from, const std::string &str) { return ToCString(from, str); } -MPT_DEPRECATED static inline CString ToCStringW(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); } -#else // !UNICODE -CStringW ToCStringW(const CString &str); -CStringW ToCStringW(const std::wstring &str); -CStringW ToCStringW(Charset from, const std::string &str); -static inline CStringW ToCStringW(Charset from, const char * str) { return ToCStringW(from, str ? std::string(str) : std::string()); } -CStringW ToCStringW(const CStringW &str); -std::wstring ToWide(const CStringW &str); -std::string ToCharset(Charset to, const CStringW &str); -CString ToCString(const CStringW &str); -#endif // UNICODE - #endif // MFC @@ -316,6 +437,10 @@ typedef mpt::u8string ustring; #endif // MPT_USTRING_MODE_UTF8 +#define UC_(x) MPT_UCHAR(x) +#define UL_(x) MPT_ULITERAL(x) +#define U_(x) MPT_USTRING(x) + #if MPT_USTRING_MODE_WIDE #if !(MPT_WSTRING_CONVERT) #error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)" @@ -324,11 +449,11 @@ static inline mpt::ustring ToUnicode(const std::wstring &str) { return str; } static inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } static inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); } static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +static inline mpt::ustring ToUnicode(const mpt::lstring &str) { return ToWide(str); } +#endif // MPT_ENABLE_CHARSET_LOCALE #if defined(_MFC_VER) static inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); } -#ifndef UNICODE -static inline mpt::ustring ToUnicode(const CStringW &str) { return ToWide(str); } -#endif // !UNICODE #endif // MFC #else // !MPT_USTRING_MODE_WIDE static inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; } @@ -338,11 +463,11 @@ static inline mpt::ustring ToUnicode(const wchar_t * str) { return ToUnicode(str #endif mpt::ustring ToUnicode(Charset from, const std::string &str); static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::ustring ToUnicode(const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE #if defined(_MFC_VER) mpt::ustring ToUnicode(const CString &str); -#ifndef UNICODE -mpt::ustring ToUnicode(const CStringW &str); -#endif // !UNICODE #endif // MFC #endif // MPT_USTRING_MODE_WIDE @@ -356,13 +481,14 @@ mpt::ustring ToUnicode(const CStringW &str); std::wstring ToWide(const mpt::ustring &str); #endif std::string ToCharset(Charset to, const mpt::ustring &str); +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::lstring ToLocale(const mpt::ustring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const mpt::ustring &str); +#endif // MPT_OS_WINDOWS #if defined(_MFC_VER) CString ToCString(const mpt::ustring &str); -#ifdef UNICODE -MPT_DEPRECATED static inline CString ToCStringW(const mpt::ustring &str) { return ToCString(str); } -#else // !UNICODE -static inline CStringW ToCStringW(const mpt::ustring &str) { return ToCStringW(ToWide(str)); } -#endif // UNICODE #endif // MFC #endif // MPT_USTRING_MODE_WIDE @@ -377,143 +503,7 @@ static inline CStringW ToCStringW(const mpt::ustring &str) { return ToCStringW(T -#ifdef MODPLUG_TRACKER - -#if MPT_OS_WINDOWS - -namespace String { namespace detail -{ - - template - inline mpt::ustring StringFromBuffer(const char (&buf)[size]) - { - STATIC_ASSERT(size > 0); - std::size_t len = std::find(buf, buf + size, '\0') - buf; // terminate at \0 - return mpt::ToUnicode(charset, std::string(buf, buf + len)); - } - - template - inline mpt::ustring StringFromBuffer(const wchar_t (&buf)[size]) - { - STATIC_ASSERT(size > 0); - std::size_t len = std::find(buf, buf + size, L'\0') - buf; // terminate at \0 - return mpt::ToUnicode(std::wstring(buf, buf + len)); - } - - template - inline bool StringToBuffer(char (&buf)[size], const mpt::ustring &str) - { - STATIC_ASSERT(size > 0); - MemsetZero(buf); - std::string encoded = mpt::ToCharset(charset, str); - std::copy(encoded.data(), encoded.data() + std::min(encoded.length(), size - 1), buf); - buf[size - 1] = '\0'; - return (encoded.length() <= size - 1); - } - - template - inline bool StringToBuffer(wchar_t (&buf)[size], const mpt::ustring &str) - { - STATIC_ASSERT(size > 0); - MemsetZero(buf); - std::wstring encoded = mpt::ToWide(str); - std::copy(encoded.data(), encoded.data() + std::min(encoded.length(), size - 1), buf); - buf[size - 1] = L'\0'; - return (encoded.length() <= size - 1); - } - -} } // namespace String::detail - -// mpt::FromTcharBuf -// A lot of expecially older WinAPI functions return strings by filling in -// fixed-width TCHAR arrays inside some struct. -// mpt::FromTcharBuf converts these string to mpt::ustring regardless of whether -// in ANSI or UNICODE build and properly handles potentially missing NULL -// termination. - -template -inline mpt::ustring FromTcharBuf(const Tchar (&buf)[size]) -{ - return mpt::String::detail::StringFromBuffer(buf); -} - -// mpt::FromTcharStr -// Converts TCHAR strings to mpt::ustring in both ANSI and UNICODE builds. -// Useful when going through CString is not appropriate. - -template mpt::ustring FromTcharStr(const Tchar *str); -template <> inline mpt::ustring FromTcharStr(const char *str) -{ - if(!str) - { - return mpt::ustring(); - } - return mpt::ToUnicode(mpt::CharsetLocale, std::string(str)); -} -template <> inline mpt::ustring FromTcharStr(const wchar_t *str) -{ - if(!str) - { - return mpt::ustring(); - } - return mpt::ToUnicode(std::wstring(str)); -} - -// mpt::ToTcharBuf -// The inverse of mpt::FromTcharBuf. -// Always NULL-terminates the buffer. -// Return false if the string has been truncated to fit. - -template -inline bool ToTcharBuf(Tchar (&buf)[size], const mpt::ustring &str) -{ - return mpt::String::detail::StringToBuffer(buf, str); -} - -// mpt::ToTcharStr -// Converts mpt::ustring to std::basic_stringy, -// which is usable in both ANSI and UNICODE builds. -// Useful when going through CString is not appropriate. - -template std::basic_string ToTcharStrImpl(const mpt::ustring &str); -template <> inline std::string ToTcharStrImpl(const mpt::ustring &str) -{ - return mpt::ToCharset(mpt::CharsetLocale, str); -} -template <> inline std::wstring ToTcharStrImpl(const mpt::ustring &str) -{ - return mpt::ToWide(str); -} - -inline std::basic_string ToTcharStr(const mpt::ustring &str) -{ - return ToTcharStrImpl(str); -} - -#if defined(_MFC_VER) - -template -inline CString CStringFromBuffer(const TCHAR (&buf)[size]) -{ - MPT_STATIC_ASSERT(size > 0); - std::size_t len = std::find(buf, buf + size, _T('\0')) - buf; // terminate at \0 - return CString(buf, len); -} - -template -inline void CopyCStringToBuffer(TCHAR (&buf)[size], const CString &str) -{ - MPT_STATIC_ASSERT(size > 0); - MemsetZero(buf); - std::copy(str.GetString(), str.GetString() + std::min(static_cast(str.GetLength()), size - 1), buf); - buf[size - 1] = _T('\0'); -} - -#endif // _MFC_VER - -#endif // MPT_OS_WINDOWS - -#endif // MODPLUG_TRACKER +mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str); @@ -587,6 +577,11 @@ public: BasicAnyString(const char *str) : mpt::ustring(From8bit(str ? str : std::string())) { } BasicAnyString(const std::string str) : mpt::ustring(From8bit(str)) { } + // locale +#if defined(MPT_ENABLE_CHARSET_LOCALE) + BasicAnyString(const mpt::lstring str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif // MPT_ENABLE_CHARSET_LOCALE + // unicode BasicAnyString(const mpt::ustring &str) : mpt::ustring(str) { } BasicAnyString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } @@ -600,9 +595,6 @@ public: // mfc #if defined(_MFC_VER) BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#ifndef UNICODE - BasicAnyString(const CStringW &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#endif #endif // fallback for custom string types @@ -617,6 +609,11 @@ class AnyUnicodeString : public mpt::ustring public: + // locale +#if defined(MPT_ENABLE_CHARSET_LOCALE) + AnyUnicodeString(const mpt::lstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif // MPT_ENABLE_CHARSET_LOCALE + // unicode AnyUnicodeString(const mpt::ustring &str) : mpt::ustring(str) { } AnyUnicodeString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } @@ -630,9 +627,6 @@ public: // mfc #if defined(_MFC_VER) AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#ifndef UNICODE - AnyUnicodeString(const CStringW &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#endif #endif // fallback for custom string types diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.cpp new file mode 100644 index 000000000..7740e7d7e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.cpp @@ -0,0 +1,117 @@ +/* + * mptStringBuffer.cpp + * ------------------- + * Purpose: Various functions for "fixing" char array strings for writing to or + * reading from module files, or for securing char arrays in general. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "mptStringBuffer.h" + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + +namespace String +{ + +namespace detail +{ + +std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize) +{ + + std::string dest; + const char *src = srcBuffer; + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // We assume that the last character of the source buffer is null. + if(srcSize > 0) + { + srcSize -= 1; + } + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + + // Copy null-terminated string, stopping at null. + dest.assign(src, std::find(src, src + srcSize, '\0')); + + } else if(mode == spacePadded || mode == spacePaddedNull) + { + + // Copy string over. + dest.assign(src, src + srcSize); + + // Convert null characters to spaces. + std::transform(dest.begin(), dest.end(), dest.begin(), [] (char c) -> char { return (c != '\0') ? c : ' '; }); + + // Trim trailing spaces. + dest = mpt::String::RTrim(dest, std::string(" ")); + + } + + return dest; + +} + +void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::size_t srcSize) +{ + + MPT_ASSERT(destSize > 0); + + const size_t maxSize = std::min(destSize, srcSize); + char *dst = destBuffer; + const char *src = srcBuffer; + + // First, copy over null-terminated string. + size_t pos = maxSize; + while(pos > 0) + { + if((*dst = *src) == '\0') + { + break; + } + pos--; + dst++; + src++; + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + // Fill rest of string with nulls. + std::fill(dst, dst + destSize - maxSize + pos, '\0'); + } else if(mode == spacePadded || mode == spacePaddedNull) + { + // Fill the rest of the destination string with spaces. + std::fill(dst, dst + destSize - maxSize + pos, ' '); + } + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // Make sure that destination is really null-terminated. + SetNullTerminator(destBuffer, destSize); + } + +} + +} // namespace detail + +} // namespace String + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h new file mode 100644 index 000000000..cb1dc426e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h @@ -0,0 +1,692 @@ +/* + * mptStringBuffer.h + * ----------------- + * Purpose: Various functions for "fixing" char array strings for writing to or + * reading from module files, or for securing char arrays in general. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "mptString.h" + +#include +#include +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + + +namespace String +{ + + + enum ReadWriteMode + { + // Reading / Writing: Standard null-terminated string handling. + nullTerminated, + // Reading: Source string is not guaranteed to be null-terminated (if it fills the whole char array). + // Writing: Destination string is not guaranteed to be null-terminated (if it fills the whole char array). + maybeNullTerminated, + // Reading: String may contain null characters anywhere. They should be treated as spaces. + // Writing: A space-padded string is written. + spacePadded, + // Reading: String may contain null characters anywhere. The last character is ignored (it is supposed to be 0). + // Writing: A space-padded string with a trailing null is written. + spacePaddedNull + }; + + namespace detail + { + + std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize); + + void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::size_t srcSize); + + } // namespace detail + + +} // namespace String + + + +template +class StringBufRefImpl +{ +private: + Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit StringBufRefImpl(Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + MPT_STATIC_ASSERT(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + MPT_ASSERT(size > 0); + } + StringBufRefImpl(const StringBufRefImpl &) = delete; + StringBufRefImpl(StringBufRefImpl &&) = default; + StringBufRefImpl & operator = (const StringBufRefImpl &) = delete; + StringBufRefImpl & operator = (StringBufRefImpl &&) = delete; + operator Tstring () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return Tstring(buf, buf + len); + } + StringBufRefImpl & operator = (const Tstring & str) + { + std::fill(buf, buf + size, Tchar('\0')); + std::copy(str.data(), str.data() + std::min(str.length(), size - 1), buf); + std::fill(buf + std::min(str.length(), size - 1), buf + size, Tchar('\0')); + return *this; + } +}; + +template +class StringBufRefImpl +{ +private: + const Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit StringBufRefImpl(const Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + MPT_STATIC_ASSERT(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + MPT_ASSERT(size > 0); + } + StringBufRefImpl(const StringBufRefImpl &) = delete; + StringBufRefImpl(StringBufRefImpl &&) = default; + StringBufRefImpl & operator = (const StringBufRefImpl &) = delete; + StringBufRefImpl & operator = (StringBufRefImpl &&) = delete; + operator Tstring () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return Tstring(buf, buf + len); + } +}; + +namespace String { +template +inline StringBufRefImpl::type> ReadTypedBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>(buf, size); +} +template +inline StringBufRefImpl::type> ReadTypedBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>(buf, size); +} +template +inline StringBufRefImpl WriteTypedBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl(buf, size); +} +template +inline StringBufRefImpl WriteTypedBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl(buf, size); +} +} // namespace String + +namespace String { +template +inline StringBufRefImpl::type>, typename std::add_const::type> ReadAutoBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>, typename std::add_const::type> ReadAutoBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>, Tchar> WriteAutoBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>, Tchar>(buf, size); +} +template +inline StringBufRefImpl::type>, Tchar> WriteAutoBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>, Tchar>(buf, size); +} +} // namespace String + +template +class StringModeBufRefImpl +{ +private: + Tchar * buf; + std::size_t size; + String::ReadWriteMode mode; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + StringModeBufRefImpl(Tchar * buf, std::size_t size, String::ReadWriteMode mode) + : buf(buf) + , size(size) + , mode(mode) + { + MPT_STATIC_ASSERT(sizeof(Tchar) == 1); + MPT_ASSERT(size > 0); + } + StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl(StringModeBufRefImpl &&) = default; + StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete; + operator std::string () const + { + return String::detail::ReadStringBuffer(mode, buf, size); + } + StringModeBufRefImpl & operator = (const std::string & str) + { + String::detail::WriteStringBuffer(mode, buf, size, str.data(), str.size()); + return *this; + } +}; + +template +class StringModeBufRefImpl +{ +private: + const Tchar * buf; + std::size_t size; + String::ReadWriteMode mode; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + StringModeBufRefImpl(const Tchar * buf, std::size_t size, String::ReadWriteMode mode) + : buf(buf) + , size(size) + , mode(mode) + { + MPT_STATIC_ASSERT(sizeof(Tchar) == 1); + MPT_ASSERT(size > 0); + } + StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl(StringModeBufRefImpl &&) = default; + StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete; + operator std::string () const + { + return String::detail::ReadStringBuffer(mode, buf, size); + } +}; + +namespace String { +template +inline StringModeBufRefImpl::type> ReadBuf(String::ReadWriteMode mode, Tchar (&buf)[size]) +{ + return StringModeBufRefImpl::type>(buf, size, mode); +} +template +inline StringModeBufRefImpl::type> ReadBuf(String::ReadWriteMode mode, Tchar * buf, std::size_t size) +{ + return StringModeBufRefImpl::type>(buf, size, mode); +} +template +inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar (&buf)[size]) +{ + return StringModeBufRefImpl(buf, size, mode); +} +template +inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar * buf, std::size_t size) +{ + return StringModeBufRefImpl(buf, size, mode); +} +} // namespace String + + +#ifdef MODPLUG_TRACKER + +#if MPT_OS_WINDOWS + +namespace String { +template +inline StringBufRefImpl::type>::string_type, typename std::add_const::type> ReadWinBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>::string_type, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>::string_type, typename std::add_const::type> ReadWinBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>::string_type, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>::string_type, Tchar> WriteWinBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>::string_type, Tchar>(buf, size); +} +template +inline StringBufRefImpl::type>::string_type, Tchar> WriteWinBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>::string_type, Tchar>(buf, size); +} +} // namespace String + +#if defined(_MFC_VER) + +template +class CStringBufRefImpl +{ +private: + Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit CStringBufRefImpl(Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + MPT_ASSERT(size > 0); + } + CStringBufRefImpl(const CStringBufRefImpl &) = delete; + CStringBufRefImpl(CStringBufRefImpl &&) = default; + CStringBufRefImpl & operator = (const CStringBufRefImpl &) = delete; + CStringBufRefImpl & operator = (CStringBufRefImpl &&) = delete; + operator CString () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return CString(buf, mpt::saturate_cast(len)); + } + CStringBufRefImpl & operator = (const CString & str) + { + std::fill(buf, buf + size, Tchar('\0')); + std::copy(str.GetString(), str.GetString() + std::min(static_cast(str.GetLength()), size - 1), buf); + buf[size - 1] = Tchar('\0'); + return *this; + } +}; + +template +class CStringBufRefImpl +{ +private: + const Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit CStringBufRefImpl(const Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + MPT_ASSERT(size > 0); + } + CStringBufRefImpl(const CStringBufRefImpl &) = delete; + CStringBufRefImpl(CStringBufRefImpl &&) = default; + CStringBufRefImpl & operator = (const CStringBufRefImpl &) = delete; + CStringBufRefImpl & operator = (CStringBufRefImpl &&) = delete; + operator CString () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return CString(buf, mpt::saturate_cast(len)); + } +}; + +namespace String { +template +inline CStringBufRefImpl::type> ReadCStringBuf(Tchar (&buf)[size]) +{ + return CStringBufRefImpl::type>(buf, size); +} +template +inline CStringBufRefImpl::type> ReadCStringBuf(Tchar * buf, std::size_t size) +{ + return CStringBufRefImpl::type>(buf, size); +} +template +inline CStringBufRefImpl WriteCStringBuf(Tchar (&buf)[size]) +{ + return CStringBufRefImpl(buf, size); +} +template +inline CStringBufRefImpl WriteCStringBuf(Tchar * buf, std::size_t size) +{ + return CStringBufRefImpl(buf, size); +} +} // namespace String + +#endif // _MFC_VER + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + + + + +namespace String +{ + + +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif // MPT_COMPILER_MSVC + + + // Sets last character to null in given char array. + // Size of the array must be known at compile time. + template + void SetNullTerminator(char (&buffer)[size]) + { + STATIC_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + inline void SetNullTerminator(char *buffer, size_t size) + { + MPT_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + template + void SetNullTerminator(wchar_t (&buffer)[size]) + { + STATIC_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + inline void SetNullTerminator(wchar_t *buffer, size_t size) + { + MPT_ASSERT(size > 0); + buffer[size - 1] = 0; + } + + + // Remove any chars after the first null char + template + void FixNullString(char (&buffer)[size]) + { + STATIC_ASSERT(size > 0); + SetNullTerminator(buffer); + size_t pos = 0; + // Find the first null char. + while(pos < size && buffer[pos] != '\0') + { + pos++; + } + // Remove everything after the null char. + while(pos < size) + { + buffer[pos++] = '\0'; + } + } + + inline void FixNullString(std::string & str) + { + for(std::size_t i = 0; i < str.length(); ++i) + { + if(str[i] == '\0') + { + // if we copied \0 in the middle of the buffer, terminate std::string here + str.resize(i); + break; + } + } + } + + + // Copy a string from srcBuffer to destBuffer using a given read mode. + // Used for reading strings from files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Read(std::string &dest, const Tbyte *srcBuffer, size_t srcSize) + { + + const char *src = mpt::byte_cast(srcBuffer); + + dest.clear(); + + try + { + dest = mpt::String::detail::ReadStringBuffer(mode, src, srcSize); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + } + + // Copy a charset encoded string from srcBuffer to destBuffer using a given read mode. + // Used for reading strings from files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte *srcBuffer, size_t srcSize) + { + std::string tmp; + Read(tmp, srcBuffer, srcSize); + dest = mpt::ToUnicode(charset, tmp); + } + + // Used for reading strings from files. + // Preferrably use this version of the function, it is safer. + template + void Read(std::string &dest, const Tbyte (&srcBuffer)[srcSize]) + { + STATIC_ASSERT(srcSize > 0); + Read(dest, srcBuffer, srcSize); + } + + // Used for reading charset encoded strings from files. + // Preferrably use this version of the function, it is safer. + template + void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte(&srcBuffer)[srcSize]) + { + std::string tmp; + Read(tmp, srcBuffer); + dest = mpt::ToUnicode(charset, tmp); + } + + // Copy a string from srcBuffer to destBuffer using a given read mode. + // Used for reading strings from files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Read(char (&destBuffer)[destSize], const Tbyte *srcBuffer, size_t srcSize) + { + STATIC_ASSERT(destSize > 0); + + char *dst = destBuffer; + const char *src = mpt::byte_cast(srcBuffer); + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // We assume that the last character of the source buffer is null. + if(srcSize > 0) + { + srcSize -= 1; + } + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + + // Copy string and leave one character space in the destination buffer for null. + dst = std::copy(src, std::find(src, src + std::min(srcSize, destSize - 1), '\0'), dst); + + } else if(mode == spacePadded || mode == spacePaddedNull) + { + + // Copy string and leave one character space in the destination buffer for null. + // Convert nulls to spaces while copying and counts the length that contains actual characters. + std::size_t lengthWithoutNullOrSpace = 0; + for(std::size_t pos = 0; pos < srcSize; ++pos) + { + char c = srcBuffer[pos]; + if(c != '\0' && c != ' ') + { + lengthWithoutNullOrSpace = pos + 1; + } + if(c == '\0') + { + c = ' '; + } + if(pos < destSize - 1) + { + destBuffer[pos] = c; + } + } + + std::size_t destLength = std::min(lengthWithoutNullOrSpace, destSize - 1); + + dst += destLength; + + } + + // Fill rest of string with nulls. + std::fill(dst, destBuffer + destSize, '\0'); + + } + + // Used for reading strings from files. + // Preferrably use this version of the function, it is safer. + template + void Read(char (&destBuffer)[destSize], const Tbyte (&srcBuffer)[srcSize]) + { + STATIC_ASSERT(destSize > 0); + STATIC_ASSERT(srcSize > 0); + Read(destBuffer, srcBuffer, srcSize); + } + + + // Copy a string from srcBuffer to destBuffer using a given write mode. + // You should only use this function if src and dest are dynamically sized, + // otherwise use one of the safer overloads below. + template + void Write(char *destBuffer, const size_t destSize, const char *srcBuffer, const size_t srcSize) + { + MPT_ASSERT(destSize > 0); + + mpt::String::detail::WriteStringBuffer(mode, destBuffer, destSize, srcBuffer, srcSize); + + } + + // Copy a string from srcBuffer to a dynamically sized std::vector destBuffer using a given write mode. + // Used for writing strings to files. + // Only use this version of the function if the size of the source buffer is variable and the destination buffer also has variable size. + template + void Write(std::vector &destBuffer, const char *srcBuffer, const size_t srcSize) + { + MPT_ASSERT(destBuffer.size() > 0); + Write(destBuffer.data(), destBuffer.size(), srcBuffer, srcSize); + } + + // Copy a string from srcBuffer to destBuffer using a given write mode. + // Used for writing strings to files. + // Only use this version of the function if the size of the source buffer is variable. + template + void Write(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize) + { + STATIC_ASSERT(destSize > 0); + Write(destBuffer, destSize, srcBuffer, srcSize); + } + + // Copy a string from srcBuffer to destBuffer using a given write mode. + // Used for writing strings to files. + // Preferrably use this version of the function, it is safer. + template + void Write(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize]) + { + STATIC_ASSERT(destSize > 0); + STATIC_ASSERT(srcSize > 0); + Write(destBuffer, srcBuffer, srcSize); + } + + template + void Write(char *destBuffer, const size_t destSize, const std::string &src) + { + MPT_ASSERT(destSize > 0); + Write(destBuffer, destSize, src.c_str(), src.length()); + } + + template + void Write(std::vector &destBuffer, const std::string &src) + { + MPT_ASSERT(destBuffer.size() > 0); + Write(destBuffer, src.c_str(), src.length()); + } + + template + void Write(char (&destBuffer)[destSize], const std::string &src) + { + STATIC_ASSERT(destSize > 0); + Write(destBuffer, src.c_str(), src.length()); + } + + + // Copy from a char array to a fixed size char array. + template + void CopyN(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize = std::numeric_limits::max()) + { + const size_t copySize = std::min(destSize - 1u, srcSize); + std::strncpy(destBuffer, srcBuffer, copySize); + destBuffer[copySize] = '\0'; + } + + // Copy at most srcSize characters from srcBuffer to a std::string. + static inline void CopyN(std::string &dest, const char *srcBuffer, const size_t srcSize = std::numeric_limits::max()) + { + dest.assign(srcBuffer, srcBuffer + mpt::strnlen(srcBuffer, srcSize)); + } + + + // Copy from one fixed size char array to another one. + template + void Copy(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize]) + { + CopyN(destBuffer, srcBuffer, srcSize); + } + + // Copy from a std::string to a fixed size char array. + template + void Copy(char (&destBuffer)[destSize], const std::string &src) + { + CopyN(destBuffer, src.c_str(), src.length()); + } + + // Copy from a fixed size char array to a std::string. + template + void Copy(std::string &dest, const char (&srcBuffer)[srcSize]) + { + CopyN(dest, srcBuffer, srcSize); + } + + // Copy from a std::string to a std::string. + static inline void Copy(std::string &dest, const std::string &src) + { + dest.assign(src); + } + + +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + + +} // namespace String + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp index cbf21e0d5..f77ef17fb 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp @@ -10,10 +10,22 @@ #include "stdafx.h" #include "mptStringFormat.h" +#if MPT_CXX_AT_LEAST(17) && MPT_COMPILER_MSVC +#define MPT_FORMAT_CXX17_INT 1 +#else +#define MPT_FORMAT_CXX17_INT 0 +#endif + +#if MPT_FORMAT_CXX17_INT +#include +#endif // MPT_FORMAT_CXX17_INT #include #include #include #include +#if MPT_FORMAT_CXX17_INT +#include +#endif // MPT_FORMAT_CXX17_INT OPENMPT_NAMESPACE_BEGIN @@ -24,14 +36,65 @@ namespace mpt { + template inline void SaneInsert(Tstream & s, const T & x) { s << x; } // do the right thing for signed/unsigned char and bool -template void SaneInsert(Tstream & s, const bool & x) { s << static_cast(x); } -template void SaneInsert(Tstream & s, const signed char & x) { s << static_cast(x); } -template void SaneInsert(Tstream & s, const unsigned char & x) { s << static_cast(x); } +template inline void SaneInsert(Tstream & s, const bool & x) { s << static_cast(x); } +template inline void SaneInsert(Tstream & s, const signed char & x) { s << static_cast(x); } +template inline void SaneInsert(Tstream & s, const unsigned char & x) { s << static_cast(x); } +#if MPT_FORMAT_CXX17_INT + +#if MPT_WSTRING_FORMAT +std::wstring ToWideSimple(const std::string &nstr) +{ + std::wstring wstr(nstr.size(), L'\0'); + for(std::size_t i = 0; i < nstr.size(); ++i) + { + wstr[i] = static_cast(nstr[i]); + } + return wstr; +} +#endif // MPT_WSTRING_FORMAT + template -inline std::string ToStringHelper(const T & x) +static inline std::string ToChars(const T & x, int base = 10) +{ + std::string str(1, '\0'); + bool done = false; + while(!done) + { + std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, base); + if(result.ec != std::errc{}) + { + str.resize(Util::ExponentialGrow(str.size()), '\0'); + } else + { + str.resize(result.ptr - str.data()); + done = true; + } + } + return str; +} + +template +static inline std::string ToStringHelperInt(const T & x) +{ + return ToChars(x); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring ToWStringHelperInt(const T & x) +{ + return ToWideSimple(ToChars(x)); +} +#endif + +#else // !MPT_FORMAT_CXX17_INT + +template +static inline std::string ToStringHelperInt(const T & x) { std::ostringstream o; o.imbue(std::locale::classic()); @@ -41,7 +104,29 @@ inline std::string ToStringHelper(const T & x) #if MPT_WSTRING_FORMAT template -inline std::wstring ToWStringHelper(const T & x) +static inline std::wstring ToWStringHelperInt(const T & x) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} +#endif + +#endif // MPT_FORMAT_CXX17_INT + +template +static inline std::string ToStringHelperFloat(const T & x) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring ToWStringHelperFloat(const T & x) { std::wostringstream o; o.imbue(std::locale::classic()); @@ -61,20 +146,20 @@ std::string ToString(const mpt::ustring & x) { return mpt::ToCharset(mpt::Charse #if defined(_MFC_VER) std::string ToString(const CString & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } #endif -std::string ToString(const bool & x) { return ToStringHelper(x); } -std::string ToString(const signed char & x) { return ToStringHelper(x); } -std::string ToString(const unsigned char & x) { return ToStringHelper(x); } -std::string ToString(const signed short & x) { return ToStringHelper(x); } -std::string ToString(const unsigned short & x) { return ToStringHelper(x); } -std::string ToString(const signed int & x) { return ToStringHelper(x); } -std::string ToString(const unsigned int & x) { return ToStringHelper(x); } -std::string ToString(const signed long & x) { return ToStringHelper(x); } -std::string ToString(const unsigned long & x) { return ToStringHelper(x); } -std::string ToString(const signed long long & x) { return ToStringHelper(x); } -std::string ToString(const unsigned long long & x) { return ToStringHelper(x); } -std::string ToString(const float & x) { return ToStringHelper(x); } -std::string ToString(const double & x) { return ToStringHelper(x); } -std::string ToString(const long double & x) { return ToStringHelper(x); } +std::string ToString(const bool & x) { return ToStringHelperInt(static_cast(x)); } +std::string ToString(const signed char & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned char & x) { return ToStringHelperInt(x); } +std::string ToString(const signed short & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned short & x) { return ToStringHelperInt(x); } +std::string ToString(const signed int & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned int & x) { return ToStringHelperInt(x); } +std::string ToString(const signed long & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned long & x) { return ToStringHelperInt(x); } +std::string ToString(const signed long long & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned long long & x) { return ToStringHelperInt(x); } +std::string ToString(const float & x) { return ToStringHelperFloat(x); } +std::string ToString(const double & x) { return ToStringHelperFloat(x); } +std::string ToString(const long double & x) { return ToStringHelperFloat(x); } mpt::ustring ToUString(const std::string & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, x); } mpt::ustring ToUString(const char * const & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, x); } @@ -90,36 +175,36 @@ mpt::ustring ToUString(const wchar_t & x) { return mpt::ToUnicode(std::wstring(1 mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); } #endif #if MPT_USTRING_MODE_WIDE -mpt::ustring ToUString(const bool & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const signed char & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const unsigned char & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const signed short & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const unsigned short & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const signed int & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const unsigned int & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const signed long & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const unsigned long & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const signed long long & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const unsigned long long & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const float & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const double & x) { return ToWStringHelper(x); } -mpt::ustring ToUString(const long double & x) { return ToWStringHelper(x); } +mpt::ustring ToUString(const bool & x) { return ToWStringHelperInt(static_cast(x)); } +mpt::ustring ToUString(const signed char & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned char & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed short & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned short & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed int & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned int & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed long long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned long long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const float & x) { return ToWStringHelperFloat(x); } +mpt::ustring ToUString(const double & x) { return ToWStringHelperFloat(x); } +mpt::ustring ToUString(const long double & x) { return ToWStringHelperFloat(x); } #endif #if MPT_USTRING_MODE_UTF8 -mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } -mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); } +mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(static_cast(x))); } +mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperFloat(x)); } #endif #if MPT_WSTRING_FORMAT @@ -132,26 +217,54 @@ std::wstring ToWString(const mpt::ustring & x) { return mpt::ToWide(x); } #if defined(_MFC_VER) std::wstring ToWString(const CString & x) { return mpt::ToWide(x); } #endif -std::wstring ToWString(const bool & x) { return ToWStringHelper(x); } -std::wstring ToWString(const signed char & x) { return ToWStringHelper(x); } -std::wstring ToWString(const unsigned char & x) { return ToWStringHelper(x); } -std::wstring ToWString(const signed short & x) { return ToWStringHelper(x); } -std::wstring ToWString(const unsigned short & x) { return ToWStringHelper(x); } -std::wstring ToWString(const signed int & x) { return ToWStringHelper(x); } -std::wstring ToWString(const unsigned int & x) { return ToWStringHelper(x); } -std::wstring ToWString(const signed long & x) { return ToWStringHelper(x); } -std::wstring ToWString(const unsigned long & x) { return ToWStringHelper(x); } -std::wstring ToWString(const signed long long & x) { return ToWStringHelper(x); } -std::wstring ToWString(const unsigned long long & x) { return ToWStringHelper(x); } -std::wstring ToWString(const float & x) { return ToWStringHelper(x); } -std::wstring ToWString(const double & x) { return ToWStringHelper(x); } -std::wstring ToWString(const long double & x) { return ToWStringHelper(x); } +std::wstring ToWString(const bool & x) { return ToWStringHelperInt(static_cast(x)); } +std::wstring ToWString(const signed char & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned char & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed short & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned short & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed int & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned int & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed long long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned long long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const float & x) { return ToWStringHelperFloat(x); } +std::wstring ToWString(const double & x) { return ToWStringHelperFloat(x); } +std::wstring ToWString(const long double & x) { return ToWStringHelperFloat(x); } #endif -template -inline void ApplyFormat(Tostream & o, const FormatSpec & format) +template +struct NumPunct : std::numpunct { +private: + unsigned int group; + char sep; +public: + NumPunct(unsigned int g, char s) + : group(g) + , sep(s) + {} + std::string do_grouping() const override + { + return std::string(1, static_cast(group)); + } + Tchar do_thousands_sep() const override + { + return static_cast(sep); + } +}; + +template +static inline void ApplyFormat(Tostream & o, const FormatSpec & format, const T &) +{ + MPT_MAYBE_CONSTANT_IF(!std::numeric_limits::is_integer) + { + if(format.GetGroup() > 0) + { + o.imbue(std::locale(o.getloc(), new NumPunct(format.GetGroup(), format.GetGroupSep()))); + } + } FormatFlags f = format.GetFlags(); std::size_t width = format.GetWidth(); int precision = format.GetPrecision(); @@ -170,154 +283,255 @@ inline void ApplyFormat(Tostream & o, const FormatSpec & format) else if(f & fmt_base::NotaSci ) { o << std::setiosflags(std::ios::scientific); } if(f & fmt_base::CaseLow) { o << std::nouppercase; } else if(f & fmt_base::CaseUpp) { o << std::uppercase; } - if(f & fmt_base::FillOff) { /* nothing */ } - else if(f & fmt_base::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); } - else if(f & fmt_base::FillSpc) { o << std::setw(width) << std::setfill(typename Tostream::char_type(' ')); } + MPT_MAYBE_CONSTANT_IF(!std::numeric_limits::is_integer) + { + if(f & fmt_base::FillOff) { /* nothing */ } + else if(f & fmt_base::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); } + } if(precision != -1) { o << std::setprecision(precision); } } +template +static inline Tstring PostProcessCase(Tstring str, const FormatSpec & format) +{ + FormatFlags f = format.GetFlags(); + if(f & fmt_base::CaseUpp) + { + for(auto & c : str) + { + if('a' <= c && c <= 'z') + { + c -= 'a' - 'A'; + } + } + } + return str; +} + +template +static inline Tstring PostProcessDigits(Tstring str, const FormatSpec & format) +{ + FormatFlags f = format.GetFlags(); + std::size_t width = format.GetWidth(); + if(f & fmt_base::FillNul) + { + auto pos = str.begin(); + if(str.length() > 0) + { + if(str[0] == typename Tstring::value_type('+')) + { + pos++; + width++; + } else if(str[0] == typename Tstring::value_type('-')) + { + pos++; + width++; + } + } + if(str.length() < width) + { + str.insert(pos, width - str.length(), '0'); + } + } + return str; +} + +template +static inline Tstring PostProcessGroup(Tstring str, const FormatSpec & format) +{ + if(format.GetGroup() > 0) + { + const unsigned int groupSize = format.GetGroup(); + const char groupSep = format.GetGroupSep(); + std::size_t len = str.length(); + for(std::size_t n = 0; n < len; ++n) + { + if(n > 0 && (n % groupSize) == 0) + { + if(!(n == (len - 1) && (str[0] == typename Tstring::value_type('+') || str[0] == typename Tstring::value_type('-')))) + { + str.insert(str.begin() + (len - n), 1, groupSep); + } + } + } + } + return str; +} + +#if MPT_FORMAT_CXX17_INT template -inline std::string FormatValHelper(const T & x, const FormatSpec & f) +static inline std::string FormatValHelperInt(const T & x, const FormatSpec & f) +{ + int base = 10; + if(f.GetFlags() & fmt_base::BaseDec) { base = 10; } + if(f.GetFlags() & fmt_base::BaseHex) { base = 16; } + return PostProcessGroup(PostProcessDigits(PostProcessCase(ToChars(x, base), f), f), f); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring FormatValWHelperInt(const T & x, const FormatSpec & f) +{ + int base = 10; + if(f.GetFlags() & fmt_base::BaseDec) { base = 10; } + if(f.GetFlags() & fmt_base::BaseHex) { base = 16; } + return ToWideSimple(PostProcessGroup(PostProcessDigits(PostProcessCase(ToChars(x, base), f), f), f)); +} +#endif + +#else // !MPT_FORMAT_CXX17_INT + +template +static inline std::string FormatValHelperInt(const T & x, const FormatSpec & f) +{ + MPT_MAYBE_CONSTANT_IF((f.GetFlags() & fmt_base::BaseHex) && std::is_signed::value) + { + if(x == std::numeric_limits::min()) + { + return std::string(1, '-') + FormatValHelperInt(static_cast::type>(x), f); + } else MPT_MAYBE_CONSTANT_IF(x < 0) + { + return std::string(1, '-') + FormatValHelperInt(static_cast::type>(0-x), f); + } else + { + return FormatValHelperInt(static_cast::type>(x), f); + } + } + std::ostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f, x); + SaneInsert(o, x); + return PostProcessGroup(PostProcessDigits(o.str(), f), f); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring FormatValWHelperInt(const T & x, const FormatSpec & f) +{ + MPT_MAYBE_CONSTANT_IF((f.GetFlags() & fmt_base::BaseHex) && std::is_signed::value) + { + if(x == std::numeric_limits::min()) + { + return std::wstring(1, L'-') + FormatValWHelperInt(static_cast::type>(x), f); + } else MPT_MAYBE_CONSTANT_IF(x < 0) + { + return std::wstring(1, L'-') + FormatValWHelperInt(static_cast::type>(0-x), f); + } else + { + return FormatValWHelperInt(static_cast::type>(x), f); + } + } + std::wostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f, x); + SaneInsert(o, x); + return PostProcessGroup(PostProcessDigits(o.str(), f), f); +} +#endif + +#endif // MPT_FORMAT_CXX17_INT + +template +static inline std::string FormatValHelperFloat(const T & x, const FormatSpec & f) { std::ostringstream o; o.imbue(std::locale::classic()); - ApplyFormat(o, f); + ApplyFormat(o, f, x); SaneInsert(o, x); return o.str(); } #if MPT_WSTRING_FORMAT template -inline std::wstring FormatValWHelper(const T & x, const FormatSpec & f) +static inline std::wstring FormatValWHelperFloat(const T & x, const FormatSpec & f) { std::wostringstream o; o.imbue(std::locale::classic()); - ApplyFormat(o, f); + ApplyFormat(o, f, x); SaneInsert(o, x); return o.str(); } #endif -// Parses a useful subset of standard sprintf syntax for specifying floating point formatting. -template -static inline FormatSpec ParseFormatStringFloat(const Tchar * str) -{ - MPT_ASSERT(str); - FormatFlags f = FormatFlags(); - std::size_t width = 0; - int precision = -1; - if(!str) - { - return FormatSpec(); - } - const Tchar * p = str; - while(*p && *p != Tchar('%')) - { - ++p; - } - ++p; - while(*p && (*p == Tchar(' ') || *p == Tchar('0'))) - { - if(*p == Tchar(' ')) f |= mpt::fmt_base::FillSpc; - if(*p == Tchar('0')) f |= mpt::fmt_base::FillNul; - ++p; - } - if(!(f & mpt::fmt_base::FillSpc) && !(f & mpt::fmt_base::FillNul)) - { - f |= mpt::fmt_base::FillOff; - } - while(*p && (Tchar('0') <= *p && *p <= Tchar('9'))) - { - if(f & mpt::fmt_base::FillOff) - { - f &= ~mpt::fmt_base::FillOff; - f |= mpt::fmt_base::FillSpc; - } - width *= 10; - width += *p - Tchar('0'); - ++p; - } - if(*p && *p == Tchar('.')) - { - ++p; - precision = 0; - while(*p && (Tchar('0') <= *p && *p <= Tchar('9'))) - { - precision *= 10; - precision += *p - Tchar('0'); - ++p; - } - } - if(*p && (*p == Tchar('g') || *p == Tchar('G') || *p == Tchar('f') || *p == Tchar('F') || *p == Tchar('e') || *p == Tchar('E'))) - { - if(*p == Tchar('g')) f |= mpt::fmt_base::NotaNrm | mpt::fmt_base::CaseLow; - if(*p == Tchar('G')) f |= mpt::fmt_base::NotaNrm | mpt::fmt_base::CaseUpp; - if(*p == Tchar('f')) f |= mpt::fmt_base::NotaFix | mpt::fmt_base::CaseLow; - if(*p == Tchar('F')) f |= mpt::fmt_base::NotaFix | mpt::fmt_base::CaseUpp; - if(*p == Tchar('e')) f |= mpt::fmt_base::NotaSci | mpt::fmt_base::CaseLow; - if(*p == Tchar('E')) f |= mpt::fmt_base::NotaSci | mpt::fmt_base::CaseUpp; - ++p; - } - return FormatSpec().SetFlags(f).SetWidth(width).SetPrecision(precision); -} -FormatSpec & FormatSpec::ParsePrintf(const char * format) -{ - *this = ParseFormatStringFloat(format); - return *this; -} -FormatSpec & FormatSpec::ParsePrintf(const wchar_t * format) -{ - *this = ParseFormatStringFloat(format); - return *this; -} -FormatSpec & FormatSpec::ParsePrintf(const std::string & format) -{ - *this = ParseFormatStringFloat(format.c_str()); - return *this; -} -FormatSpec & FormatSpec::ParsePrintf(const std::wstring & format) -{ - *this = ParseFormatStringFloat(format.c_str()); - return *this; -} +std::string FormatVal(const char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +std::string FormatVal(const wchar_t & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +std::string FormatVal(const bool & x, const FormatSpec & f) { return FormatValHelperInt(static_cast(x), f); } +std::string FormatVal(const signed char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed short & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned short & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed int & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned int & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed long long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned long long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const float & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); } +std::string FormatVal(const double & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); } +std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); } - -std::string FormatVal(const char & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const wchar_t & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const bool & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const signed char & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const unsigned char & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const signed short & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const unsigned short & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const signed int & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const unsigned int & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const signed long & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const unsigned long & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const signed long long & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const unsigned long long & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const float & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const double & x, const FormatSpec & f) { return FormatValHelper(x, f); } -std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelper(x, f); } +#if MPT_USTRING_MODE_WIDE +mpt::ustring FormatValU(const char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast(x), f); } +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +#endif +#if MPT_USTRING_MODE_UTF8 +mpt::ustring FormatValU(const char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(static_cast(x), f)); } +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperFloat(x, f)); } +#endif #if MPT_WSTRING_FORMAT -std::wstring FormatValW(const char & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const bool & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const float & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const double & x, const FormatSpec & f) { return FormatValWHelper(x, f); } -std::wstring FormatValW(const long double & x, const FormatSpec & f) { return FormatValWHelper(x, f); } +std::wstring FormatValW(const char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +std::wstring FormatValW(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast(x), f); } +std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const float & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +std::wstring FormatValW(const double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +std::wstring FormatValW(const long double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } #endif @@ -340,90 +554,42 @@ Tstring PrintImplTemplate(const Tstring & format , const Tstring & x8 ) { + typedef typename mpt::string_traits traits; Tstring result; - const std::size_t len = format.length(); - result.reserve(len); - for(std::size_t pos = 0; pos != len; ++pos) + const typename traits::size_type len = traits::length(format); + traits::reserve(result, len); + for(typename traits::size_type pos = 0; pos != len; ++pos) { - typename Tstring::value_type c = format[pos]; - if(pos + 1 != len && c == '%') + typename traits::char_type c = format[pos]; + if(pos + 1 != len && c == typename traits::char_type('%')) { pos++; c = format[pos]; - if('1' <= c && c <= '9') + if(typename traits::char_type('1') <= c && c <= typename traits::char_type('9')) { - const std::size_t n = c - '0'; + const std::size_t n = c - typename traits::char_type('0'); switch(n) { - case 1: result.append(x1); break; - case 2: result.append(x2); break; - case 3: result.append(x3); break; - case 4: result.append(x4); break; - case 5: result.append(x5); break; - case 6: result.append(x6); break; - case 7: result.append(x7); break; - case 8: result.append(x8); break; + case 1: traits::append(result, x1); break; + case 2: traits::append(result, x2); break; + case 3: traits::append(result, x3); break; + case 4: traits::append(result, x4); break; + case 5: traits::append(result, x5); break; + case 6: traits::append(result, x6); break; + case 7: traits::append(result, x7); break; + case 8: traits::append(result, x8); break; } continue; - } else if(c != '%') + } else if(c != typename traits::char_type('%')) { - result.append(1, '%'); + traits::append(result, 1, typename traits::char_type('%')); } } - result.append(1, c); + traits::append(result, 1, c); } return result; } -#if defined(_MFC_VER) -template<> -CString PrintImplTemplate(const CString & format - , const CString & x1 - , const CString & x2 - , const CString & x3 - , const CString & x4 - , const CString & x5 - , const CString & x6 - , const CString & x7 - , const CString & x8 - ) -{ - CString result; - const int len = format.GetLength(); - result.Preallocate(len); - for(int pos = 0; pos != len; ++pos) - { - CString::XCHAR c = format[pos]; - if(pos + 1 != len && c == _T('%')) - { - pos++; - c = format[pos]; - if(_T('1') <= c && c <= _T('9')) - { - const std::size_t n = c - _T('0'); - switch(n) - { - case 1: result += x1; break; - case 2: result += x2; break; - case 3: result += x3; break; - case 4: result += x4; break; - case 5: result += x5; break; - case 6: result += x6; break; - case 7: result += x7; break; - case 8: result += x8; break; - } - continue; - } else if(c != _T('%')) - { - result.AppendChar(_T('%')); - } - } - result.AppendChar(c); - } - return result; -} -#endif - std::string PrintImpl(const std::string & format , const std::string & x1 , const std::string & x2 @@ -470,6 +636,22 @@ mpt::ustring PrintImpl(const mpt::ustring & format } #endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::lstring PrintImpl(const mpt::lstring & format + , const mpt::lstring & x1 + , const mpt::lstring & x2 + , const mpt::lstring & x3 + , const mpt::lstring & x4 + , const mpt::lstring & x5 + , const mpt::lstring & x6 + , const mpt::lstring & x7 + , const mpt::lstring & x8 + ) +{ + return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); +} +#endif // MPT_ENABLE_CHARSET_LOCALE + #if defined(_MFC_VER) CString PrintImpl(const CString & format , const CString & x1 diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h index 3db356def..56a5d6e15 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "mptString.h" @@ -40,14 +42,12 @@ OPENMPT_NAMESPACE_BEGIN // This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality // with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter // ordering. -// There are macro verions (MPT_FORMAT and variants) which properly use wide string literals for the format parameter. // 4. Every function is available for std::string, std::wstring and mpt::ustring. std::string makes no assumption about the encoding, which // basically means, it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding. -// std::string std::wstring mpt::ustring CString -// mpt::fmt::val mpt::wfmt::val mpt::ufmt::val mpt::tfmt::val -// mpt::FormatVal mpt::FormatValW mpt::FormatValTFunctor() mpt::FormatValTFunctor() -// mpt::fmt mpt::wfmt mpt::ufmt mpt::tfmt -// mpt::format mpt::format mpt::format mpt::format +// std::string std::wstring mpt::ustring mpt::tsrtring CString +// mpt::fmt mpt::wfmt mpt::ufmt mpt::tfmt mpt::cfmt +// mpt::format("%1") mpt::wformat(L"%1") mpt::uformat(MPT_ULITERAL(%1) mpt::tformat(_T("%1")) mpt::cformat(_T("%1")) +// mpt::format("%1") mpt::format(L"%1") mpt::format(MPT_USTRING(%1)) mpt::format(mpt::tstring(_T("%1")) mpt::format(CString(_T("%1")) // 5. All functionality here delegates real work outside of the header file so that and do not need to be included when // using this functionality. // Advantages: @@ -55,8 +55,8 @@ OPENMPT_NAMESPACE_BEGIN // - Faster compile times because and (2 very complex headers) are not included everywhere. // Disadvantages: // - Slightly more c++ code is required for delegating work. -// - As the header does not use iostreams, custom types need to overload mpt::String, mpt::ToWstring and mpt::UString instead of -// iostream operator << to allow for custom type formatting. +// - As the header does not use iostreams, custom types need to overload mpt::UString instead of iostream operator << to allow for custom type +// formatting. // - std::string, std::wstring and mpt::ustring are returned from somewhat deep cascades of helper functions. Where possible, code is // written in such a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate // almost all these copies. This should not be a problem for any decent modern compiler (and even less so for a c++11 compiler where @@ -65,12 +65,16 @@ OPENMPT_NAMESPACE_BEGIN namespace mpt { -// ToString() converts various built-in types to a well-defined, locale-independent string representation. +// ToUString() converts various built-in types to a well-defined, locale-independent string representation. // This is also used as a type-tunnel pattern for mpt::format. -// Custom types that need to be converted to strings are encouraged to overload ToString() and ToWString(). +// Custom types that need to be converted to strings are encouraged to overload ToUString(). -// fallback to member function ToString() -template auto ToString(const T & x) -> decltype(x.ToString()) { return x.ToString(); } +// fallback to member function ToUString() +#if MPT_USTRING_MODE_UTF8 +template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::CharsetUTF8, x.ToUString())) { return mpt::ToCharset(mpt::CharsetUTF8, x.ToUString()); } +#else +template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString())) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString()); } +#endif static inline std::string ToString(const std::string & x) { return x; } static inline std::string ToString(const char * const & x) { return x; } @@ -163,22 +167,18 @@ std::wstring ToWString(const unsigned long long & x); std::wstring ToWString(const float & x); std::wstring ToWString(const double & x); std::wstring ToWString(const long double & x); +// fallback to member function ToUString() +template auto ToWString(const T & x) -> decltype(mpt::ToWide(x.ToUString())) { return mpt::ToWide(x.ToUString()); } #endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template struct ToLocaleHelper { mpt::lstring operator () (const T & v) { return mpt::ToLocale(ToUString(v)); } }; +template <> struct ToLocaleHelper { mpt::lstring operator () (const mpt::lstring & v) { return v; } }; +#endif // MPT_ENABLE_CHARSET_LOCALE + #if defined(_MFC_VER) -#ifdef UNICODE -#if MPT_WSTRING_FORMAT -template static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToWString(x)); } -#else -template static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToUString(x)); } -#endif -#else -namespace detail { -template struct CstringToStdStringImpl { CString operator () (const T & v) { return mpt::ToCString(mpt::CharsetLocale, ToString(v)); } }; -template <> struct CstringToStdStringImpl { CString operator () (const CString & v) { return v; } }; -} -template static inline CString ToCStringHelper(const T & x) { return mpt::detail::CstringToStdStringImpl()(x); } -#endif +template struct ToCStringHelper { CString operator () (const T & v) { return mpt::ToCString(ToUString(v)); } }; +template <> struct ToCStringHelper { CString operator () (const CString & v) { return v; } }; #endif template struct ToStringTFunctor {}; @@ -187,13 +187,17 @@ template <> struct ToStringTFunctor { template inline #if MPT_WSTRING_FORMAT && MPT_USTRING_MODE_UTF8 template <> struct ToStringTFunctor { template inline std::wstring operator() (const T & x) { return ToWString(x); } }; #endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template <> struct ToStringTFunctor { template inline mpt::lstring operator() (const T & x) { return mpt::ToLocaleHelper()(x); } }; +#endif // MPT_ENABLE_CHARSET_LOCALE #if defined(_MFC_VER) -template <> struct ToStringTFunctor { template inline CString operator() (const T & x) { return mpt::ToCStringHelper(x); } }; +template <> struct ToStringTFunctor { template inline CString operator() (const T & x) { return mpt::ToCStringHelper()(x); } }; #endif template inline Tstring ToStringT(const T & x) { return ToStringTFunctor()(x); } + struct fmt_base { @@ -204,7 +208,6 @@ enum FormatFlagsEnum CaseLow = 0x0010, // lower case hex digits CaseUpp = 0x0020, // upper case hex digits FillOff = 0x0100, // do not fill up width - FillSpc = 0x0200, // fill up width with spaces FillNul = 0x0400, // fill up width with zeros NotaNrm = 0x1000, // float: normal/default notation NotaFix = 0x2000, // float: fixed point notation @@ -220,7 +223,9 @@ STATIC_ASSERT(sizeof(FormatFlags) >= sizeof(fmt_base::FormatFlagsEnum)); class FormatSpec; MPT_DEPRECATED std::string FormatVal(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) MPT_DEPRECATED std::string FormatVal(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::string FormatVal(const bool & x, const FormatSpec & f); std::string FormatVal(const signed char & x, const FormatSpec & f); std::string FormatVal(const unsigned char & x, const FormatSpec & f); @@ -236,9 +241,30 @@ std::string FormatVal(const float & x, const FormatSpec & f); std::string FormatVal(const double & x, const FormatSpec & f); std::string FormatVal(const long double & x, const FormatSpec & f); +MPT_DEPRECATED mpt::ustring FormatValU(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +MPT_DEPRECATED mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +mpt::ustring FormatValU(const bool & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f); +mpt::ustring FormatValU(const float & x, const FormatSpec & f); +mpt::ustring FormatValU(const double & x, const FormatSpec & f); +mpt::ustring FormatValU(const long double & x, const FormatSpec & f); + #if MPT_WSTRING_FORMAT MPT_DEPRECATED std::wstring FormatValW(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) MPT_DEPRECATED std::wstring FormatValW(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::wstring FormatValW(const bool & x, const FormatSpec & f); std::wstring FormatValW(const signed char & x, const FormatSpec & f); std::wstring FormatValW(const unsigned char & x, const FormatSpec & f); @@ -257,12 +283,13 @@ std::wstring FormatValW(const long double & x, const FormatSpec & f); template struct FormatValTFunctor {}; template <> struct FormatValTFunctor { template inline std::string operator() (const T & x, const FormatSpec & f) { return FormatVal(x, f); } }; -#if MPT_WSTRING_FORMAT +template <> struct FormatValTFunctor { template inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return FormatValU(x, f); } }; +#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_FORMAT template <> struct FormatValTFunctor { template inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } }; #endif -#if MPT_USTRING_MODE_UTF8 -template <> struct FormatValTFunctor { template inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatVal(x, f)); } }; -#endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template <> struct FormatValTFunctor { template inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::CharsetLocale, FormatVal(x, f)); } }; +#endif // MPT_ENABLE_CHARSET_LOCALE #if defined(_MFC_VER) #ifdef UNICODE template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(FormatValW(x, f)); } }; @@ -278,80 +305,55 @@ private: FormatFlags flags; std::size_t width; int precision; + unsigned int group; + char group_sep; public: - FormatSpec() : flags(0), width(0), precision(-1) {} - FormatFlags GetFlags() const { return flags; } - std::size_t GetWidth() const { return width; } - int GetPrecision() const { return precision; } - FormatSpec & SetFlags(FormatFlags f) { flags = f; return *this; } - FormatSpec & SetWidth(std::size_t w) { width = w; return *this; } - FormatSpec & SetPrecision(int p) { precision = p; return *this; } + MPT_CONSTEXPR11_FUN FormatSpec() noexcept : flags(0), width(0), precision(-1), group(0), group_sep(',') {} + MPT_CONSTEXPR11_FUN FormatFlags GetFlags() const noexcept { return flags; } + MPT_CONSTEXPR11_FUN std::size_t GetWidth() const noexcept { return width; } + MPT_CONSTEXPR11_FUN int GetPrecision() const noexcept { return precision; } + MPT_CONSTEXPR11_FUN unsigned int GetGroup() const noexcept { return group; } + MPT_CONSTEXPR11_FUN char GetGroupSep() const noexcept { return group_sep; } + MPT_CONSTEXPR14_FUN FormatSpec & SetFlags(FormatFlags f) noexcept { flags = f; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetWidth(std::size_t w) noexcept { width = w; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetPrecision(int p) noexcept { precision = p; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetGroup(unsigned int g) noexcept { group = g; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetGroupSep(char s) noexcept { group_sep = s; return *this; } public: - // short-hand construction - explicit FormatSpec(FormatFlags f, std::size_t w = 0, int p = -1) : flags(f), width(w), precision(p) {} - explicit FormatSpec(const char * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } - explicit FormatSpec(const wchar_t * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } - explicit FormatSpec(const std::string & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } - explicit FormatSpec(const std::wstring & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); } + MPT_CONSTEXPR14_FUN FormatSpec & BaseDec() noexcept { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseDec; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & BaseHex() noexcept { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseHex; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & CaseLow() noexcept { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseLow; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & CaseUpp() noexcept { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseUpp; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & FillOff() noexcept { flags &= ~(fmt_base::FillOff|fmt_base::FillNul); flags |= fmt_base::FillOff; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & FillNul() noexcept { flags &= ~(fmt_base::FillOff|fmt_base::FillNul); flags |= fmt_base::FillNul; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & NotaNrm() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaNrm; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & NotaFix() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaFix; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & NotaSci() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaSci; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & Width(std::size_t w) noexcept { width = w; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & Prec(int p) noexcept { precision = p; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & Group(unsigned int g) noexcept { group = g; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & GroupSep(char s) noexcept { group_sep = s; return *this; } public: - // only for floating point formats - FormatSpec & ParsePrintf(const char * format); - FormatSpec & ParsePrintf(const wchar_t * format); - FormatSpec & ParsePrintf(const std::string & format); - FormatSpec & ParsePrintf(const std::wstring & format); + MPT_CONSTEXPR14_FUN FormatSpec & Dec() noexcept { return BaseDec(); } + MPT_CONSTEXPR14_FUN FormatSpec & Hex() noexcept { return BaseHex(); } + MPT_CONSTEXPR14_FUN FormatSpec & Low() noexcept { return CaseLow(); } + MPT_CONSTEXPR14_FUN FormatSpec & Upp() noexcept { return CaseUpp(); } + MPT_CONSTEXPR14_FUN FormatSpec & Off() noexcept { return FillOff(); } + MPT_CONSTEXPR14_FUN FormatSpec & Nul() noexcept { return FillNul(); } + MPT_CONSTEXPR14_FUN FormatSpec & Nrm() noexcept { return NotaNrm(); } + MPT_CONSTEXPR14_FUN FormatSpec & Fix() noexcept { return NotaFix(); } + MPT_CONSTEXPR14_FUN FormatSpec & Sci() noexcept { return NotaSci(); } public: - FormatSpec & BaseDec() { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseDec; return *this; } - FormatSpec & BaseHex() { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseHex; return *this; } - FormatSpec & CaseLow() { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseLow; return *this; } - FormatSpec & CaseUpp() { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseUpp; return *this; } - FormatSpec & FillOff() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillOff; return *this; } - FormatSpec & FillSpc() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillSpc; return *this; } - FormatSpec & FillNul() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillNul; return *this; } - FormatSpec & NotaNrm() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaNrm; return *this; } - FormatSpec & NotaFix() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaFix; return *this; } - FormatSpec & NotaSci() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaSci; return *this; } - FormatSpec & Width(std::size_t w) { width = w; return *this; } - FormatSpec & Prec(int p) { precision = p; return *this; } -public: - FormatSpec & Dec() { return BaseDec(); } - FormatSpec & Hex() { return BaseHex(); } - FormatSpec & Low() { return CaseLow(); } - FormatSpec & Upp() { return CaseUpp(); } - FormatSpec & Off() { return FillOff(); } - FormatSpec & Spc() { return FillSpc(); } - FormatSpec & Nul() { return FillNul(); } - FormatSpec & Nrm() { return NotaNrm(); } - FormatSpec & Fix() { return NotaFix(); } - FormatSpec & Sci() { return NotaSci(); } -public: - FormatSpec & Decimal() { return BaseDec(); } - FormatSpec & Hexadecimal() { return BaseHex(); } - FormatSpec & Lower() { return CaseLow(); } - FormatSpec & Upper() { return CaseUpp(); } - FormatSpec & FillNone() { return FillOff(); } - FormatSpec & FillSpace() { return FillSpc(); } - FormatSpec & FillZero() { return FillNul(); } - FormatSpec & FloatNormal() { return NotaNrm(); } - FormatSpec & FloatFixed() { return NotaFix(); } - FormatSpec & FloatScientific() { return NotaSci(); } - FormatSpec & Precision(int p) { return Prec(p); } - template - inline Tstring ToStringT(const T & x) const - { - return FormatValTFunctor()(x, *this); - } - template - inline std::string ToString(const T & x) const - { - return FormatVal(x, *this); - } -#if MPT_WSTRING_FORMAT - template - inline std::wstring ToWString(const T & x) const - { - return FormatValW(x, *this); - } -#endif + MPT_CONSTEXPR14_FUN FormatSpec & Decimal() noexcept { return BaseDec(); } + MPT_CONSTEXPR14_FUN FormatSpec & Hexadecimal() noexcept { return BaseHex(); } + MPT_CONSTEXPR14_FUN FormatSpec & Lower() noexcept { return CaseLow(); } + MPT_CONSTEXPR14_FUN FormatSpec & Upper() noexcept { return CaseUpp(); } + MPT_CONSTEXPR14_FUN FormatSpec & FillNone() noexcept { return FillOff(); } + MPT_CONSTEXPR14_FUN FormatSpec & FillZero() noexcept { return FillNul(); } + MPT_CONSTEXPR14_FUN FormatSpec & FloatNormal() noexcept { return NotaNrm(); } + MPT_CONSTEXPR14_FUN FormatSpec & FloatFixed() noexcept { return NotaFix(); } + MPT_CONSTEXPR14_FUN FormatSpec & FloatScientific() noexcept { return NotaSci(); } + MPT_CONSTEXPR14_FUN FormatSpec & Precision(int p) noexcept { return Prec(p); } }; @@ -365,6 +367,12 @@ static inline Tstring val(const T& x) return ToStringTFunctor()(x); } +template +static inline Tstring fmt(const T& x, const FormatSpec& f) +{ + return FormatValTFunctor()(x, f); +} + template static inline Tstring dec(const T& x) { @@ -372,18 +380,25 @@ static inline Tstring dec(const T& x) return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff()); } template -static inline Tstring dec(const T& x) -{ - STATIC_ASSERT(std::numeric_limits::is_integer); - return FormatValTFunctor()(x, FormatSpec().BaseDec().FillSpc().Width(width)); -} -template static inline Tstring dec0(const T& x) { STATIC_ASSERT(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width)); } +template +static inline Tstring dec(unsigned int g, char s, const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff().Group(g).GroupSep(s)); +} +template +static inline Tstring dec0(unsigned int g, char s, const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s)); +} + template static inline Tstring hex(const T& x) { @@ -397,18 +412,6 @@ static inline Tstring HEX(const T& x) return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff()); } template -static inline Tstring hex(const T& x) -{ - STATIC_ASSERT(std::numeric_limits::is_integer); - return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillSpc().Width(width)); -} -template -static inline Tstring HEX(const T& x) -{ - STATIC_ASSERT(std::numeric_limits::is_integer); - return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillSpc().Width(width)); -} -template static inline Tstring hex0(const T& x) { STATIC_ASSERT(std::numeric_limits::is_integer); @@ -422,51 +425,83 @@ static inline Tstring HEX0(const T& x) } template -static inline Tstring flt(const T& x, std::size_t width = 0, int precision = -1) +static inline Tstring hex(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::is_floating_point::value); - if(width == 0) - { - return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillOff().Precision(precision)); - } else - { - return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillSpc().Width(width).Precision(precision)); - } + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s)); } template -static inline Tstring fix(const T& x, std::size_t width = 0, int precision = -1) +static inline Tstring HEX(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::is_floating_point::value); - if(width == 0) - { - return FormatValTFunctor()(x, FormatSpec().NotaFix().FillOff().Precision(precision)); - } else - { - return FormatValTFunctor()(x, FormatSpec().NotaFix().FillSpc().Width(width).Precision(precision)); - } + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s)); } -template -static inline Tstring sci(const T& x, std::size_t width = 0, int precision = -1) +template +static inline Tstring hex0(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::is_floating_point::value); - if(width == 0) - { - return FormatValTFunctor()(x, FormatSpec().NotaSci().FillOff().Precision(precision)); - } else - { - return FormatValTFunctor()(x, FormatSpec().NotaSci().FillSpc().Width(width).Precision(precision)); - } + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s)); +} +template +static inline Tstring HEX0(unsigned int g, char s, const T& x) +{ + STATIC_ASSERT(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s)); } -template -static inline Tstring f(const Tformat & format, const T& x) +template +static inline Tstring flt(const T& x, int precision = -1) { STATIC_ASSERT(std::is_floating_point::value); - return FormatValTFunctor()(x, FormatSpec().ParsePrintf(format)); + return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillOff().Precision(precision)); +} +template +static inline Tstring fix(const T& x, int precision = -1) +{ + STATIC_ASSERT(std::is_floating_point::value); + return FormatValTFunctor()(x, FormatSpec().NotaFix().FillOff().Precision(precision)); +} +template +static inline Tstring sci(const T& x, int precision = -1) +{ + STATIC_ASSERT(std::is_floating_point::value); + return FormatValTFunctor()(x, FormatSpec().NotaSci().FillOff().Precision(precision)); +} + +static inline Tstring pad_left(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return traits::pad(str, width, 0); +} +static inline Tstring pad_right(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return traits::pad(str, 0, width); +} +static inline Tstring left(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str; +} +static inline Tstring right(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str; +} +static inline Tstring center(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str; } }; // struct fmtT + typedef fmtT fmt; #if MPT_WSTRING_FORMAT typedef fmtT wfmt; @@ -476,8 +511,14 @@ typedef fmtT ufmt; #else typedef fmtT ufmt; #endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +typedef fmtT lfmt; +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +typedef fmtT tfmt; +#endif #if defined(_MFC_VER) -typedef fmtT tfmt; +typedef fmtT cfmt; #endif } // namespace mpt @@ -495,14 +536,19 @@ template <> struct to_string_type { typedef std::string type; template <> struct to_string_type { typedef std::string type; }; template <> struct to_string_type { typedef std::string type; }; template <> struct to_string_type { typedef std::string type; }; +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) template <> struct to_string_type { typedef std::wstring type; }; template <> struct to_string_type { typedef std::wstring type; }; template <> struct to_string_type { typedef std::wstring type; }; template <> struct to_string_type { typedef std::wstring type; }; template <> struct to_string_type { typedef std::wstring type; }; +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR #if MPT_USTRING_MODE_UTF8 template <> struct to_string_type { typedef mpt::ustring type; }; #endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template <> struct to_string_type { typedef mpt::lstring type; }; +#endif // MPT_ENABLE_CHARSET_LOCALE #if defined(_MFC_VER) template <> struct to_string_type { typedef CString type; }; #endif @@ -545,6 +591,19 @@ mpt::ustring PrintImpl(const mpt::ustring & format ); #endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::lstring PrintImpl(const mpt::lstring & format + , const mpt::lstring & x1 = mpt::lstring() + , const mpt::lstring & x2 = mpt::lstring() + , const mpt::lstring & x3 = mpt::lstring() + , const mpt::lstring & x4 = mpt::lstring() + , const mpt::lstring & x5 = mpt::lstring() + , const mpt::lstring & x6 = mpt::lstring() + , const mpt::lstring & x7 = mpt::lstring() + , const mpt::lstring & x8 = mpt::lstring() + ); +#endif // MPT_ENABLE_CHARSET_LOCALE + #if defined(_MFC_VER) CString PrintImpl(const CString & format , const CString & x1 = CString() @@ -769,8 +828,22 @@ static inline message_formatter uformat(const mpt::ustring &format return message_formatter(format); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +static inline message_formatter lformat(const mpt::lstring &format) +{ + return message_formatter(format); +} +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS +static inline message_formatter tformat(const mpt::tstring &format) +{ + return message_formatter(format); +} +#endif + #if defined(_MFC_VER) -static inline message_formatter tformat(const CString &format) +static inline message_formatter cformat(const CString &format) { return message_formatter(format); } @@ -779,4 +852,42 @@ static inline message_formatter tformat(const CString &format) } // namespace mpt + +namespace mpt { namespace String { + +// Combine a vector of values into a string, separated with the given separator. +// No escaping is performed. +template +mpt::ustring Combine(const std::vector &vals, const mpt::ustring &sep=U_(",")) +{ + mpt::ustring str; + for(std::size_t i = 0; i < vals.size(); ++i) + { + if(i > 0) + { + str += sep; + } + str += mpt::ufmt::val(vals[i]); + } + return str; +} +template +std::string Combine(const std::vector &vals, const std::string &sep=std::string(",")) +{ + std::string str; + for(std::size_t i = 0; i < vals.size(); ++i) + { + if(i > 0) + { + str += sep; + } + str += mpt::fmt::val(vals[i]); + } + return str; +} + +} } // namespace mpt::String + + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h index 62307a6e0..269a44f2f 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN @@ -129,6 +131,15 @@ template<> inline std::wstring ConvertStrTo(const mpt::ustring &str) { return mp #endif #endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template +inline T ConvertStrTo(const mpt::lstring &str) +{ + return ConvertStrTo(mpt::ToCharset(mpt::CharsetLocale, str)); +} +template<> inline mpt::lstring ConvertStrTo(const mpt::lstring &str) { return str; } +#endif + namespace mpt { @@ -193,4 +204,47 @@ inline T Hex(const mpt::ustring &str) } // namespace mpt + +namespace mpt { namespace String { + +// Split the given string at separator positions into individual values returned as a vector. +// An empty string results in an empty vector. +// Leading or trailing separators result in a default-constructed element being inserted before or after the other elements. +template +std::vector Split(const mpt::ustring &str, const mpt::ustring &sep=U_(",")) +{ + std::vector vals; + std::size_t pos = 0; + while(str.find(sep, pos) != std::string::npos) + { + vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); + pos = str.find(sep, pos) + sep.length(); + } + if(!vals.empty() || (str.substr(pos).length() > 0)) + { + vals.push_back(ConvertStrTo(str.substr(pos))); + } + return vals; +} +template +std::vector Split(const std::string &str, const std::string &sep=std::string(",")) +{ + std::vector vals; + std::size_t pos = 0; + while(str.find(sep, pos) != std::string::npos) + { + vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); + pos = str.find(sep, pos) + sep.length(); + } + if(!vals.empty() || (str.substr(pos).length() > 0)) + { + vals.push_back(ConvertStrTo(str.substr(pos))); + } + return vals; +} + +} } // namespace mpt::String + + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h index 499fc17e3..86a479464 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h @@ -9,37 +9,11 @@ #pragma once +#include "BuildSettings.h" + #if defined(MPT_ENABLE_THREAD) -#include // some C++ header in order to have the C++ standard library version information available - -#if defined(MPT_QUIRK_NO_CPP_THREAD) -#define MPT_STD_THREAD 0 -#elif MPT_COMPILER_GENERIC -#define MPT_STD_THREAD 1 -#elif MPT_COMPILER_MSVC -#define MPT_STD_THREAD 1 -#elif MPT_COMPILER_GCC && !MPT_OS_WINDOWS -#define MPT_STD_THREAD 1 -#elif MPT_COMPILER_CLANG && defined(__GLIBCXX__) -#define MPT_STD_THREAD 1 -#elif (MPT_OS_MACOSX_OR_IOS || MPT_OS_FREEBSD) && MPT_COMPILER_CLANG -#define MPT_STD_THREAD 1 -#elif MPT_CLANG_AT_LEAST(3,6,0) && defined(_LIBCPP_VERSION) -#define MPT_STD_THREAD 1 -#else -#define MPT_STD_THREAD 0 -#endif - -#if MPT_STD_THREAD #include -#else // !MPT_STD_THREAD -#if MPT_OS_WINDOWS -#include -#else // !MPT_OS_WINDOWS -#include -#endif // MPT_OS_WINDOWS -#endif // MPT_STD_THREAD #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS @@ -59,303 +33,9 @@ namespace mpt { - -#if MPT_STD_THREAD - - - -typedef std::thread::native_handle_type native_handle_type; -typedef std::thread thread; - - - -#else // !MPT_STD_THREAD - - - -#if MPT_OS_WINDOWS - - - -typedef HANDLE native_handle_type; - -// std::thread -// NOTE: This implementation is not movable and prevents copying. -// Therefore, it is not as versatile as a full C++11 std::thread implementation. -// It is only a strict subset. -class thread -{ - -private: - - thread(const thread &) = delete; - thread & operator = (const thread &) = delete; - -private: - - class functor_helper_base { - protected: - functor_helper_base() {} - public: - virtual ~functor_helper_base() {} - public: - virtual void operator () () = 0; - }; - - template - class functor_helper : public functor_helper_base { - private: - Tfunc func; - public: - functor_helper(Tfunc func_) : func(func_) { return; } - virtual ~functor_helper() { return; } - virtual void operator () () { func(); } - }; - - enum FunctionMode - { - FunctionModeNone = 0, - FunctionModeParamNone = 1, - FunctionModeParamPointer = 2, - FunctionModeFunctor = 3, - }; - - native_handle_type threadHandle; - - // Thread startup accesses members of mpt::thread. - // If the mpt::thread instanced gets detached and destroyed directly after initialization, - // there is a race between thread startup and detach/destroy. - // startupDoneEvent protects against this race. - HANDLE startupDoneEvent; - - FunctionMode functionMode; - union { - struct { - void (*function)(void); - } ModeParamNone; - struct { - void (*function)(void*); - void * userdata; - } ModeParamPointer; - struct { - functor_helper_base * pfunctor; - } ModeFunctor; - } modeState; - -private: - - uintptr_t ThreadFuntion() - { - switch(functionMode) - { - case FunctionModeNone: - SetEvent(startupDoneEvent); - return 0; - break; - case FunctionModeParamNone: - { - void (*f)(void) = modeState.ModeParamNone.function; - SetEvent(startupDoneEvent); - f(); - } - return 0; - break; - case FunctionModeParamPointer: - { - void (*f)(void*) = modeState.ModeParamPointer.function; - void * d = modeState.ModeParamPointer.userdata; - SetEvent(startupDoneEvent); - f(d); - } - return 0; - break; - case FunctionModeFunctor: - { - functor_helper_base * pf = modeState.ModeFunctor.pfunctor; - SetEvent(startupDoneEvent); - (*pf)(); - delete pf; - } - return 0; - break; - default: - SetEvent(startupDoneEvent); - return 0; - break; - } - SetEvent(startupDoneEvent); - return 0; - } - - static DWORD WINAPI ThreadFunctionWrapper(LPVOID param) - { - reinterpret_cast(param)->ThreadFuntion(); - return 0; - } - -public: - - mpt::native_handle_type native_handle() - { - return threadHandle; - } - - bool joinable() const - { - return (threadHandle != nullptr); - } - - void join() - { - if(!joinable()) - { - throw std::invalid_argument("thread::joinable() == false"); - } - WaitForSingleObject(threadHandle, INFINITE); - CloseHandle(threadHandle); - threadHandle = nullptr; - } - - void detach() - { - if(!joinable()) - { - throw std::invalid_argument("thread::joinable() == false"); - } - CloseHandle(threadHandle); - threadHandle = nullptr; - } - - void swap(thread & other) noexcept - { - using std::swap; - swap(threadHandle, other.threadHandle); - swap(startupDoneEvent, other.startupDoneEvent); - swap(functionMode, other.functionMode); - } - - friend void swap(thread & a, thread & b) noexcept - { - a.swap(b); - } - - thread(thread && other) noexcept - : threadHandle(nullptr) - , startupDoneEvent(nullptr) - , functionMode(FunctionModeNone) - { - swap(other); - } - - thread & operator=(thread && other) noexcept - { - if(joinable()) - { - std::terminate(); - } - swap(other); - return *this; - } - - thread() - : threadHandle(nullptr) - , startupDoneEvent(nullptr) - , functionMode(FunctionModeNone) - { - std::memset(&modeState, 0, sizeof(modeState)); - } - - thread(void (*function)(void)) - : threadHandle(nullptr) - , startupDoneEvent(nullptr) - , functionMode(FunctionModeParamNone) - { - std::memset(&modeState, 0, sizeof(modeState)); - modeState.ModeParamNone.function = function; - startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); } - DWORD dummy = 0; // For Win9x - threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy); - if(threadHandle) - { - WaitForSingleObject(startupDoneEvent, INFINITE); - } - CloseHandle(startupDoneEvent); - startupDoneEvent = nullptr; - if(!threadHandle) { throw std::runtime_error("unable to start thread"); } - } - - thread(void (*function)(void*), void * userdata) - : threadHandle(nullptr) - , startupDoneEvent(nullptr) - , functionMode(FunctionModeParamPointer) - { - std::memset(&modeState, 0, sizeof(modeState)); - modeState.ModeParamPointer.function = function; - modeState.ModeParamPointer.userdata = userdata; - startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); } - DWORD dummy = 0; // For Win9x - threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy); - if(threadHandle) - { - WaitForSingleObject(startupDoneEvent, INFINITE); - } - CloseHandle(startupDoneEvent); - startupDoneEvent = nullptr; - if(!threadHandle) { throw std::runtime_error("unable to start thread"); } - } - - template - thread(Tfunctor functor) - : threadHandle(nullptr) - , startupDoneEvent(nullptr) - , functionMode(FunctionModeFunctor) - { - std::memset(&modeState, 0, sizeof(modeState)); - modeState.ModeFunctor.pfunctor = new functor_helper(functor); - startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); } - DWORD dummy = 0; // For Win9x - threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy); - if(threadHandle) - { - WaitForSingleObject(startupDoneEvent, INFINITE); - } - CloseHandle(startupDoneEvent); - startupDoneEvent = nullptr; - if(!threadHandle) { throw std::runtime_error("unable to start thread"); } - } - - ~thread() - { - MPT_ASSERT(!joinable()); - } - -public: - - static unsigned int hardware_concurrency() - { - SYSTEM_INFO sysInfo; - GetSystemInfo(&sysInfo); - return std::max(sysInfo.dwNumberOfProcessors, 1); - } - -}; - - - -#endif // MPT_OS_WINDOWS - - - -#endif // MPT_STD_THREAD - - - #if defined(MODPLUG_TRACKER) -#if MPT_OS_WINDOWS +#if MPT_OS_WINDOWS && (MPT_COMPILER_MSVC || MPT_COMPILER_CLANG) enum ThreadPriority { @@ -366,7 +46,7 @@ enum ThreadPriority ThreadPriorityHighest = THREAD_PRIORITY_HIGHEST }; -inline void SetThreadPriority(mpt::thread &t, mpt::ThreadPriority priority) +inline void SetThreadPriority(std::thread &t, mpt::ThreadPriority priority) { ::SetThreadPriority(t.native_handle(), priority); } @@ -387,7 +67,7 @@ enum ThreadPriority ThreadPriorityHighest = 2 }; -inline void SetThreadPriority(mpt::thread & /*t*/ , mpt::ThreadPriority /*priority*/ ) +inline void SetThreadPriority(std::thread & /*t*/ , mpt::ThreadPriority /*priority*/ ) { // nothing } @@ -397,7 +77,7 @@ inline void SetCurrentThreadPriority(mpt::ThreadPriority /*priority*/ ) // nothing } -#endif // MPT_OS_WINDOWS +#endif // MPT_OS_WINDOWS && (MPT_COMPILER_MSVC || MPT_COMPILER_CLANG) #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp index dec625f89..704d1f957 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp @@ -11,6 +11,8 @@ #include "stdafx.h" #include "mptTime.h" +#include "mptStringBuffer.h" + #include #if MPT_OS_WINDOWS @@ -43,7 +45,7 @@ uint64 Now() return ((uint64)filetime.dwHighDateTime << 32 | filetime.dwLowDateTime); } -mpt::ustring ToString(uint64 time100ns) +mpt::ustring ToUString(uint64 time100ns) { static const std::size_t bufsize = 256; @@ -55,17 +57,17 @@ mpt::ustring ToString(uint64 time100ns) filetime.dwLowDateTime = (DWORD)((uint64)time100ns); FileTimeToSystemTime(&filetime, &systime); - WCHAR buf[bufsize]; + TCHAR buf[bufsize]; - GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &systime, L"yyyy-MM-dd", buf, bufsize); - result.append(mpt::ToUnicode(buf)); + GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &systime, TEXT("yyyy-MM-dd"), buf, bufsize); + result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf))); - result.append(MPT_USTRING(" ")); + result.append(U_(" ")); - GetTimeFormatW(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &systime, L"HH:mm:ss", buf, bufsize); - result.append(mpt::ToUnicode(buf)); + GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &systime, TEXT("HH:mm:ss"), buf, bufsize); + result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf))); - result.append(MPT_USTRING(".")); + result.append(U_(".")); result.append(mpt::ufmt::dec0<3>((unsigned)systime.wMilliseconds)); @@ -159,7 +161,7 @@ mpt::ustring ToShortenedISO8601(tm date) // and strftime does not support reduced precision ISO8601 at all. // Just do the formatting ourselves. mpt::ustring result; - mpt::ustring tz = MPT_USTRING("Z"); + mpt::ustring tz = U_("Z"); if(date.tm_year == 0) { return result; @@ -169,12 +171,12 @@ mpt::ustring ToShortenedISO8601(tm date) { return result; } - result += MPT_USTRING("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1); + result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1); if(date.tm_mday < 1 || date.tm_mday > 31) { return result; } - result += MPT_USTRING("-") + mpt::ufmt::dec0<2>(date.tm_mday); + result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mday); if(date.tm_hour == 0 && date.tm_min == 0 && date.tm_sec == 0) { return result; @@ -187,17 +189,17 @@ mpt::ustring ToShortenedISO8601(tm date) { return result; } - result += MPT_USTRING("T"); + result += U_("T"); if(date.tm_isdst > 0) { - tz = MPT_USTRING("+01:00"); + tz = U_("+01:00"); } - result += mpt::ufmt::dec0<2>(date.tm_hour) + MPT_USTRING(":") + mpt::ufmt::dec0<2>(date.tm_min); + result += mpt::ufmt::dec0<2>(date.tm_hour) + U_(":") + mpt::ufmt::dec0<2>(date.tm_min); if(date.tm_sec < 0 || date.tm_sec > 61) { return result + tz; } - result += MPT_USTRING(":") + mpt::ufmt::dec0<2>(date.tm_sec); + result += U_(":") + mpt::ufmt::dec0<2>(date.tm_sec); result += tz; return result; } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.h b/Frameworks/OpenMPT/OpenMPT/common/mptTime.h index 9d876f1e2..1332710a8 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptTime.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include @@ -34,7 +36,7 @@ namespace ANSI uint64 Now(); -mpt::ustring ToString(uint64 time100ns); // i.e. 2015-01-15 18:32:01.718 +mpt::ustring ToUString(uint64 time100ns); // i.e. 2015-01-15 18:32:01.718 } // namespacee ANSI diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTypeTraits.h b/Frameworks/OpenMPT/OpenMPT/common/mptTypeTraits.h deleted file mode 100644 index 762f8e4b0..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/mptTypeTraits.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * mptTypeTraits.h - * --------------- - * Purpose: C++11 similar type_traits header plus some OpenMPT specific traits. - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - -#include - - - -OPENMPT_NAMESPACE_BEGIN - - - -namespace mpt { - - - -template struct int_of_size { }; -template <> struct int_of_size<1> { typedef int8 type; }; -template <> struct int_of_size<2> { typedef int16 type; }; -template <> struct int_of_size<3> { typedef int32 type; }; -template <> struct int_of_size<4> { typedef int32 type; }; -template <> struct int_of_size<5> { typedef int64 type; }; -template <> struct int_of_size<6> { typedef int64 type; }; -template <> struct int_of_size<7> { typedef int64 type; }; -template <> struct int_of_size<8> { typedef int64 type; }; - -template struct uint_of_size { }; -template <> struct uint_of_size<1> { typedef uint8 type; }; -template <> struct uint_of_size<2> { typedef uint16 type; }; -template <> struct uint_of_size<3> { typedef uint32 type; }; -template <> struct uint_of_size<4> { typedef uint32 type; }; -template <> struct uint_of_size<5> { typedef uint64 type; }; -template <> struct uint_of_size<6> { typedef uint64 type; }; -template <> struct uint_of_size<7> { typedef uint64 type; }; -template <> struct uint_of_size<8> { typedef uint64 type; }; - - -// Tell which types are safe for mpt::byte_cast. -// signed char is actually not allowed to alias into an object representation, -// which means that, if the actual type is not itself signed char but char or -// unsigned char instead, dereferencing the signed char pointer is undefined -// behaviour. -template struct is_byte_castable : public std::false_type { }; -template <> struct is_byte_castable : public std::true_type { }; -template <> struct is_byte_castable : public std::true_type { }; -template <> struct is_byte_castable : public std::true_type { }; -template <> struct is_byte_castable : public std::true_type { }; - - -// Tell which types are safe to binary write into files. -// By default, no types are safe. -// When a safe type gets defined, -// also specialize this template so that IO functions will work. -template struct is_binary_safe : public std::false_type { }; - -// Specialization for byte types. -template <> struct is_binary_safe : public std::true_type { }; -template <> struct is_binary_safe : public std::true_type { }; -template <> struct is_binary_safe : public std::true_type { }; - -// Generic Specialization for arrays. -template struct is_binary_safe : public is_binary_safe { }; -template struct is_binary_safe : public is_binary_safe { }; - -template -struct GetRawBytesFunctor -{ - inline const mpt::byte * operator () (const T & v) const - { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return reinterpret_cast(&v); - } - inline mpt::byte * operator () (T & v) const - { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return reinterpret_cast(&v); - } -}; - -template -struct GetRawBytesFunctor -{ - inline const mpt::byte * operator () (const T (&v)[N]) const - { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return reinterpret_cast(v); - } - inline mpt::byte * operator () (T (&v)[N]) const - { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return reinterpret_cast(v); - } -}; - -template -struct GetRawBytesFunctor -{ - inline const mpt::byte * operator () (const T (&v)[N]) const - { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return reinterpret_cast(v); - } -}; - -// In order to be able to partially specialize it, -// as_raw_memory is implemented via a class template. -// Do not overload or specialize as_raw_memory directly. -// Using a wrapper (by default just around a cast to const mpt::byte *), -// allows for implementing raw memory access -// via on-demand generating a cached serialized representation. -template inline const mpt::byte * as_raw_memory(const T & v) -{ - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::GetRawBytesFunctor()(v); -} -template inline mpt::byte * as_raw_memory(T & v) -{ - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::GetRawBytesFunctor()(v); -} - -} // namespace mpt - -#define MPT_BINARY_STRUCT(type, size) \ - MPT_STATIC_ASSERT(sizeof( type ) == (size) ); \ - MPT_STATIC_ASSERT(alignof( type ) == 1); \ - MPT_STATIC_ASSERT(std::is_standard_layout< type >::value); \ - namespace mpt { \ - template <> struct is_binary_safe< type > : public std::true_type { }; \ - } \ -/**/ - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp index fa4c9e3b7..1dbbc3507 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp @@ -39,7 +39,7 @@ namespace Util #if defined(MODPLUG_TRACKER) || !defined(NO_DMO) -std::wstring CLSIDToString(CLSID clsid) +mpt::winstring CLSIDToString(CLSID clsid) { std::wstring str; LPOLESTR tmp = nullptr; @@ -75,17 +75,17 @@ std::wstring CLSIDToString(CLSID clsid) { ::CoTaskMemFree(tmp); tmp = nullptr; - MPT_UNUSED_VARIABLE(e); - MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } ::CoTaskMemFree(tmp); tmp = nullptr; - return str; + return mpt::ToWin(str); } -CLSID StringToCLSID(const std::wstring &str) +CLSID StringToCLSID(const mpt::winstring &str_) { + const std::wstring str = mpt::ToWide(str_); CLSID clsid = CLSID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); switch(::CLSIDFromString(tmp.data(), &clsid)) @@ -115,8 +115,9 @@ CLSID StringToCLSID(const std::wstring &str) } -bool VerifyStringToCLSID(const std::wstring &str, CLSID &clsid) +bool VerifyStringToCLSID(const mpt::winstring &str_, CLSID &clsid) { + const std::wstring str = mpt::ToWide(str_); bool result = false; clsid = CLSID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); @@ -145,8 +146,9 @@ bool VerifyStringToCLSID(const std::wstring &str, CLSID &clsid) } -bool IsCLSID(const std::wstring &str) +bool IsCLSID(const mpt::winstring &str_) { + const std::wstring str = mpt::ToWide(str_); bool result = false; CLSID clsid = CLSID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); @@ -177,7 +179,7 @@ bool IsCLSID(const std::wstring &str) } -std::wstring IIDToString(IID iid) +mpt::winstring IIDToString(IID iid) { std::wstring str; LPOLESTR tmp = nullptr; @@ -213,15 +215,15 @@ std::wstring IIDToString(IID iid) { ::CoTaskMemFree(tmp); tmp = nullptr; - MPT_UNUSED_VARIABLE(e); - MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } - return str; + return mpt::ToWin(str); } -IID StringToIID(const std::wstring &str) +IID StringToIID(const mpt::winstring &str_) { + const std::wstring str = mpt::ToWide(str_); IID iid = IID(); std::vector tmp(str.c_str(), str.c_str() + str.length() + 1); switch(::IIDFromString(tmp.data(), &iid)) @@ -245,18 +247,18 @@ IID StringToIID(const std::wstring &str) } -std::wstring GUIDToString(GUID guid) +mpt::winstring GUIDToString(GUID guid) { std::vector tmp(256); if(::StringFromGUID2(guid, tmp.data(), static_cast(tmp.size())) <= 0) { throw std::logic_error("StringFromGUID2() failed."); } - return tmp.data(); + return mpt::ToWin(tmp.data()); } -GUID StringToGUID(const std::wstring &str) +GUID StringToGUID(const mpt::winstring &str) { return StringToIID(str); } @@ -278,71 +280,6 @@ GUID CreateGUID() } -#if !MPT_OS_WINDOWS_WINRT - -UUID StringToUUID(const mpt::ustring &str) -{ - UUID uuid = UUID(); - std::wstring wstr = mpt::ToWide(str); - std::vector tmp(wstr.c_str(), wstr.c_str() + wstr.length() + 1); - switch(::UuidFromStringW((RPC_WSTR)(&(tmp[0])), &uuid)) - { - case RPC_S_OK: - // nothing - break; - case RPC_S_INVALID_STRING_UUID: - uuid = UUID(); - break; - default: - throw std::logic_error("UuidFromStringW() failed."); - break; - } - return uuid; -} - - -mpt::ustring UUIDToString(UUID uuid) -{ - std::wstring wstr; - RPC_WSTR tmp = nullptr; - switch(::UuidToStringW(&uuid, &tmp)) - { - case RPC_S_OK: - // nothing - break; - case RPC_S_OUT_OF_MEMORY: - if(tmp) - { - ::RpcStringFreeW(&tmp); - tmp = nullptr; - } - MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); - break; - default: - throw std::logic_error("UuidToStringW() failed."); - break; - } - try - { - std::size_t len = 0; - for(len = 0; tmp[len] != 0; ++len) - { - // nothing - } - wstr = std::wstring(tmp, tmp + len); - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - ::RpcStringFreeW(&tmp); - tmp = nullptr; - MPT_UNUSED_VARIABLE(e); - MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(); - } - return mpt::ToUnicode(wstr); -} - -#endif // !MPT_OS_WINDOWS_WINRT - - bool IsValid(UUID uuid) { return false @@ -423,17 +360,6 @@ UUID::operator ::UUID () const return UUIDToWin32(*this); } -mpt::UUID UUID::FromGroups(uint32 group1, uint16 group2, uint16 group3, uint16 group4, uint64 group5) -{ - MPT_ASSERT((group5 & 0xffff000000000000ull) == 0ull); - return mpt::UUID - ( group1 - , group2 - , group3 - , (static_cast(group4) << 48) | group5 - ); -} - #endif // MODPLUG_TRACKER || !NO_DMO #endif // MPT_OS_WINDOWS @@ -489,31 +415,22 @@ UUID UUID::GenerateLocalUseOnly() return mpt::UUID::RFC4122Random(); #endif #elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT - #if _WIN32_WINNT >= 0x0501 - // Available since Win2000, but we check for WinXP in order to not use this - // function in Win32old builds. It is not available on some non-fully - // patched Win98SE installs in the wild. - ::UUID uuid = ::UUID(); - RPC_STATUS status = ::UuidCreateSequential(&uuid); - if(status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) - { - return Generate(); - } - status = RPC_S_OK; - if(UuidIsNil(&uuid, &status) != FALSE) - { - return mpt::UUID::RFC4122Random(); - } - if(status != RPC_S_OK) - { - return mpt::UUID::RFC4122Random(); - } - return mpt::UUIDFromWin32(uuid); - #else - // Fallback to ::UuidCreate is safe as ::UuidCreateSequential is only a - // tiny performance optimization. + ::UUID uuid = ::UUID(); + RPC_STATUS status = ::UuidCreateSequential(&uuid); + if(status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) + { return Generate(); - #endif + } + status = RPC_S_OK; + if(UuidIsNil(&uuid, &status) != FALSE) + { + return mpt::UUID::RFC4122Random(); + } + if(status != RPC_S_OK) + { + return mpt::UUID::RFC4122Random(); + } + return mpt::UUIDFromWin32(uuid); #else return RFC4122Random(); #endif @@ -522,7 +439,7 @@ UUID UUID::GenerateLocalUseOnly() UUID UUID::RFC4122Random() { UUID result; - mpt::thread_safe_prng & prng = mpt::global_prng(); + mpt::thread_safe_prng & prng = mpt::global_prng(); result.Data1 = mpt::random(prng); result.Data2 = mpt::random(prng); result.Data3 = mpt::random(prng); @@ -531,62 +448,7 @@ UUID UUID::RFC4122Random() return result; } -uint32 UUID::GetData1() const -{ - return Data1; -} - -uint16 UUID::GetData2() const -{ - return Data2; -} - -uint16 UUID::GetData3() const -{ - return Data3; -} - -uint64 UUID::GetData4() const -{ - return Data4; -} - -bool UUID::IsNil() const -{ - return (Data1 == 0) && (Data2 == 0) && (Data3 == 0) && (Data4 == 0); -} - -bool UUID::IsValid() const -{ - return (Data1 != 0) || (Data2 != 0) || (Data3 != 0) || (Data4 != 0); -} - -uint8 UUID::Mm() const -{ - return static_cast((Data3 >> 8) & 0xffu); -} - -uint8 UUID::Nn() const -{ - return static_cast((Data4 >> 56) & 0xffu); -} - -uint8 UUID::Variant() const -{ - return Nn() >> 4u; -} - -uint8 UUID::Version() const -{ - return Mm() >> 4u; -} - -bool UUID::IsRFC4122() const -{ - return (Variant() & 0xcu) == 0x8u; -} - -void UUID::MakeRFC4122(uint8 version) +void UUID::MakeRFC4122(uint8 version) noexcept { // variant uint8 Nn = static_cast((Data4 >> 56) & 0xffu); @@ -603,70 +465,9 @@ void UUID::MakeRFC4122(uint8 version) Data3 |= static_cast(Mm) << 8; } -UUID::UUID() -{ - Data1 = 0; - Data2 = 0; - Data3 = 0; - Data4 = 0; -} - -UUID::UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4) -{ - this->Data1 = Data1; - this->Data2 = Data2; - this->Data3 = Data3; - this->Data4 = Data4; -} - -bool operator==(const mpt::UUID & a, const mpt::UUID & b) -{ - return (a.Data1 == b.Data1) && (a.Data2 == b.Data2) && (a.Data3 == b.Data3) && (a.Data4 == b.Data4); -} - -bool operator!=(const mpt::UUID & a, const mpt::UUID & b) -{ - return (a.Data1 != b.Data1) || (a.Data2 != b.Data2) || (a.Data3 != b.Data3) || (a.Data4 != b.Data4); -} - -UUID UUID::FromString(const std::string &str) -{ - std::vector segments = mpt::String::Split(str, std::string("-")); - if(segments.size() != 5) - { - return UUID(); - } - if(segments[0].length() != 8) - { - return UUID(); - } - if(segments[1].length() != 4) - { - return UUID(); - } - if(segments[2].length() != 4) - { - return UUID(); - } - if(segments[3].length() != 4) - { - return UUID(); - } - if(segments[4].length() != 12) - { - return UUID(); - } - UUID result; - result.Data1 = mpt::String::Parse::Hex(segments[0]); - result.Data2 = mpt::String::Parse::Hex(segments[1]); - result.Data3 = mpt::String::Parse::Hex(segments[2]); - result.Data4 = mpt::String::Parse::Hex(segments[3] + segments[4]); - return result; -} - UUID UUID::FromString(const mpt::ustring &str) { - std::vector segments = mpt::String::Split(str, MPT_USTRING("-")); + std::vector segments = mpt::String::Split(str, U_("-")); if(segments.size() != 5) { return UUID(); @@ -699,38 +500,30 @@ UUID UUID::FromString(const mpt::ustring &str) return result; } -std::string UUID::ToString() const -{ - return std::string() - + mpt::fmt::hex0<8>(GetData1()) - + std::string("-") - + mpt::fmt::hex0<4>(GetData2()) - + std::string("-") - + mpt::fmt::hex0<4>(GetData3()) - + std::string("-") - + mpt::fmt::hex0<4>(static_cast(GetData4() >> 48)) - + std::string("-") - + mpt::fmt::hex0<4>(static_cast(GetData4() >> 32)) - + mpt::fmt::hex0<8>(static_cast(GetData4() >> 0)) - ; -} - mpt::ustring UUID::ToUString() const { return mpt::ustring() + mpt::ufmt::hex0<8>(GetData1()) - + MPT_USTRING("-") + + U_("-") + mpt::ufmt::hex0<4>(GetData2()) - + MPT_USTRING("-") + + U_("-") + mpt::ufmt::hex0<4>(GetData3()) - + MPT_USTRING("-") + + U_("-") + mpt::ufmt::hex0<4>(static_cast(GetData4() >> 48)) - + MPT_USTRING("-") + + U_("-") + mpt::ufmt::hex0<4>(static_cast(GetData4() >> 32)) + mpt::ufmt::hex0<8>(static_cast(GetData4() >> 0)) ; } +UUID::UUID(UUIDbin uuid) +{ + Data1 = uuid.Data1.get(); + Data2 = uuid.Data2.get(); + Data3 = uuid.Data3.get(); + Data4 = uuid.Data4.get(); +} + UUID::UUID(GUIDms guid) { Data1 = guid.Data1.get(); @@ -739,6 +532,16 @@ UUID::UUID(GUIDms guid) Data4 = guid.Data4.get(); } +UUID::operator UUIDbin() const +{ + UUIDbin result; + result.Data1 = GetData1(); + result.Data2 = GetData2(); + result.Data3 = GetData3(); + result.Data4 = GetData4(); + return result; +} + UUID::operator GUIDms() const { GUIDms result; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h index d0b3b3676..677fafce3 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "Endianness.h" @@ -34,30 +36,23 @@ namespace Util // A CLSID string is not necessarily a standard UUID string, // it might also be a symbolic name for the interface. // (see CLSIDFromString ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms680589%28v=vs.85%29.aspx )) -std::wstring CLSIDToString(CLSID clsid); -CLSID StringToCLSID(const std::wstring &str); -bool VerifyStringToCLSID(const std::wstring &str, CLSID &clsid); -bool IsCLSID(const std::wstring &str); +mpt::winstring CLSIDToString(CLSID clsid); +CLSID StringToCLSID(const mpt::winstring &str); +bool VerifyStringToCLSID(const mpt::winstring &str, CLSID &clsid); +bool IsCLSID(const mpt::winstring &str); // COM IID<->string conversion -IID StringToIID(const std::wstring &str); -std::wstring IIDToString(IID iid); +IID StringToIID(const mpt::winstring &str); +mpt::winstring IIDToString(IID iid); // General GUID<->string conversion. // The string must/will be in standard GUID format: {4F9A455D-E7EF-4367-B2F0-0C83A38A5C72} -GUID StringToGUID(const std::wstring &str); -std::wstring GUIDToString(GUID guid); +GUID StringToGUID(const mpt::winstring &str); +mpt::winstring GUIDToString(GUID guid); // Create a COM GUID GUID CreateGUID(); -#if !MPT_OS_WINDOWS_WINRT -// General UUID<->string conversion. -// The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72 -UUID StringToUUID(const mpt::ustring &str); -mpt::ustring UUIDToString(UUID uuid); -#endif // !MPT_OS_WINDOWS_WINRT - // Checks the UUID against the NULL UUID. Returns false if it is NULL, true otherwise. bool IsValid(UUID uuid); @@ -75,49 +70,104 @@ struct GUIDms uint16le Data3; uint64be Data4; // yes, big endian here }; -STATIC_ASSERT(sizeof(GUIDms) == 16); +MPT_BINARY_STRUCT(GUIDms, 16) + +// RFC binary format +struct UUIDbin +{ + uint32be Data1; + uint16be Data2; + uint16be Data3; + uint64be Data4; +}; +MPT_BINARY_STRUCT(UUIDbin, 16) namespace mpt { struct UUID { private: - uint32be Data1; - uint16be Data2; - uint16be Data3; - uint64be Data4; + uint32 Data1; + uint16 Data2; + uint16 Data3; + uint64 Data4; public: - uint32 GetData1() const; - uint16 GetData2() const; - uint16 GetData3() const; - uint64 GetData4() const; + MPT_CONSTEXPR11_FUN uint32 GetData1() const noexcept { return Data1; } + MPT_CONSTEXPR11_FUN uint16 GetData2() const noexcept { return Data2; } + MPT_CONSTEXPR11_FUN uint16 GetData3() const noexcept { return Data3; } + MPT_CONSTEXPR11_FUN uint64 GetData4() const noexcept { return Data4; } public: // xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx // <--32-->-<16>-<16>-<-------64------> - bool IsNil() const; - bool IsValid() const; - uint8 Variant() const; - uint8 Version() const; - bool IsRFC4122() const; + MPT_CONSTEXPR11_FUN bool IsNil() const noexcept { return (Data1 == 0) && (Data2 == 0) && (Data3 == 0) && (Data4 == 0); } + MPT_CONSTEXPR11_FUN bool IsValid() const noexcept { return (Data1 != 0) || (Data2 != 0) || (Data3 != 0) || (Data4 != 0); } + MPT_CONSTEXPR11_FUN uint8 Variant() const noexcept { return Nn() >> 4u; } + MPT_CONSTEXPR11_FUN uint8 Version() const noexcept { return Mm() >> 4u; } + MPT_CONSTEXPR11_FUN bool IsRFC4122() const noexcept { return (Variant() & 0xcu) == 0x8u; } private: - uint8 Mm() const; - uint8 Nn() const; - void MakeRFC4122(uint8 version); + MPT_CONSTEXPR11_FUN uint8 Mm() const noexcept { return static_cast((Data3 >> 8) & 0xffu); } + MPT_CONSTEXPR11_FUN uint8 Nn() const noexcept { return static_cast((Data4 >> 56) & 0xffu); } + void MakeRFC4122(uint8 version) noexcept; public: #if MPT_OS_WINDOWS && (defined(MODPLUG_TRACKER) || !defined(NO_DMO)) explicit UUID(::UUID uuid); operator ::UUID () const; - static UUID FromGroups(uint32 group1, uint16 group2, uint16 group3, uint16 group4, uint64 group5); - #define MPT_UUID_HELPER( prefix , value , suffix ) ( prefix ## value ## suffix ) - #define MPT_UUID(group1, group2, group3, group4, group5) mpt::UUID::FromGroups(MPT_UUID_HELPER(0x,group1,u), MPT_UUID_HELPER(0x,group2,u), MPT_UUID_HELPER(0x,group3,u), MPT_UUID_HELPER(0x,group4,u), MPT_UUID_HELPER(0x,group5,ull)) #endif // MPT_OS_WINDOWS && (MODPLUG_TRACKER || !NO_DMO) +private: + static MPT_CONSTEXPR11_FUN uint8 NibbleFromChar(char x) + { + return + ('0' <= x && x <= '9') ? static_cast(x - '0' + 0) : + ('a' <= x && x <= 'z') ? static_cast(x - 'a' + 10) : + ('A' <= x && x <= 'Z') ? static_cast(x - 'A' + 10) : + throw std::domain_error(""); + } + static MPT_CONSTEXPR11_FUN uint8 ByteFromHex(char x, char y) + { + return static_cast(uint8(0) + | (NibbleFromChar(x) << 4) + | (NibbleFromChar(y) << 0) + ); + } + static MPT_CONSTEXPR11_FUN uint16 ParseHex16(const char * str) + { + return static_cast(uint16(0) + | (static_cast(ByteFromHex(str[0], str[1])) << 8) + | (static_cast(ByteFromHex(str[2], str[3])) << 0) + ); + } + static MPT_CONSTEXPR11_FUN uint32 ParseHex32(const char * str) + { + return static_cast(uint32(0) + | (static_cast(ByteFromHex(str[0], str[1])) << 24) + | (static_cast(ByteFromHex(str[2], str[3])) << 16) + | (static_cast(ByteFromHex(str[4], str[5])) << 8) + | (static_cast(ByteFromHex(str[6], str[7])) << 0) + ); + } public: - UUID(); - explicit UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4); + static MPT_CONSTEXPR11_FUN UUID ParseLiteral(const char * str, std::size_t len) + { + return + (len == 36 && str[8] == '-' && str[13] == '-' && str[18] == '-' && str[23] == '-') ? + mpt::UUID( + ParseHex32(str + 0), + ParseHex16(str + 9), + ParseHex16(str + 14), + uint64(0) + | (static_cast(ParseHex16(str + 19)) << 48) + | (static_cast(ParseHex16(str + 24)) << 32) + | (static_cast(ParseHex32(str + 28)) << 0) + ) + : throw std::domain_error(""); + } +public: + MPT_CONSTEXPR11_FUN UUID() noexcept : Data1(0), Data2(0), Data3(0), Data4(0) { } + MPT_CONSTEXPR11_FUN explicit UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4) noexcept : Data1(Data1), Data2(Data2), Data3(Data3), Data4(Data4) { } + explicit UUID(UUIDbin uuid); explicit UUID(GUIDms guid); + operator UUIDbin () const; operator GUIDms () const; - friend bool operator==(const mpt::UUID & a, const mpt::UUID & b); - friend bool operator!=(const mpt::UUID & a, const mpt::UUID & b); public: // Create a UUID static UUID Generate(); @@ -129,18 +179,26 @@ public: public: // General UUID<->string conversion. // The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72 - static UUID FromString(const std::string &str); static UUID FromString(const mpt::ustring &str); - std::string ToString() const; mpt::ustring ToUString() const; }; -STATIC_ASSERT(sizeof(mpt::UUID) == 16); - -bool operator==(const mpt::UUID & a, const mpt::UUID & b); -bool operator!=(const mpt::UUID & a, const mpt::UUID & b); +MPT_CONSTEXPR11_FUN bool operator==(const mpt::UUID & a, const mpt::UUID & b) noexcept +{ + return (a.GetData1() == b.GetData1()) && (a.GetData2() == b.GetData2()) && (a.GetData3() == b.GetData3()) && (a.GetData4() == b.GetData4()); +} +MPT_CONSTEXPR11_FUN bool operator!=(const mpt::UUID & a, const mpt::UUID & b) noexcept +{ + return (a.GetData1() != b.GetData1()) || (a.GetData2() != b.GetData2()) || (a.GetData3() != b.GetData3()) || (a.GetData4() != b.GetData4()); +} } // namespace mpt +MPT_CONSTEXPR11_FUN mpt::UUID operator "" _uuid (const char * str, std::size_t len) +{ + return mpt::UUID::ParseLiteral(str, len); +} + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp index 747fd7af2..adf1b23c6 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp @@ -48,7 +48,7 @@ Context::Context(mpt::Wine::VersionContext versionContext) { throw mpt::Wine::Exception("Unknown Wine version detected."); } - m_Kernel32 = mpt::Library(mpt::LibraryPath::FullPath(MPT_PATHSTRING("kernel32.dll"))); + m_Kernel32 = mpt::Library(mpt::LibraryPath::FullPath(P_("kernel32.dll"))); if(!m_Kernel32.IsValid()) { throw mpt::Wine::Exception("Could not load Wine kernel32.dll."); @@ -136,7 +136,7 @@ std::string Context::PathToPosix(mpt::PathString windowsPath) throw mpt::Wine::Exception("Path too long."); } LPSTR tmp = nullptr; - tmp = wine_get_unix_file_name(windowsPath.AsNative().c_str()); + tmp = wine_get_unix_file_name(windowsPath.ToWide().c_str()); if(!tmp) { throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_unix_file_name failed."); @@ -164,7 +164,7 @@ mpt::PathString Context::PathToWindows(std::string hostPath) { throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_dos_file_name failed."); } - result = mpt::PathString::FromNative(tmp); + result = mpt::PathString::FromWide(tmp); HeapFree(GetProcessHeap(), 0, tmp); tmp = nullptr; return result; @@ -213,13 +213,13 @@ std::string Context::EscapePosixShell(std::string line) const char escape_chars [] = { '|', '&', ';', '<', '>', '(', ')', '$', '`', '"', '\'', ' ', '\t' }; const char maybe_escape_chars [] = { '*', '?', '[', '#', '~', '=', '%' }; line = mpt::String::Replace(line, "\\", "\\\\"); - for(std::size_t i = 0; i < mpt::size(escape_chars); ++i) + for(char c : escape_chars) { - line = mpt::String::Replace(line, std::string(1, escape_chars[i]), "\\" + std::string(1, escape_chars[i])); + line = mpt::String::Replace(line, std::string(1, c), "\\" + std::string(1, c)); } - for(std::size_t i = 0; i < mpt::size(maybe_escape_chars); ++i) + for(char c : maybe_escape_chars) { - line = mpt::String::Replace(line, std::string(1, maybe_escape_chars[i]), "\\" + std::string(1, maybe_escape_chars[i])); + line = mpt::String::Replace(line, std::string(1, c), "\\" + std::string(1, c)); } return line; } @@ -257,7 +257,7 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet path = mpt::String::Split(mpt::ToUnicode(mpt::CharsetUTF8, file.first), MPT_USTRING("/")); - mpt::PathString combinedPath = dirWindows + MPT_PATHSTRING("filetree") + MPT_PATHSTRING("\\"); + std::vector path = mpt::String::Split(mpt::ToUnicode(mpt::CharsetUTF8, file.first), U_("/")); + mpt::PathString combinedPath = dirWindows + P_("filetree") + P_("\\"); if(path.size() > 1) { for(std::size_t singlepath = 0; singlepath < path.size() - 1; ++singlepath) @@ -362,17 +362,17 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet terminals; - terminals.push_back("x-terminal-emulator"); - terminals.push_back("konsole"); - terminals.push_back("mate-terminal"); - terminals.push_back("xfce4-terminal"); - terminals.push_back("gnome-terminal"); - terminals.push_back("uxterm"); - terminals.push_back("xterm"); - terminals.push_back("rxvt"); - std::map terminalLanchers; - for(std::size_t i = 0; i < terminals.size(); ++i) + static constexpr const char * terminals[] = + { + "x-terminal-emulator", + "konsole", + "mate-terminal", + "xfce4-terminal", + "gnome-terminal", + "uxterm", + "xterm", + "rxvt", + }; + std::string terminalscript = "\n"; + for(const std::string terminal : terminals) { // mate-terminal on Debian 8 cannot execute commands with arguments, // thus we use a separate script that requires no arguments to execute. - terminalLanchers[terminals[i]] += std::string() + "if command -v " + terminals[i] + " 2>/dev/null 1>/dev/null ; then" + "\n"; - terminalLanchers[terminals[i]] += std::string() + " chmod u+x " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" + "\n"; - terminalLanchers[terminals[i]] += std::string() + " exec `command -v " + terminals[i] + "` -e \"" + EscapePosixShell(dirPosix) + "wrapperstarter.sh\"" + "\n"; - terminalLanchers[terminals[i]] += std::string() + "fi" + "\n"; - } - - std::string terminalscript; - - terminalscript += std::string() + "\n"; - - for(std::size_t i = 0; i < terminals.size(); ++i) - { - terminalscript += terminalLanchers[terminals[i]]; + terminalscript += "if command -v " + terminal + " 2>/dev/null 1>/dev/null ; then" "\n"; + terminalscript += " chmod u+x " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" "\n"; + terminalscript += " exec `command -v " + terminal + "` -e \"" + EscapePosixShell(dirPosix) + "wrapperstarter.sh\"" "\n"; + terminalscript += "fi" "\n"; } tempfile << terminalscript; @@ -508,7 +501,8 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet paths; - paths.push_back(dirWindows + MPT_PATHSTRING("filetree")); - mpt::PathString basePath = (dirWindows + MPT_PATHSTRING("filetree")).EnsureTrailingSlash(); + paths.push_back(dirWindows + P_("filetree")); + mpt::PathString basePath = (dirWindows + P_("filetree")).EnsureTrailingSlash(); while(!paths.empty()) { mpt::PathString path = paths.front(); paths.pop_front(); path.EnsureTrailingSlash(); HANDLE hFind = NULL; - WIN32_FIND_DATAW wfd; + WIN32_FIND_DATA wfd; MemsetZero(wfd); - hFind = FindFirstFileW((path + MPT_PATHSTRING("*.*")).AsNative().c_str(), &wfd); + hFind = FindFirstFile((path + P_("*.*")).AsNative().c_str(), &wfd); if(hFind != NULL && hFind != INVALID_HANDLE_VALUE) { do { mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName); - if(filename != MPT_PATHSTRING(".") && filename != MPT_PATHSTRING("..")) + if(filename != P_(".") && filename != P_("..")) { filename = path + filename; filetree[filename.ToUTF8()] = std::vector(); @@ -693,7 +687,7 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSetnIdLength < 30 && m_Idarray.size() > 0) ? ID(&m_Idarray[pRe->nIdpos], pRe->nIdLength).AsString() : MPT_USTRING(""), + (pRe && pRe->nIdLength < 30 && m_Idarray.size() > 0) ? ID(&m_Idarray[pRe->nIdpos], pRe->nIdLength).AsString() : U_(""), (pRe) ? pRe->rposStart : 0, - (pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : MPT_USTRING(""), - MPT_USTRING(""))); + (pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : U_(""), + U_(""))); #ifndef SSB_LOGGING MPT_UNREFERENCED_PARAMETER(pRe); MPT_UNREFERENCED_PARAMETER(nNum); @@ -242,7 +242,7 @@ void SsbWrite::WriteMapItem(const ID &id, const char* pszDesc) { SSB_LOG(mpt::format(mpt::ustring(tstrMapEntryWrite))( - (id.GetSize() > 0) ? id.AsString() : MPT_USTRING(""), + (id.GetSize() > 0) ? id.AsString() : U_(""), rposDataStart, nDatasize)); @@ -285,8 +285,6 @@ void SsbWrite::IncrementWriteCounter() void SsbWrite::BeginWrite(const ID &id, const uint64& nVersion) { - std::ostream& oStrm = *m_pOstrm; - SSB_LOG(mpt::format(mpt::ustring(tstrWriteHeader))(id.AsString())); ResetWritestatus(); @@ -368,7 +366,7 @@ SsbRead::ReadRv SsbRead::OnReadEntry(const ReadEntry* pE, const ID &id, const Po { ReadEntry e; e.rposStart = static_cast(posReadBegin - m_posStart); - e.nSize = static_cast(m_pIstrm->tellg() - posReadBegin); + e.nSize = static_cast(iStrm.tellg() - posReadBegin); AddReadNote(&e, m_nCounter); } else // Entry not found. @@ -386,7 +384,7 @@ SsbRead::ReadRv SsbRead::OnReadEntry(const ReadEntry* pE, const ID &id, const Po void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite) { - const Offtype nRawEntrySize = m_pOstrm->tellp() - posBeforeWrite; + const Offtype nRawEntrySize = oStrm.tellp() - posBeforeWrite; if (nRawEntrySize < 0 || static_cast(nRawEntrySize) > std::numeric_limits::max()) { AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE); return; } @@ -402,7 +400,7 @@ void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite) if(nEntrySize <= m_nFixedEntrySize) { for(uint32 i = 0; iput(0); + oStrm.put(0); nEntrySize = m_nFixedEntrySize; } else @@ -418,8 +416,6 @@ void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite) void SsbRead::BeginRead(const ID &id, const uint64& nVersion) { - std::istream& iStrm = *m_pIstrm; - SSB_LOG(mpt::format(mpt::ustring(tstrReadingHeader))(id.AsString())); ResetReadstatus(); @@ -444,7 +440,7 @@ void SsbRead::BeginRead(const ID &id, const uint64& nVersion) uint8 storedIdLen = 0; Binaryread(iStrm, storedIdLen); char storedIdBuf[256]; - MemsetZero(storedIdBuf); + Clear(storedIdBuf); if(storedIdLen > 0) { iStrm.read(storedIdBuf, storedIdLen); @@ -574,7 +570,6 @@ void SsbRead::BeginRead(const ID &id, const uint64& nVersion) void SsbRead::CacheMap() { - std::istream& iStrm = *m_pIstrm; if(GetFlag(RwfRwHasMap) || m_nFixedEntrySize > 0) { iStrm.seekg(m_posStart + m_rposMapBegin); @@ -649,7 +644,7 @@ void SsbRead::CacheMap() SetFlag(RwfRMapCached, true); m_posDataBegin = (m_rposMapBegin == m_rposEndofHdrData) ? m_posMapEnd : m_posStart + Postype(m_rposEndofHdrData); - m_pIstrm->seekg(m_posDataBegin); + iStrm.seekg(m_posDataBegin); // If there are no positions in the map but there are entry sizes, rposStart will // be relative to data start. Now that posDataBegin is known, make them relative to @@ -665,12 +660,12 @@ void SsbRead::CacheMap() const ReadEntry* SsbRead::Find(const ID &id) { - m_pIstrm->clear(); + iStrm.clear(); if (GetFlag(RwfRMapCached) == false) CacheMap(); if (m_nFixedEntrySize > 0 && GetFlag(RwfRMapHasStartpos) == false && GetFlag(RwfRMapHasSize) == false) - m_pIstrm->seekg(m_posDataBegin + Postype(m_nFixedEntrySize * m_nCounter)); + iStrm.seekg(m_posDataBegin + Postype(m_nFixedEntrySize * m_nCounter)); if (GetFlag(RwfRMapHasId) == true) { @@ -682,7 +677,7 @@ const ReadEntry* SsbRead::Find(const ID &id) { m_nNextReadHint = (i + 1) % nEntries; if (mapData[i].rposStart != 0) - m_pIstrm->seekg(m_posStart + Postype(mapData[i].rposStart)); + iStrm.seekg(m_posStart + Postype(mapData[i].rposStart)); return &mapData[i]; } } @@ -693,7 +688,6 @@ const ReadEntry* SsbRead::Find(const ID &id) void SsbWrite::FinishWrite() { - std::ostream& oStrm = *m_pOstrm; const Postype posDataEnd = oStrm.tellp(); Postype posMapStart = oStrm.tellp(); @@ -711,7 +705,7 @@ void SsbWrite::FinishWrite() oStrm.seekp(m_posEntrycount); // Write a fixed size=2 Adaptive64LE because space for this value has already been reserved berforehand. - mpt::IO::WriteAdaptiveInt64LE(oStrm, m_nCounter, 2, 2); + mpt::IO::WriteAdaptiveInt64LE(oStrm, m_nCounter, 2); if (GetFlag(RwfRwHasMap)) { // Write map start position. @@ -719,7 +713,7 @@ void SsbWrite::FinishWrite() const uint64 rposMap = posMapStart - m_posStart; // Write a fixed size=8 Adaptive64LE because space for this value has already been reserved berforehand. - mpt::IO::WriteAdaptiveInt64LE(oStrm, rposMap, 8, 8); + mpt::IO::WriteAdaptiveInt64LE(oStrm, rposMap, 8); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h index 0d31c68fe..51984f946 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h +++ b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h @@ -10,8 +10,9 @@ #pragma once -#include "../common/typedefs.h" -#include "../common/mptTypeTraits.h" +#include "BuildSettings.h" + +#include "../common/mptBaseTypes.h" #include "../common/mptIO.h" #include "../common/Endianness.h" @@ -236,7 +237,7 @@ public: STATIC_ASSERT(std::numeric_limits::is_integer); typename mpt::make_le::type valle; valle = val; - return ID(std::string(mpt::as_raw_memory(valle), mpt::as_raw_memory(valle) + sizeof(valle))); + return ID(std::string(mpt::byte_cast(mpt::as_raw_memory(valle).data()), mpt::byte_cast(mpt::as_raw_memory(valle).data() + sizeof(valle)))); } bool IsPrintable() const; mpt::ustring AsString() const; @@ -286,7 +287,7 @@ protected: protected: - static const uint8 s_DefaultFlagbyte = 0; + enum : uint8 { s_DefaultFlagbyte = 0 }; static const char s_EntryID[3]; }; @@ -374,7 +375,7 @@ private: // immutable when reading, there is no need to ever invalidate the cache and // redo CacheMap(). - std::istream* m_pIstrm; // Read: Pointer to read stream. + std::istream& iStrm; std::vector m_Idarray; // Read: Holds entry ids. @@ -437,7 +438,7 @@ private: private: - std::ostream* m_pOstrm; // Write: Pointer to write stream. + std::ostream& oStrm; Postype m_posEntrycount; // Write: Pos of entrycount field. Postype m_posMapPosField; // Write: Pos of map position field. @@ -449,8 +450,8 @@ private: template void SsbWrite::WriteItem(const T& obj, const ID &id, FuncObj Func) { - const Postype pos = m_pOstrm->tellp(); - Func(*m_pOstrm, obj); + const Postype pos = oStrm.tellp(); + Func(oStrm, obj); OnWroteItem(id, pos); } @@ -458,9 +459,9 @@ template SsbRead::ReadRv SsbRead::ReadItem(T& obj, const ID &id, FuncObj Func) { const ReadEntry* pE = Find(id); - const Postype pos = m_pIstrm->tellg(); + const Postype pos = iStrm.tellg(); if (pE != nullptr || GetFlag(RwfRMapHasId) == false) - Func(*m_pIstrm, obj, (pE) ? (pE->nSize) : invalidDatasize); + Func(iStrm, obj, (pE) ? (pE->nSize) : invalidDatasize); return OnReadEntry(pE, id, pos); } @@ -468,11 +469,11 @@ SsbRead::ReadRv SsbRead::ReadItem(T& obj, const ID &id, FuncObj Func) template SsbRead::ReadRv SsbRead::ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func) { - m_pIstrm->clear(); + iStrm.clear(); if (iter->rposStart != 0) - m_pIstrm->seekg(m_posStart + Postype(iter->rposStart)); - const Postype pos = m_pIstrm->tellg(); - func(*m_pIstrm, obj, iter->nSize); + iStrm.seekg(m_posStart + Postype(iter->rposStart)); + const Postype pos = iStrm.tellg(); + func(iStrm, obj, iter->nSize); return OnReadEntry(&(*iter), ID(&m_Idarray[iter->nIdpos], iter->nIdLength), pos); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/stdafx.cpp b/Frameworks/OpenMPT/OpenMPT/common/stdafx.cpp deleted file mode 100644 index 2ab2668a2..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/stdafx.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * StdAfx.cpp - * ---------- - * Purpose: Source file that includes just the standard includes - * Notes : mptrack.pch will be the pre-compiled header - * stdafx.obj will contain the pre-compiled type information - * Authors: Olivier Lapicque - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#include "stdafx.h" - - -OPENMPT_NAMESPACE_BEGIN - -MPT_MSVC_WORKAROUND_LNK4221(stdafx) - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h index 8cf4b5d5e..a6a36a56c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h +++ b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h @@ -22,12 +22,23 @@ #if !defined(MPT_BUILD_WINESUPPORT) -#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // Do not include support for MFC controls in dialogs (reduces binary bloat; remove this #define if you want to use MFC controls) -#include // MFC core and standard components +// cppcheck-suppress missingInclude +#include // MFC core +// cppcheck-suppress missingInclude +#include // MFC standard components +// cppcheck-suppress missingInclude #include // MFC extensions -#include // MFC support for Windows Common Controls +// cppcheck-suppress missingInclude +#include // MFC support for Windows Common Controls +// cppcheck-suppress missingInclude #include +// cppcheck-suppress missingInclude #include +#ifdef MPT_MFC_FULL +// cppcheck-suppress missingInclude +#include +#endif // MPT_MFC_FULL +// cppcheck-suppress missingInclude #include #endif // !MPT_BUILD_WINESUPPORT @@ -47,27 +58,54 @@ #endif -#if MPT_OS_WINDOWS -#if MPT_COMPILER_MSVCCLANGC2 -// windows.h references IUnknown in a template function without having it even forward-declared. -// Clang does not like that. Forward-declaration fixes it. -struct IUnknown; -#endif -#endif - - // this will be available everywhere -#include "../common/typedefs.h" -// -// -// +#include "../common/mptBaseMacros.h" +// // // -// -#include "../common/mptTypeTraits.h" +#include "../common/mptBaseTypes.h" +// "mptBaseMacros.h" +// +// // +// + +#include "../common/mptAssert.h" +// "mptBaseMacros.h" + +#include "../common/mptBaseUtils.h" +// +// +// +// +// + +#include "../common/mptException.h" +// +// +// + +#include "../common/mptSpan.h" +// "mptBaseTypes.h" +// +// + +#include "../common/mptMemory.h" +// "mptAssert.h" +// "mptBaseTypes.h" +// "mptSpan.h" +// +// + +#include "../common/mptAlloc.h" +// "mptBaseMacros.h" +// "mptMemory.h" +// "mptSpan.h" +// +// +// #include "../common/mptString.h" // @@ -76,6 +114,11 @@ struct IUnknown; // // +#include "../common/mptExceptionText.h" +// "mptException.h" +// "mptString.h" +// + #include "../common/mptStringFormat.h" #include "../common/mptPathString.h" @@ -83,15 +126,6 @@ struct IUnknown; #include "../common/Logging.h" #include "../common/misc_util.h" -// -// -// -// -// -// -// -// -// // for std::abs #include @@ -99,26 +133,6 @@ struct IUnknown; #include #include -#if defined(MPT_ENABLE_FILEIO_STDIO) -// for FILE* definition (which cannot be forward-declared in a portable way) -#include -#include -#endif - -#ifndef NO_VST -// VST SDK includes these headers after messing with default compiler structure -// packing. No problem in practice as VST SDK sets packing matching the default -// packing and we are compiling with default packing and standard headers should -// be careful about structure packing anyway, but it is very much unclean -// nonetheless. Pre-include the affected headers here as a future-proof -// safe-guard and let their own include guards handle the further including by -// VST SDK. -#include -#include -#include -#include -#endif - //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line. diff --git a/Frameworks/OpenMPT/OpenMPT/common/typedefs.cpp b/Frameworks/OpenMPT/OpenMPT/common/typedefs.cpp deleted file mode 100644 index 70e9dbb8d..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/typedefs.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * typedefs.cpp - * ------------ - * Purpose: Basic data type definitions and assorted compiler-related helpers. - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#include "stdafx.h" -#include "typedefs.h" - -#include "Endianness.h" - - -OPENMPT_NAMESPACE_BEGIN - -#if MPT_PLATFORM_ENDIAN_KNOWN - -MPT_MSVC_WORKAROUND_LNK4221(typedefs) - -#else - -int24::int24(int other) -{ - MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { - bytes[0] = (static_cast(other)>>16)&0xff; - bytes[1] = (static_cast(other)>> 8)&0xff; - bytes[2] = (static_cast(other)>> 0)&0xff; - } else { - bytes[0] = (static_cast(other)>> 0)&0xff; - bytes[1] = (static_cast(other)>> 8)&0xff; - bytes[2] = (static_cast(other)>>16)&0xff; - } -} - -int24::operator int() const -{ - MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) { - return (static_cast(bytes[0]) * 65536) + (bytes[1] * 256) + bytes[2]; - } else { - return (static_cast(bytes[2]) * 65536) + (bytes[1] * 256) + bytes[0]; - } -} - -#endif - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/typedefs.h b/Frameworks/OpenMPT/OpenMPT/common/typedefs.h deleted file mode 100644 index 88e0e3f99..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/typedefs.h +++ /dev/null @@ -1,544 +0,0 @@ -/* - * typedefs.h - * ---------- - * Purpose: Basic data type definitions and assorted compiler-related helpers. - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - - - -OPENMPT_NAMESPACE_BEGIN - - - -// Advanced inline attributes -#if MPT_COMPILER_MSVC -#define MPT_FORCEINLINE __forceinline -#define MPT_NOINLINE __declspec(noinline) -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 -#define MPT_FORCEINLINE __attribute__((always_inline)) inline -#define MPT_NOINLINE __attribute__((noinline)) -#else -#define MPT_FORCEINLINE inline -#define MPT_NOINLINE -#endif - - - -// constexpr -#define MPT_CONSTEXPR11_FUN constexpr MPT_FORCEINLINE -#define MPT_CONSTEXPR11_VAR constexpr -#if MPT_CXX_AT_LEAST(14) -#define MPT_CONSTEXPR14_FUN constexpr MPT_FORCEINLINE -#define MPT_CONSTEXPR14_VAR constexpr -#else -#define MPT_CONSTEXPR14_FUN MPT_FORCEINLINE -#define MPT_CONSTEXPR14_VAR const -#endif - - - -// C++17 std::size -OPENMPT_NAMESPACE_END -#include -OPENMPT_NAMESPACE_BEGIN -namespace mpt { -template -MPT_CONSTEXPR11_FUN auto size(const T & v) -> decltype(v.size()) -{ - return v.size(); -} -template -MPT_CONSTEXPR11_FUN std::size_t size(const T(&)[N]) noexcept -{ - return N; -} -} // namespace mpt - - - -// MPT_ARRAY_COUNT macro computes the number of elements in a statically-allocated array. -#if MPT_COMPILER_MSVC -OPENMPT_NAMESPACE_END -#include -OPENMPT_NAMESPACE_BEGIN -#define MPT_ARRAY_COUNT(x) _countof(x) -#else -#define MPT_ARRAY_COUNT(x) (sizeof((x))/sizeof((x)[0])) -#endif - - - -// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased. -#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 -#define MPT_RESTRICT __restrict -#else -#define MPT_RESTRICT -#endif - - - -// Some functions might be deprecated although they are still in use. -// Tag them with "MPT_DEPRECATED". -#if MPT_COMPILER_MSVC -#define MPT_DEPRECATED __declspec(deprecated) -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 -#define MPT_DEPRECATED __attribute__((deprecated)) -#else -#define MPT_DEPRECATED -#endif -#if defined(MODPLUG_TRACKER) -#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED -#define MPT_DEPRECATED_LIBOPENMPT -#elif defined(LIBOPENMPT_BUILD) -#define MPT_DEPRECATED_TRACKER -#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED -#else -#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED -#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED -#endif - - - -OPENMPT_NAMESPACE_END -#include -#include -OPENMPT_NAMESPACE_BEGIN - - - -#if MPT_CXX_AT_LEAST(14) -namespace mpt { -using std::make_unique; -} // namespace mpt -#else -namespace mpt { -template -std::unique_ptr make_unique(Args&&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} -} // namespace mpt -#endif - - - -#if MPT_COMPILER_MSVC -#define MPT_CONSTANT_IF(x) \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) \ - if(x) \ - __pragma(warning(pop)) \ -/**/ -#define MPT_MAYBE_CONSTANT_IF(x) \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) \ - if(x) \ - __pragma(warning(pop)) \ -/**/ -#endif - -#if MPT_COMPILER_GCC -#define MPT_MAYBE_CONSTANT_IF(x) \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \ - if(x) \ - _Pragma("GCC diagnostic pop") \ -/**/ -#endif - -#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 -#define MPT_MAYBE_CONSTANT_IF(x) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \ - _Pragma("clang diagnostic ignored \"-Wtype-limits\"") \ - _Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \ - if(x) \ - _Pragma("clang diagnostic pop") \ -/**/ -#endif - -#if !defined(MPT_CONSTANT_IF) -// MPT_CONSTANT_IF disables compiler warnings for conditions that are either always true or always false for some reason (dependent on template arguments for example) -#define MPT_CONSTANT_IF(x) if(x) -#endif - -#if !defined(MPT_MAYBE_CONSTANT_IF) -// MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases). -#define MPT_MAYBE_CONSTANT_IF(x) if(x) -#endif - - - -#if MPT_COMPILER_MSVC -// MSVC warns for the well-known and widespread "do { } while(0)" idiom with warning level 4 ("conditional expression is constant"). -// It does not warn with "while(0,0)". However this again causes warnings with other compilers. -// Solve it with a macro. -#define MPT_DO do -#define MPT_WHILE_0 while(0,0) -#endif - -#ifndef MPT_DO -#define MPT_DO do -#endif -#ifndef MPT_WHILE_0 -#define MPT_WHILE_0 while(0) -#endif - - - -#if MPT_COMPILER_MSVC && defined(UNREFERENCED_PARAMETER) -#define MPT_UNREFERENCED_PARAMETER(x) UNREFERENCED_PARAMETER(x) -#else -#define MPT_UNREFERENCED_PARAMETER(x) (void)(x) -#endif - -#define MPT_UNUSED_VARIABLE(x) MPT_UNREFERENCED_PARAMETER(x) - - - -// Exception handling helpers, because MFC requires explicit deletion of the exception object, -// Thus, always call exactly one of MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() or MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e). - -#if defined(_MFC_VER) - -#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { AfxThrowMemoryException(); } MPT_WHILE_0 -#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( CMemoryException * e ) -#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() MPT_DO { throw; } MPT_WHILE_0 -#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { if(e) { e->Delete(); e = nullptr; } } MPT_WHILE_0 - -#else // !_MFC_VER - -OPENMPT_NAMESPACE_END -#include -OPENMPT_NAMESPACE_BEGIN -#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { throw std::bad_alloc(); } MPT_WHILE_0 -#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( const std::bad_alloc & e ) -#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() MPT_DO { throw; } MPT_WHILE_0 -#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { MPT_UNUSED_VARIABLE(e); } MPT_WHILE_0 - -#endif // _MFC_VER - - - -// Static code checkers might need to get the knowledge of our assertions transferred to them. -#define MPT_CHECKER_ASSUME_ASSERTIONS 1 -//#define MPT_CHECKER_ASSUME_ASSERTIONS 0 - -#ifdef MPT_BUILD_ANALYZED - -#if MPT_COMPILER_MSVC - -#if MPT_CHECKER_ASSUME_ASSERTIONS -#define MPT_CHECKER_ASSUME(x) __analysis_assume(!!(x)) -#endif - -#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 - -#if MPT_CHECKER_ASSUME_ASSERTIONS -#ifdef NDEBUG -#error "Builds for static analyzers depend on std::assert being enabled, but the current build has #define NDEBUG. This makes no sense." -#endif -OPENMPT_NAMESPACE_END -#include -OPENMPT_NAMESPACE_BEGIN -#define MPT_CHECKER_ASSUME(x) assert(!!(x)) -#endif - -#endif // MPT_COMPILER - -#endif // MPT_BUILD_ANALYZED - -#ifndef MPT_CHECKER_ASSUME -#define MPT_CHECKER_ASSUME(x) MPT_DO { } MPT_WHILE_0 -#endif - - - -#if defined(_MFC_VER) - -#if !defined(ASSERT) -#error "MFC is expected to #define ASSERT" -#endif // !defined(ASERRT) -#define MPT_FRAMEWORK_ASSERT_IS_DEFINED - -#if defined(_DEBUG) - #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 -#else // !_DEBUG - #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 -#endif // _DEBUG - -// let MFC handle our asserts -#define MPT_ASSERT_USE_FRAMEWORK 1 - -#else // !_MFC_VER - -#if defined(ASSERT) -#define MPT_FRAMEWORK_ASSERT_IS_DEFINED -#if defined(_DEBUG) - #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 -#else // !_DEBUG - #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 -#endif // _DEBUG -#endif // !defined(ASERRT) - -// handle assert in our own way without relying on some platform-/framework-specific assert implementation -#define MPT_ASSERT_USE_FRAMEWORK 0 - -#endif // _MFC_VER - - -#if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1) - -#define MPT_ASSERT_NOTREACHED() ASSERT(0) -#define MPT_ASSERT(expr) ASSERT((expr)) -#define MPT_ASSERT_MSG(expr, msg) ASSERT((expr) && (msg)) -#if (MPT_FRAMEWORK_ASSERT_IS_ACTIVE == 1) -#define MPT_ASSERT_ALWAYS(expr) ASSERT((expr)) -#define MPT_ASSERT_ALWAYS_MSG(expr, msg) ASSERT((expr) && (msg)) -#else -#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#ifndef MPT_ASSERT_HANDLER_NEEDED -#define MPT_ASSERT_HANDLER_NEEDED -#endif -#endif - -#elif defined(NO_ASSERTS) - -#define MPT_ASSERT_NOTREACHED() MPT_CHECKER_ASSUME(0) -#define MPT_ASSERT(expr) MPT_CHECKER_ASSUME(expr) -#define MPT_ASSERT_MSG(expr, msg) MPT_CHECKER_ASSUME(expr) -#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#ifndef MPT_ASSERT_HANDLER_NEEDED -#define MPT_ASSERT_HANDLER_NEEDED -#endif - -#else // !NO_ASSERTS - -#define MPT_ASSERT_NOTREACHED() MPT_DO { MPT_CONSTANT_IF(!(0)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0 -#define MPT_ASSERT(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#define MPT_ASSERT_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 -#ifndef MPT_ASSERT_HANDLER_NEEDED -#define MPT_ASSERT_HANDLER_NEEDED -#endif - -#endif // NO_ASSERTS - - -#if defined(MPT_ASSERT_HANDLER_NEEDED) -// custom assert handler needed -MPT_NOINLINE void AssertHandler(const char *file, int line, const char *function, const char *expr, const char *msg=nullptr); -#endif // MPT_ASSERT_HANDLER_NEEDED - - - -// Compile time assert. -#define MPT_STATIC_ASSERT(expr) static_assert((expr), "compile time assertion failed: " #expr) - - - -// Macro for marking intentional fall-throughs in switch statements - can be used for static analysis if supported. -#if (MPT_CXX >= 17) - #define MPT_FALLTHROUGH [[fallthrough]] -#elif MPT_COMPILER_MSVC - #define MPT_FALLTHROUGH __fallthrough -#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 - #define MPT_FALLTHROUGH [[clang::fallthrough]] -#elif MPT_COMPILER_GCC && MPT_GCC_AT_LEAST(7,1,0) - #define MPT_FALLTHROUGH __attribute__((fallthrough)) -#elif defined(__has_cpp_attribute) - #if __has_cpp_attribute(fallthrough) - #define MPT_FALLTHROUGH [[fallthrough]] - #else - #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0 - #endif -#else - #define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0 -#endif - - - -OPENMPT_NAMESPACE_END -#include -#include -#include -OPENMPT_NAMESPACE_BEGIN - -typedef std::int8_t int8; -typedef std::int16_t int16; -typedef std::int32_t int32; -typedef std::int64_t int64; -typedef std::uint8_t uint8; -typedef std::uint16_t uint16; -typedef std::uint32_t uint32; -typedef std::uint64_t uint64; - -const int8 int8_min = INT8_MIN; -const int16 int16_min = INT16_MIN; -const int32 int32_min = INT32_MIN; -const int64 int64_min = INT64_MIN; - -const int8 int8_max = INT8_MAX; -const int16 int16_max = INT16_MAX; -const int32 int32_max = INT32_MAX; -const int64 int64_max = INT64_MAX; - -const uint8 uint8_max = UINT8_MAX; -const uint16 uint16_max = UINT16_MAX; -const uint32 uint32_max = UINT32_MAX; -const uint64 uint64_max = UINT64_MAX; - - -// 24-bit integer wrapper (for 24-bit PCM) -struct int24 -{ - uint8 bytes[3]; - int24() { bytes[0] = bytes[1] = bytes[2] = 0; } -#if MPT_PLATFORM_ENDIAN_KNOWN - explicit int24(int other) - { - #ifdef MPT_PLATFORM_BIG_ENDIAN - bytes[0] = (static_cast(other)>>16)&0xff; - bytes[1] = (static_cast(other)>> 8)&0xff; - bytes[2] = (static_cast(other)>> 0)&0xff; - #else - bytes[0] = (static_cast(other)>> 0)&0xff; - bytes[1] = (static_cast(other)>> 8)&0xff; - bytes[2] = (static_cast(other)>>16)&0xff; - #endif - } - operator int() const - { - #ifdef MPT_PLATFORM_BIG_ENDIAN - return (static_cast(bytes[0]) * 65536) + (bytes[1] * 256) + bytes[2]; - #else - return (static_cast(bytes[2]) * 65536) + (bytes[1] * 256) + bytes[0]; - #endif - } -#else - explicit int24(int other); - operator int() const; -#endif -}; -MPT_STATIC_ASSERT(sizeof(int24) == 3); -#define int24_min (0-0x00800000) -#define int24_max (0+0x007fffff) - - -typedef float float32; -MPT_STATIC_ASSERT(sizeof(float32) == 4); - -typedef double float64; -MPT_STATIC_ASSERT(sizeof(float64) == 8); - - -MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); - - -namespace mpt { - -MPT_STATIC_ASSERT(CHAR_BIT == 8); - -MPT_STATIC_ASSERT(sizeof(char) == 1); - -typedef unsigned char byte; -MPT_STATIC_ASSERT(sizeof(mpt::byte) == 1); - -} // namespace mpt - - - -#if MPT_COMPILER_MSVC - - #if defined(_M_X64) - #define MPT_ARCH_BITS 64 - #define MPT_ARCH_BITS_32 0 - #define MPT_ARCH_BITS_64 1 - #elif defined(_M_IX86) - #define MPT_ARCH_BITS 32 - #define MPT_ARCH_BITS_32 1 - #define MPT_ARCH_BITS_64 0 - #endif - -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 - - #if defined(__SIZEOF_POINTER__) - #if (__SIZEOF_POINTER__ == 8) - #define MPT_ARCH_BITS 64 - #define MPT_ARCH_BITS_32 0 - #define MPT_ARCH_BITS_64 1 - #elif (__SIZEOF_POINTER__ == 4) - #define MPT_ARCH_BITS 32 - #define MPT_ARCH_BITS_32 1 - #define MPT_ARCH_BITS_64 0 - #endif - #endif - -#endif // MPT_COMPILER - -// fallback - -#if !defined(MPT_ARCH_BITS) -#include -#include -MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); -#if defined(UINTPTR_MAX) - #if (UINTPTR_MAX == 0xffffffffffffffffull) - #define MPT_ARCH_BITS 64 - #define MPT_ARCH_BITS_32 0 - #define MPT_ARCH_BITS_64 1 - #elif (UINTPTR_MAX == 0xffffffffu) - #define MPT_ARCH_BITS 32 - #define MPT_ARCH_BITS_32 1 - #define MPT_ARCH_BITS_64 0 - #endif -#endif // UINTPTR_MAX -#endif // MPT_ARCH_BITS - - - -#if MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 -#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) __attribute__((format(printf, formatstringindex, varargsindex))) -#else -#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) -#endif - - - -#if MPT_COMPILER_MSVC -// warning LNK4221: no public symbols found; archive member will be inaccessible -// There is no way to selectively disable linker warnings. -// #pragma warning does not apply and a command line option does not exist. -// Some options: -// 1. Macro which generates a variable with a unique name for each file (which means we have to pass the filename to the macro) -// 2. unnamed namespace containing any symbol (does not work for c++11 compilers because they actually have internal linkage now) -// 3. An unused trivial inline function. -// Option 3 does not actually solve the problem though, which leaves us with option 1. -// In any case, for optimized builds, the linker will just remove the useless symbol. -#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) x##y -#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT(x,y) MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) -#define MPT_MSVC_WORKAROUND_LNK4221(x) int MPT_MSVC_WORKAROUND_LNK4221_CONCAT(mpt_msvc_workaround_lnk4221_,x) = 0; -#endif - -#ifndef MPT_MSVC_WORKAROUND_LNK4221 -#define MPT_MSVC_WORKAROUND_LNK4221(x) -#endif - - - -// legacy -#define CountOf(x) MPT_ARRAY_COUNT(x) -#define STATIC_ASSERT(x) MPT_STATIC_ASSERT(x) - - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.cpp b/Frameworks/OpenMPT/OpenMPT/common/version.cpp index c3406a919..60ad87c54 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/version.cpp @@ -17,98 +17,86 @@ #include "versionNumber.h" #include "svn_version.h" + + OPENMPT_NAMESPACE_BEGIN -namespace MptVersion { -static_assert((MPT_VERSION_NUMERIC & 0xffff) != 0x0000, "Version numbers ending in .00.00 shall never exist again, as they make interpreting the version number ambiguous for file formats which can only store the two major parts of the version number (e.g. IT and S3M)."); -const VersionNum num = MPT_VERSION_NUMERIC; +static_assert((MPT_VERSION_CURRENT.GetRawVersion() & 0xffffu) != 0x0000u, "Version numbers ending in .00.00 shall never exist again, as they make interpreting the version number ambiguous for file formats which can only store the two major parts of the version number (e.g. IT and S3M)."); -const char * const str = MPT_VERSION_STR; -std::string GetOpenMPTVersionStr() + +Version Version::Current() noexcept { - return std::string("OpenMPT " MPT_VERSION_STR); + return MPT_VERSION_CURRENT; } -VersionNum ToNum(const std::string &s) +mpt::ustring Version::GetOpenMPTVersionString() const { - VersionNum result = 0; - std::vector numbers = mpt::String::Split(s, std::string(".")); - for(std::size_t i = 0; i < numbers.size() && i < 4; ++i) + return U_("OpenMPT ") + ToUString(); +} + +Version Version::Parse(const mpt::ustring &s) +{ + uint32 result = 0; + std::vector numbers = mpt::String::Split(s, U_(".")); + for (std::size_t i = 0; i < numbers.size() && i < 4; ++i) { - result |= (mpt::String::Parse::Hex(numbers[i]) & 0xff) << ((3-i)*8); + result |= (mpt::String::Parse::Hex(numbers[i]) & 0xff) << ((3 - i) * 8); } - return result; - + return Version(result); } -std::string ToStr(const VersionNum v) +mpt::ustring Version::ToUString() const { + uint32 v = m_Version; if(v == 0) { // Unknown version - return "Unknown"; + return U_("Unknown"); } else if((v & 0xFFFF) == 0) { // Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header) - return mpt::format("%1.%2")(mpt::fmt::HEX((v >> 24) & 0xFF), mpt::fmt::HEX0<2>((v >> 16) & 0xFF)); + return mpt::format(U_("%1.%2"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF)); } else { // Full version info available - return mpt::format("%1.%2.%3.%4")(mpt::fmt::HEX((v >> 24) & 0xFF), mpt::fmt::HEX0<2>((v >> 16) & 0xFF), mpt::fmt::HEX0<2>((v >> 8) & 0xFF), mpt::fmt::HEX0<2>((v) & 0xFF)); + return mpt::format(U_("%1.%2.%3.%4"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF), mpt::ufmt::HEX0<2>((v >> 8) & 0xFF), mpt::ufmt::HEX0<2>((v) & 0xFF)); } } -mpt::ustring ToUString(const VersionNum v) +Version Version::WithoutTestNumber() const noexcept { - if(v == 0) - { - // Unknown version - return MPT_USTRING("Unknown"); - } else if((v & 0xFFFF) == 0) - { - // Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header) - return mpt::format(MPT_USTRING("%1.%2"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF)); - } else - { - // Full version info available - return mpt::format(MPT_USTRING("%1.%2.%3.%4"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF), mpt::ufmt::HEX0<2>((v >> 8) & 0xFF), mpt::ufmt::HEX0<2>((v) & 0xFF)); - } + return Version(m_Version & 0xFFFFFF00u); } -VersionNum RemoveBuildNumber(const VersionNum num_) +Version Version::WithoutPatchOrTestNumbers() const noexcept { - return (num_ & 0xFFFFFF00); + return Version(m_Version & 0xFFFF0000u); } -bool IsTestBuild(const VersionNum num_) +bool Version::IsTestVersion() const noexcept { return ( // Legacy - (num_ > MAKE_VERSION_NUMERIC(1,17,02,54) && num_ < MAKE_VERSION_NUMERIC(1,18,02,00) && num_ != MAKE_VERSION_NUMERIC(1,18,00,00)) + (*this > MAKE_VERSION_NUMERIC(1,17,02,54) && *this < MAKE_VERSION_NUMERIC(1,18,02,00) && *this != MAKE_VERSION_NUMERIC(1,18,00,00)) || // Test builds have non-zero VER_MINORMINOR - (num_ > MAKE_VERSION_NUMERIC(1,18,02,00) && RemoveBuildNumber(num_) != num_) + (*this > MAKE_VERSION_NUMERIC(1,18,02,00) && ((m_Version & 0xFFFFFF00u) != m_Version)) ); } -bool IsDebugBuild() -{ - #ifdef _DEBUG - return true; - #else - return false; - #endif -} -static std::string GetUrl() + +namespace Source { + +static mpt::ustring GetUrl() { #ifdef OPENMPT_VERSION_URL - return OPENMPT_VERSION_URL; + return mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_VERSION_URL); #else - return ""; + return mpt::ustring(); #endif } @@ -146,7 +134,7 @@ static int GetRevision() #else #if MPT_COMPILER_MSVC #pragma message("SVN revision unknown. Please check your build system.") - #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 + #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG #warning "SVN revision unknown. Please check your build system." #else // There is no portable way to display a warning. @@ -218,281 +206,302 @@ static bool IsPackage() #endif } -static std::string GetSourceDate() +static mpt::ustring GetSourceDate() { #if defined(OPENMPT_VERSION_DATE) - return OPENMPT_VERSION_DATE; + return mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_VERSION_DATE); #else - return ""; + return mpt::ustring(); #endif } -SourceInfo GetSourceInfo() +} // namespace Source + +SourceInfo::SourceInfo() + : m_Url(Source::GetUrl()) + , m_Revision(Source::GetRevision()) + , m_IsDirty(Source::IsDirty()) + , m_HasMixedRevisions(Source::HasMixedRevisions()) + , m_IsPackage(Source::IsPackage()) + , m_Date(Source::GetSourceDate()) { - SourceInfo result; - result.Url = GetUrl(); - result.Revision = GetRevision(); - result.IsDirty = IsDirty(); - result.HasMixedRevisions = HasMixedRevisions(); - result.IsPackage = IsPackage(); - result.Date = GetSourceDate(); - return result; } -std::string SourceInfo::GetStateString() const +mpt::ustring SourceInfo::GetUrlWithRevision() const { - std::string retval; - if(IsDirty) + if(m_Url.empty() || (m_Revision == 0)) { - retval += "+dirty"; + return mpt::ustring(); } - if(HasMixedRevisions) + return m_Url + UL_("@") + mpt::ufmt::val(m_Revision); +} + +mpt::ustring SourceInfo::GetStateString() const +{ + mpt::ustring retval; + if(m_IsDirty) { - retval += "+mixed"; + retval += UL_("+dirty"); + } + if(m_HasMixedRevisions) + { + retval += UL_("+mixed"); } if(retval.empty()) { - retval += "clean"; + retval += UL_("clean"); } - if(IsPackage) + if(m_IsPackage) { - retval += "-pkg"; + retval += UL_("-pkg"); } return retval; } -std::string GetBuildDateString() +SourceInfo SourceInfo::Current() { - #ifdef MODPLUG_TRACKER - #if defined(OPENMPT_BUILD_DATE) - return OPENMPT_BUILD_DATE; - #else - return __DATE__ " " __TIME__ ; - #endif - #else // !MODPLUG_TRACKER - return GetSourceInfo().Date; - #endif // MODPLUG_TRACKER + return SourceInfo(); } -static std::string GetBuildFlagsString() + + +namespace Build { + +bool IsReleasedBuild() { - std::string retval; + return !(Version::Current().IsTestVersion() || IsDebugBuild() || Source::IsDirty() || Source::HasMixedRevisions()); +} + +bool IsDebugBuild() +{ + #if defined(MPT_BUILD_DEBUG) || defined(DEBUG) || defined(_DEBUG) + return true; + #else + return false; + #endif +} + +mpt::ustring GetBuildDateString() +{ + mpt::ustring result; #ifdef MODPLUG_TRACKER - if(IsTestBuild()) + #if defined(OPENMPT_BUILD_DATE) + result = mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_BUILD_DATE ); + #else + result = mpt::ToUnicode(mpt::CharsetASCII, __DATE__ " " __TIME__ ); + #endif + #else // !MODPLUG_TRACKER + result = SourceInfo::Current().Date(); + #endif // MODPLUG_TRACKER + return result; +} + +static mpt::ustring GetBuildFlagsString() +{ + mpt::ustring retval; + #ifdef MODPLUG_TRACKER + if(Version::Current().IsTestVersion()) { - retval += " TEST"; + retval += UL_(" TEST"); } #endif // MODPLUG_TRACKER if(IsDebugBuild()) { - retval += " DEBUG"; + retval += UL_(" DEBUG"); } return retval; } -std::string GetBuildFeaturesString() +mpt::ustring GetBuildFeaturesString() { - std::string retval; + mpt::ustring retval; #ifdef LIBOPENMPT_BUILD + retval = UL_("") #if defined(MPT_CHARSET_WIN32) - retval += " +WINAPI"; + UL_(" +WINAPI") #endif #if defined(MPT_CHARSET_ICONV) - retval += " +ICONV"; + UL_(" +ICONV") #endif #if defined(MPT_CHARSET_CODECVTUTF8) - retval += " +CODECVTUTF8"; + UL_(" +CODECVTUTF8") #endif #if defined(MPT_CHARSET_INTERNAL) - retval += " +INTERNALCHARSETS"; + UL_(" +INTERNALCHARSETS") #endif #if defined(MPT_WITH_ZLIB) - retval += " +ZLIB"; + UL_(" +ZLIB") #endif #if defined(MPT_WITH_MINIZ) - retval += " +MINIZ"; + UL_(" +MINIZ") #endif #if !defined(MPT_WITH_ZLIB) && !defined(MPT_WITH_MINIZ) - retval += " -INFLATE"; + UL_(" -INFLATE") #endif #if defined(MPT_WITH_MPG123) - retval += " +MPG123"; + UL_(" +MPG123") #endif #if defined(MPT_WITH_MINIMP3) - retval += " +MINIMP3"; + UL_(" +MINIMP3") #endif #if defined(MPT_WITH_MEDIAFOUNDATION) - retval += " +MF"; + UL_(" +MF") #endif #if !defined(MPT_WITH_MPG123) && !defined(MPT_WITH_MINIMP3) && !defined(MPT_WITH_MEDIAFOUNDATION) - retval += " -MP3"; + UL_(" -MP3") #endif #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) - retval += " +VORBIS"; + UL_(" +VORBIS") #endif #if defined(MPT_WITH_STBVORBIS) - retval += " +STBVORBIS"; + UL_(" +STBVORBIS") #endif #if !(defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)) && !defined(MPT_WITH_STBVORBIS) - retval += " -VORBIS"; + UL_(" -VORBIS") #endif #if !defined(NO_PLUGINS) - retval += " +PLUGINS"; + UL_(" +PLUGINS") #else - retval += " -PLUGINS"; + UL_(" -PLUGINS") #endif #if !defined(NO_DMO) - retval += " +DMO"; + UL_(" +DMO") #endif + ; #endif #ifdef MODPLUG_TRACKER - #if (MPT_ARCH_BITS == 64) + MPT_CONSTANT_IF(mpt::arch_bits == 64) + { if (true && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP64) && (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP64) ) { - retval += " WIN64OLD"; + retval += UL_(" WIN64OLD"); } - #elif (MPT_ARCH_BITS == 32) + } else MPT_CONSTANT_IF(mpt::arch_bits == 32) + { if (true && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP) && (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP) ) { - retval += " WIN32OLD"; + retval += UL_(" WIN32OLD"); } - #endif + } + retval += UL_("") #if defined(UNICODE) - retval += " UNICODE"; + UL_(" UNICODE") #else - retval += " ANSI"; + UL_(" ANSI") #endif #ifdef NO_VST - retval += " NO_VST"; + UL_(" NO_VST") #endif #ifdef NO_DMO - retval += " NO_DMO"; + UL_(" NO_DMO") #endif #ifdef NO_PLUGINS - retval += " NO_PLUGINS"; + UL_(" NO_PLUGINS") #endif #ifndef MPT_WITH_ASIO - retval += " NO_ASIO"; - #endif - #ifndef MPT_WITH_DSOUND - retval += " NO_DSOUND"; + UL_(" NO_ASIO") #endif + ; #endif return retval; } -std::string GetBuildCompilerString() +mpt::ustring GetBuildCompilerString() { - std::string retval; + mpt::ustring retval; #if MPT_COMPILER_GENERIC - retval += "*Generic C++11 Compiler"; + retval += U_("Generic C++11 Compiler"); #elif MPT_COMPILER_MSVC #if defined(_MSC_FULL_VER) && defined(_MSC_BUILD) && (_MSC_BUILD > 0) - retval += mpt::format("Microsoft Compiler %1.%2.%3.%4") + retval += mpt::format(U_("Microsoft Compiler %1.%2.%3.%4")) ( _MSC_FULL_VER / 10000000 - , mpt::fmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) - , mpt::fmt::dec0<5>(_MSC_FULL_VER % 100000) - , mpt::fmt::dec0<2>(_MSC_BUILD) + , mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) + , mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000) + , mpt::ufmt::dec0<2>(_MSC_BUILD) ); #elif defined(_MSC_FULL_VER) - retval += mpt::format("Microsoft Compiler %1.%2.%3") + retval += mpt::format(U_("Microsoft Compiler %1.%2.%3")) ( _MSC_FULL_VER / 10000000 - , mpt::fmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) - , mpt::fmt::dec0<5>(_MSC_FULL_VER % 100000) + , mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) + , mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000) ); #else - retval += mpt::format("Microsoft Compiler %1.%2")(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100); + retval += mpt::format(U_("Microsoft Compiler %1.%2"))(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100); #endif #elif MPT_COMPILER_GCC - retval += mpt::format("GNU Compiler Collection %1.%2.%3")(MPT_COMPILER_GCC_VERSION / 10000, (MPT_COMPILER_GCC_VERSION / 100) % 100, MPT_COMPILER_GCC_VERSION % 100); + retval += mpt::format(U_("GNU Compiler Collection %1.%2.%3"))(MPT_COMPILER_GCC_VERSION / 10000, (MPT_COMPILER_GCC_VERSION / 100) % 100, MPT_COMPILER_GCC_VERSION % 100); #elif MPT_COMPILER_CLANG - retval += mpt::format("Clang %1.%2.%3")(MPT_COMPILER_CLANG_VERSION / 10000, (MPT_COMPILER_CLANG_VERSION / 100) % 100, MPT_COMPILER_CLANG_VERSION % 100); - #elif MPT_COMPILER_MSVCCLANGC2 - retval += mpt::format("MSVC-Clang/C2 %1")(MPT_COMPILER_MSVCCLANGC2_VERSION); + retval += mpt::format(U_("Clang %1.%2.%3"))(MPT_COMPILER_CLANG_VERSION / 10000, (MPT_COMPILER_CLANG_VERSION / 100) % 100, MPT_COMPILER_CLANG_VERSION % 100); #else - retval += "*unknown"; + retval += U_("unknown"); #endif return retval; } -static std::string GetRevisionString() +static mpt::ustring GetRevisionString() { - std::string result; - if(GetRevision() == 0) + mpt::ustring result; + if(Source::GetRevision() == 0) { return result; } - result = std::string("-r") + mpt::fmt::val(GetRevision()); - if(HasMixedRevisions()) + result = U_("-r") + mpt::ufmt::val(Source::GetRevision()); + if(Source::HasMixedRevisions()) { - result += "!"; + result += UL_("!"); } - if(IsDirty()) + if(Source::IsDirty()) { - result += "+"; + result += UL_("+"); } - if(IsPackage()) + if(Source::IsPackage()) { - result += "p"; + result += UL_("p"); } return result; } -mpt::ustring GetDownloadURL() +mpt::ustring GetVersionString(FlagSet strings) { - #ifdef MODPLUG_TRACKER - return (MptVersion::IsDebugBuild() || MptVersion::IsTestBuild() || MptVersion::IsDirty() || MptVersion::HasMixedRevisions()) - ? - MPT_USTRING("https://buildbot.openmpt.org/builds/") - : - MPT_USTRING("https://openmpt.org/download") - ; - #else - return MPT_USTRING("https://lib.openmpt.org/"); - #endif -} - -std::string GetVersionString(FlagSet strings) -{ - std::vector result; + std::vector result; if(strings[StringVersion]) { - result.push_back(MPT_VERSION_STR); + result.push_back(mpt::ufmt::val(Version::Current())); } if(strings[StringRevision]) { - if(IsDebugBuild() || IsTestBuild() || IsDirty() || HasMixedRevisions()) + if(!IsReleasedBuild()) { result.push_back(GetRevisionString()); } } if(strings[StringBitness]) { - result.push_back(mpt::format(" %1 bit")(sizeof(void*)*8)); + result.push_back(mpt::format(U_(" %1 bit"))(mpt::arch_bits)); } if(strings[StringSourceInfo]) { - const SourceInfo sourceInfo = GetSourceInfo(); + const SourceInfo sourceInfo = SourceInfo::Current(); if(!sourceInfo.GetUrlWithRevision().empty()) { - result.push_back(mpt::format(" %1")(sourceInfo.GetUrlWithRevision())); + result.push_back(mpt::format(U_(" %1"))(sourceInfo.GetUrlWithRevision())); } - if(!sourceInfo.Date.empty()) + if(!sourceInfo.Date().empty()) { - result.push_back(mpt::format(" (%1)")(sourceInfo.Date)); + result.push_back(mpt::format(U_(" (%1)"))(sourceInfo.Date())); } if(!sourceInfo.GetStateString().empty()) { - result.push_back(mpt::format(" %1")(sourceInfo.GetStateString())); + result.push_back(mpt::format(U_(" %1"))(sourceInfo.GetStateString())); } } if(strings[StringBuildFlags]) { - if(IsDebugBuild() || IsTestBuild() || IsDirty() || HasMixedRevisions()) + if(!IsReleasedBuild()) { result.push_back(GetBuildFlagsString()); } @@ -501,81 +510,78 @@ std::string GetVersionString(FlagSet strings) { result.push_back(GetBuildFeaturesString()); } - return mpt::String::Trim(mpt::String::Combine(result, std::string(""))); + return mpt::String::Trim(mpt::String::Combine(result, U_(""))); } -std::string GetVersionStringPure() +mpt::ustring GetVersionStringPure() { - FlagSet strings; - strings |= MptVersion::StringVersion; - strings |= MptVersion::StringRevision; + FlagSet strings; + strings |= Build::StringVersion; + strings |= Build::StringRevision; #ifdef MODPLUG_TRACKER - strings |= MptVersion::StringBitness; + strings |= Build::StringBitness; #endif return GetVersionString(strings); } -std::string GetVersionStringSimple() +mpt::ustring GetVersionStringSimple() { - FlagSet strings; - strings |= MptVersion::StringVersion; - strings |= MptVersion::StringRevision; - strings |= MptVersion::StringBuildFlags; + FlagSet strings; + strings |= Build::StringVersion; + strings |= Build::StringRevision; + strings |= Build::StringBuildFlags; return GetVersionString(strings); } -std::string GetVersionStringExtended() +mpt::ustring GetVersionStringExtended() { - FlagSet strings; - strings |= MptVersion::StringVersion; - strings |= MptVersion::StringRevision; + FlagSet strings; + strings |= Build::StringVersion; + strings |= Build::StringRevision; #ifdef MODPLUG_TRACKER - strings |= MptVersion::StringBitness; + strings |= Build::StringBitness; #endif #ifndef MODPLUG_TRACKER - strings |= MptVersion::StringSourceInfo; + strings |= Build::StringSourceInfo; #endif - strings |= MptVersion::StringBuildFlags; + strings |= Build::StringBuildFlags; #ifdef MODPLUG_TRACKER - strings |= MptVersion::StringBuildFeatures; + strings |= Build::StringBuildFeatures; #endif return GetVersionString(strings); } -std::string SourceInfo::GetUrlWithRevision() const -{ - if(Url.empty() || (Revision == 0)) - { - return std::string(); - } - return Url + "@" + mpt::fmt::val(Revision); -} - -mpt::ustring GetURL(std::string key) +mpt::ustring GetURL(Build::Url key) { mpt::ustring result; - if(key.empty()) + switch(key) { - result = mpt::ustring(); - } else if(key == "website") - { - #ifdef LIBOPENMPT_BUILD - result = MPT_USTRING("https://lib.openmpt.org/"); - #else - result = MPT_USTRING("https://openmpt.org/"); - #endif - } else if(key == "forum") - { - result = MPT_USTRING("https://forum.openmpt.org/"); - } else if(key == "bugtracker") - { - result = MPT_USTRING("https://bugs.openmpt.org/"); - } else if(key == "updates") - { - result = MPT_USTRING("https://openmpt.org/download"); - } else if(key == "top_picks") - { - result = MPT_USTRING("https://openmpt.org/top_picks"); + case Url::Website: + #ifdef LIBOPENMPT_BUILD + result = U_("https://lib.openmpt.org/"); + #else + result = U_("https://openmpt.org/"); + #endif + break; + case Url::Download: + #ifdef MODPLUG_TRACKER + result = IsReleasedBuild() ? U_("https://openmpt.org/download") : U_("https://builds.openmpt.org/builds/"); + #else + result = U_("https://lib.openmpt.org/libopenmpt/download/"); + #endif + break; + case Url::Forum: + result = U_("https://forum.openmpt.org/"); + break; + case Url::Bugtracker: + result = U_("https://bugs.openmpt.org/"); + break; + case Url::Updates: + result = U_("https://openmpt.org/download"); + break; + case Url::TopPicks: + result = U_("https://openmpt.org/top_picks"); + break; } return result; } @@ -589,12 +595,12 @@ mpt::ustring GetFullCreditsString() "libopenmpt (based on OpenMPT / ModPlug Tracker)\n" #endif "\n" - "Copyright \xC2\xA9 2004-2018 Contributors\n" + "Copyright \xC2\xA9 2004-2019 Contributors\n" "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" "\n" "Contributors:\n" - "Johannes Schultz (2008-2018)\n" - "J\xC3\xB6rn Heusipp (2012-2018)\n" + "Johannes Schultz (2008-2019)\n" + "J\xC3\xB6rn Heusipp (2012-2019)\n" "Ahti Lepp\xC3\xA4nen (2005-2011)\n" "Robin Fernandes (2004-2007)\n" "Sergiy Pylypenko (2007)\n" @@ -603,9 +609,9 @@ mpt::ustring GetFullCreditsString() "Olivier Lapicque (1997-2003)\n" "\n" "Additional patch submitters:\n" - "coda (http://coda.s3m.us/)\n" + "coda (https://coda.s3m.us/)\n" "kode54 (https://kode54.net/)\n" - "Revenant (http://revenant1.net/)\n" + "Revenant (https://revenant1.net/)\n" "xaimus (http://xaimus.com/)\n" "\n" "Thanks to:\n" @@ -621,13 +627,16 @@ mpt::ustring GetFullCreditsString() "https://github.com/avaneev/r8brain-free-src\n" "\n" "Olli Parviainen for SoundTouch Library (time stretching)\n" - "http://www.surina.net/soundtouch/\n" + "https://www.surina.net/soundtouch/\n" "\n" #endif #ifndef NO_VST "Hermann Seib for his example VST Host implementation\n" "http://www.hermannseib.com/english/vsthost.htm\n" "\n" + "Benjamin \"BeRo\" Rosseaux for his independent VST header\n" + "https://blog.rosseaux.net/\n" + "\n" #endif "Storlek for all the IT compatibility hints and testcases\n" "as well as the IMF, MDL, OKT and ULT loaders\n" @@ -645,9 +654,12 @@ mpt::ustring GetFullCreditsString() "Antti S. Lankila for Amiga resampler implementation\n" "https://bel.fi/alankila/modguide/interpolate.txt\n" "\n" + "Shayde / Reality Productions for Opal OPL3 emulator\n" + "https://www.3eality.com/\n" + "\n" #ifdef MPT_WITH_ZLIB "Jean-loup Gailly and Mark Adler for zlib\n" - "http://zlib.net/\n" + "https://zlib.net/\n" "\n" #endif #ifdef MPT_WITH_MINIZ @@ -662,7 +674,7 @@ mpt::ustring GetFullCreditsString() #endif #ifdef MPT_WITH_UNRAR "Alexander L. Roshal for UnRAR\n" - "http://rarlab.com/\n" + "https://rarlab.com/\n" "\n" #endif #ifdef MPT_WITH_PORTAUDIO @@ -670,6 +682,11 @@ mpt::ustring GetFullCreditsString() "http://www.portaudio.com/\n" "\n" #endif +#ifdef MPT_WITH_RTAUDIO + "Gary P. Scavone, McGill University\n" + "https://www.music.mcgill.ca/~gary/rtaudio/\n" + "\n" +#endif #ifdef MPT_WITH_FLAC "Josh Coalson / Xiph.Org Foundation for libFLAC\n" "https://xiph.org/flac/\n" @@ -677,13 +694,12 @@ mpt::ustring GetFullCreditsString() #endif #if defined(MPT_WITH_MPG123) "The mpg123 project for libmpg123\n" - "http://mpg123.de/\n" + "https://mpg123.de/\n" "\n" #endif #ifdef MPT_WITH_MINIMP3 - "Fabrice Bellard, FFMPEG contributors\n" - "and Martin J. Fiedler (KeyJ/kakiarts) for minimp3\n" - "http://keyj.emphy.de/minimp3/\n" + "Lion (github.com/lieff) for minimp3\n" + "https://github.com/lieff/minimp3/\n" "\n" #endif #ifdef MPT_WITH_STBVORBIS @@ -718,9 +734,13 @@ mpt::ustring GetFullCreditsString() "https://git.xiph.org/?p=libopusenc.git;a=summary\n" "\n" #endif -#if defined(MPT_WITH_PICOJSON) - "Cybozu Labs Inc. and Kazuho Oku et. al. for picojson\n" - "https://github.com/kazuho/picojson\n" +#if defined(MPT_WITH_LAME) + "The LAME project for LAME\n" + "http://lame.sourceforge.net/\n" +#endif +#if defined(MPT_WITH_NLOHMANNJSON) + "Niels Lohmann et al. for nlohmann-json\n" + "https://github.com/nlohmann/json\n" "\n" #endif #ifdef MODPLUG_TRACKER @@ -731,9 +751,12 @@ mpt::ustring GetFullCreditsString() "https://www.music.mcgill.ca/~gary/rtmidi/\n" "\n" "Alexander Uckun for decimal input field\n" - "http://www.codeproject.com/Articles/21257/_\n" + "https://www.codeproject.com/Articles/21257/_\n" "\n" - "Nobuyuki for application and file icon\n" + "\xc3\x9alfur Kolka for application icon, splash and about screen\n" + "https://www.behance.net/ulfurkolka\n" + "\n" + "Nobuyuki for file icon\n" "https://twitter.com/nobuyukinyuu\n" "\n" #endif @@ -746,7 +769,7 @@ mpt::ustring GetFullCreditsString() "33, 8bitbubsy, Anboi, BooT-SectoR-ViruZ, Bvanoudtshoorn\n" "christofori, cubaxd, Diamond, Ganja, Georg, Goor00,\n" "Harbinger, jmkz, KrazyKatz, LPChip, Nofold, Rakib, Sam Zen\n" - "Skaven, Skilletaudio, Snu, Squirrel Havoc, Waxhead\n" + "Skaven, Skilletaudio, Snu, Squirrel Havoc, Teimoso, Waxhead\n" "\n" #ifndef NO_VST "VST PlugIn Technology by Steinberg Media Technologies GmbH\n" @@ -762,9 +785,7 @@ mpt::ustring GetFullCreditsString() mpt::ustring GetLicenseString() { return MPT_UTF8( - "The OpenMPT code is licensed under the BSD license." "\n" - "" "\n" - "Copyright (c) 2004-2018, OpenMPT contributors" "\n" + "Copyright (c) 2004-2019, OpenMPT contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" @@ -792,6 +813,8 @@ mpt::ustring GetLicenseString() ); } -} // namespace MptVersion +} // namespace Build + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.h b/Frameworks/OpenMPT/OpenMPT/common/version.h index 305c02567..5a8d33274 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.h +++ b/Frameworks/OpenMPT/OpenMPT/common/version.h @@ -10,76 +10,182 @@ #pragma once -#include "FlagSet.h" +#include "BuildSettings.h" -#include +#include "FlagSet.h" OPENMPT_NAMESPACE_BEGIN -//Creates version number from version parts that appears in version string. -//For example MAKE_VERSION_NUMERIC(1,17,02,28) gives version number of -//version 1.17.02.28. -#define MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) ((prefix##v0 << 24) | (prefix##v1<<16) | (prefix##v2<<8) | (prefix##v3)) -#define MAKE_VERSION_NUMERIC(v0,v1,v2,v3) (MptVersion::VersionNum(MAKE_VERSION_NUMERIC_HELPER(0x,v0,v1,v2,v3))) - - -namespace MptVersion +class Version { - typedef uint32 VersionNum; +private: - extern const VersionNum num; // e.g. 0x01170208 - extern const char * const str; // e.g "1.17.02.08" + uint32 m_Version; // e.g. 0x01170208 - // Return a OpenMPT version string suitable for file format tags - std::string GetOpenMPTVersionStr(); // e.g. "OpenMPT 1.17.02.08" +public: + + enum class Field + { + Major, + Minor, + Patch, + Test, + }; + +public: + + static Version Current() noexcept; + +public: + + MPT_CONSTEXPR11_FUN Version() noexcept + : m_Version(0) + {} + + explicit MPT_CONSTEXPR11_FUN Version(uint32 version) noexcept + : m_Version(version) + {} + + explicit MPT_CONSTEXPR11_FUN Version(uint8 v1, uint8 v2, uint8 v3, uint8 v4) noexcept + : m_Version((static_cast(v1) << 24) | (static_cast(v2) << 16) | (static_cast(v3) << 8) | (static_cast(v4) << 0)) + {} + +public: + + mpt::ustring ToUString() const; // e.g "1.17.02.08" // Returns numerical version value from given version string. - VersionNum ToNum(const std::string &s); + static Version Parse(const mpt::ustring &s); - // Returns version string from given numerical version value. - std::string ToStr(const VersionNum v); - mpt::ustring ToUString(const VersionNum v); +public: + + explicit MPT_CONSTEXPR11_FUN operator bool () const noexcept + { + return m_Version != 0; + } + MPT_CONSTEXPR11_FUN bool operator ! () const noexcept + { + return m_Version == 0; + } + + MPT_CONSTEXPR11_FUN uint32 GetRawVersion() const noexcept + { + return m_Version; + } + + MPT_FORCEINLINE Version Masked(uint32 mask) const noexcept + { + return Version(m_Version & mask); + } + + MPT_CONSTEXPR11_FUN uint8 GetField(Field field) const noexcept + { + return + (field == Field::Major) ? static_cast((m_Version >> 24) & 0xffu) : + (field == Field::Minor) ? static_cast((m_Version >> 16) & 0xffu) : + (field == Field::Patch) ? static_cast((m_Version >> 8) & 0xffu) : + (field == Field::Test ) ? static_cast((m_Version >> 0) & 0xffu) : + 0u; + } // Return a version without build number (the last number in the version). // The current versioning scheme uses this number only for test builds, and it should be 00 for official builds, // So sometimes it might be wanted to do comparisons without the build number. - VersionNum RemoveBuildNumber(const VersionNum num_); + Version WithoutTestNumber() const noexcept; + + Version WithoutPatchOrTestNumbers() const noexcept; + +public: + + // Return a OpenMPT version string suitable for file format tags + mpt::ustring GetOpenMPTVersionString() const; // e.g. "OpenMPT 1.17.02.08" // Returns true if a given version number is from a test build, false if it's a release build. - bool IsTestBuild(const VersionNum num_ = MptVersion::num); + bool IsTestVersion() const noexcept; + +}; + +MPT_CONSTEXPR11_FUN bool operator == (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() == b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator != (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() != b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator <= (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() <= b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator >= (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() >= b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator < (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() < b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator > (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() > b.GetRawVersion(); +} + + +//Creates version number from version parts that appears in version string. +//For example MAKE_VERSION_NUMERIC(1,17,02,28) gives version number of +//version 1.17.02.28. +#define MPT_MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) Version( prefix ## v0 , prefix ## v1 , prefix ## v2 , prefix ## v3 ) +#define MAKE_VERSION_NUMERIC(v0,v1,v2,v3) MPT_MAKE_VERSION_NUMERIC_HELPER(0x, v0, v1, v2, v3) + + + +class SourceInfo +{ +private: + mpt::ustring m_Url; // svn repository url (or empty string) + int m_Revision; // svn revision (or 0) + bool m_IsDirty; // svn working copy is dirty (or false) + bool m_HasMixedRevisions; // svn working copy has mixed revisions (or false) + bool m_IsPackage; // source code originates from a packaged version of the source code + mpt::ustring m_Date; // svn date (or empty string) +private: + SourceInfo(); +public: + static SourceInfo Current(); +public: + const mpt::ustring & Url() const { return m_Url; } + int Revision() const { return m_Revision; } + bool IsDirty() const { return m_IsDirty; } + bool HasMixedRevisions() const { return m_HasMixedRevisions; } + bool IsPackage() const { return m_IsPackage; } + const mpt::ustring & Date() const { return m_Date; } +public: + mpt::ustring GetUrlWithRevision() const; // i.e. "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234" or empty string + mpt::ustring GetStateString() const; // i.e. "+dirty" or "clean" +}; + + + +namespace Build +{ + + // Returns true if all conditions for an official release build are met + bool IsReleasedBuild(); // Return true if this is a debug build with no optimizations bool IsDebugBuild(); - struct SourceInfo - { - std::string Url; // svn repository url (or empty string) - int Revision; // svn revision (or 0) - bool IsDirty; // svn working copy is dirty (or false) - bool HasMixedRevisions; // svn working copy has mixed revisions (or false) - bool IsPackage; // source code originates from a packaged version of the source code - std::string Date; // svn date (or empty string) - SourceInfo() : Url(std::string()), Revision(0), IsDirty(false), HasMixedRevisions(false), IsPackage(false) { } - public: - std::string GetUrlWithRevision() const; // i.e. "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234" or empty string - std::string GetStateString() const; // i.e. "+dirty" or "clean" - }; - SourceInfo GetSourceInfo(); - - // Returns either the URL to download release builds or the URL to download test builds, depending on the current build. - mpt::ustring GetDownloadURL(); - // Return a string decribing the time of the build process (if built from a svn working copy and tsvn was available during build, otherwise it returns the time version.cpp was last rebuild which could be unreliable as it does not get rebuild every time without tsvn) - std::string GetBuildDateString(); + mpt::ustring GetBuildDateString(); // Return a string decribing some of the build features - std::string GetBuildFeaturesString(); // e.g. " NO_VST NO_DSOUND" + mpt::ustring GetBuildFeaturesString(); // e.g. " NO_VST NO_DSOUND" // Return a string describing the compiler version used for building. - std::string GetBuildCompilerString(); // e.g. "Microsoft Compiler 15.00.20706.01" + mpt::ustring GetBuildCompilerString(); // e.g. "Microsoft Compiler 15.00.20706.01" enum Strings { @@ -94,19 +200,28 @@ namespace MptVersion MPT_DECLARE_ENUM(Strings) // Returns a versions string with the fields selected via @strings. - std::string GetVersionString(FlagSet strings); + mpt::ustring GetVersionString(FlagSet strings); // Returns a pure version string - std::string GetVersionStringPure(); // e.g. "1.17.02.08-r1234+ 32 bit" + mpt::ustring GetVersionStringPure(); // e.g. "1.17.02.08-r1234+ 32 bit" // Returns a simple version string - std::string GetVersionStringSimple(); // e.g. "1.17.02.08-r1234+ TEST" + mpt::ustring GetVersionStringSimple(); // e.g. "1.17.02.08-r1234+ TEST" - // Returns MptVersion::str if the build is a clean release build straight from the repository or an extended string otherwise (if built from a svn working copy and tsvn was available during build) - std::string GetVersionStringExtended(); // e.g. "1.17.02.08-r1234+ 32 bit DEBUG" + // Returns Version::CurrentAsString() if the build is a clean release build straight from the repository or an extended string otherwise (if built from a svn working copy and tsvn was available during build) + mpt::ustring GetVersionStringExtended(); // e.g. "1.17.02.08-r1234+ 32 bit DEBUG" - // Returns a URL for the respective keys. Supported keys: "website", "forum", "bugtracker", "updates", "top_picks" - mpt::ustring GetURL(std::string key); + enum class Url + { + Website, + Download, + Forum, + Bugtracker, + Updates, + TopPicks, + }; + // Returns a URL for the respective key. + mpt::ustring GetURL(Build::Url key); // Returns a multi-line string containing the full credits for the code base mpt::ustring GetFullCreditsString(); @@ -114,7 +229,8 @@ namespace MptVersion // Returns the OpenMPT license text mpt::ustring GetLicenseString(); -} //namespace MptVersion +} //namespace Build + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 173b6041e..7c63c40ec 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -10,21 +10,20 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN #define VER_HELPER_STRINGIZE(x) #x #define VER_STRINGIZE(x) VER_HELPER_STRINGIZE(x) //Version definitions. The only thing that needs to be changed when changing version number. -#define VER_MAJORMAJOR 1 -#define VER_MAJOR 27 -#define VER_MINOR 10 -#define VER_MINORMINOR 00 - -//Version string. For example "1.17.02.28" -#define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR) +#define VER_MAJORMAJOR 1 +#define VER_MAJOR 28 +#define VER_MINOR 02 +#define VER_MINORMINOR 04 //Numerical value of the version. -#define MPT_VERSION_NUMERIC MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) +#define MPT_VERSION_CURRENT MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict index 8761c0956..9bb862355 100644 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/all_formats.dict @@ -22,7 +22,7 @@ dbm="DSPE" digi="DIGI Booster module\x00" dmf="DDMF" -dmf="XTRACKER" +#dmf="XTRACKER" dmf="CMSG" dmf="SEQU" dmf="SMPI" @@ -87,9 +87,8 @@ it="AUTH" itp=".pti\x03\x01\x00\x00" -j2b="MUSE" -j2b="\xDE\xAD\xBE\xAF" -j2b="\xDE\xAD\xBA\xBE" +j2b="MUSE\xDE\xAD\xBE\xAF" +j2b="MUSE\xDE\xAD\xBA\xBE" j2b="AMFF" j2b="AM " j2b="MAIN" @@ -129,7 +128,7 @@ mod="OKTA" mod="CD81" #mod="FA08" mod="FLT8" -mod="EXO8" +#mod="EXO8" # Depending on the byte offset in the file, we generate either a "xCHN" or "xxCH" magic mod="99CHN" mod="TDZ8" @@ -194,7 +193,7 @@ psm="MAINSONG" psm="\x00\xFF\x00\x00\x01\x00" psm16="PSM\xFE" psm16="PORD" -psm16="PPAN" +#psm16="PPAN" psm16="PSAH" psm16="PPAT" @@ -202,7 +201,8 @@ ptm="PTMF" ptm="\x1A\x03\x02" s3m="SCRM" -s3m="SCRS" +#s3m="SCRS" +#s3m="SCRI" stm="\x1A\x02\x15" diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp index 73c5970fc..d08b2d3dc 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp @@ -37,11 +37,7 @@ int main( int argc, char * argv[] ) { throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" ); } const std::size_t buffersize = 480; -#if defined( LIBOPENMPT_QUIRK_NO_CSTDINT ) - const openmpt::std::int32_t samplerate = 48000; -#else const std::int32_t samplerate = 48000; -#endif std::vector left( buffersize ); std::vector right( buffersize ); const float * const buffers[2] = { left.data(), right.data() }; diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/LGPL.txt b/Frameworks/OpenMPT/OpenMPT/include/minimp3/LGPL.txt deleted file mode 100644 index 1e0991447..000000000 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/LGPL.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/LICENSE b/Frameworks/OpenMPT/OpenMPT/include/minimp3/LICENSE new file mode 100644 index 000000000..2c4afabdb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/LICENSE @@ -0,0 +1,117 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + + diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt index 04488f866..56fbd7922 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt @@ -1,11 +1,4 @@ -minimp3 version as of 2017-04-25 from -http://keyj.emphy.de/files/projects/minimp3.tar.gz . +minimp3 library from https://github.com/lieff/minimp3 +commit e9df0760e94044caded36a55d70ab4152134adc5 (2018-12-23) The following changes have been made: -- mp3_create() declaration has been fixed. -- GET_DATA() has been rewritten to avoid unaligned access warnings. -- Signed/unsigned comparison warnings have been fixed. -- Detection of stdint types has been fixed to work on *BSD. -- Modifications have been marked with // OpenMPT -- Obviously, unnecessary folders and files have been removed. -- For building, premake is used to generate Visual Studio project files. - See ../build/premake/ for details. + * minimp3.c has been added diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/libc.h b/Frameworks/OpenMPT/OpenMPT/include/minimp3/libc.h deleted file mode 100644 index 0426f0e73..000000000 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/libc.h +++ /dev/null @@ -1,184 +0,0 @@ -// a libc replacement (more or less) for the Microsoft Visual C compiler -// this file is public domain -- do with it whatever you want! -#ifndef __LIBC_H_INCLUDED__ -#define __LIBC_H_INCLUDED__ - -// check if minilibc is required -#ifndef NEED_MINILIBC - #ifndef NOLIBC - #define NEED_MINILIBC 0 - #else - #define NEED_MINILIBC 1 - #endif -#endif - -#ifdef _MSC_VER - #define INLINE __forceinline - #define FASTCALL __fastcall - #ifdef NOLIBC - #ifdef MAIN_PROGRAM - int _fltused=0; - #endif - #endif -#else - #define INLINE inline - #define FASTCALL __attribute__((fastcall)) - #include -#endif - -#ifdef _WIN32 - #ifndef WIN32 - #define WIN32 - #endif -#endif -#ifdef WIN32 - #include -#endif - -#if !NEED_MINILIBC - #include - #include - #include -#endif -#include - -#if !defined(__int8_t_defined) && !defined(_INT8_T_DECLARED) /* OpenMPT */ - #define __int8_t_defined - typedef unsigned char uint8_t; - typedef signed char int8_t; - typedef unsigned short uint16_t; - typedef signed short int16_t; - typedef unsigned int uint32_t; - typedef signed int int32_t; - #ifdef _MSC_VER - typedef unsigned __int64 uint64_t; - typedef signed __int64 int64_t; - #else - typedef unsigned long long uint64_t; - typedef signed long long int64_t; - #endif -#endif - -#ifndef NULL - #define NULL 0 -#endif - -#ifndef M_PI - #define M_PI 3.14159265358979 -#endif - -/////////////////////////////////////////////////////////////////////////////// - -#if NEED_MINILIBC - -static INLINE void libc_memset(void *dest, int value, int count) { - if (!count) return; - __asm { - cld - mov edi, dest - mov eax, value - mov ecx, count - rep stosb - } -} - -static INLINE void libc_memcpy(void *dest, const void *src, int count) { - if (!count) return; - __asm { - cld - mov esi, src - mov edi, dest - mov ecx, count - rep movsb - } -} - -#define libc_memmove libc_memcpy - -static INLINE void* libc_malloc(int size) { - return (void*) LocalAlloc(LMEM_FIXED, size); -} - -static INLINE void* libc_calloc(int size, int nmemb) { - return (void*) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, size * nmemb); -} - -static INLINE void* libc_realloc(void* old, int size) { - int oldsize = (int) LocalSize((HLOCAL) old); - void *mem; - if (size <= oldsize) return old; - mem = LocalAlloc(LMEM_FIXED, size); - libc_memcpy(mem, old, oldsize); - LocalFree((HLOCAL) old); - return mem; -} - -static INLINE void libc_free(void *mem) { - LocalFree((HLOCAL) mem); -} - -static INLINE double libc_frexp(double x, int *e) { - double res = -9999.999; - unsigned __int64 i = *(unsigned __int64*)(&x); - if (!(i & 0x7F00000000000000UL)) { - *e = 0; - return x; - } - *e = ((i << 1) >> 53) - 1022; - i &= 0x800FFFFFFFFFFFFFUL; - i |= 0x3FF0000000000000UL; - return *(double*)(&i) * 0.5; -} - -static INLINE double __declspec(naked) libc_exp(double x) { __asm { - fldl2e - fld qword ptr [esp+4] - fmul - fst st(1) - frndint - fxch - fsub st(0), st(1) - f2xm1 - fld1 - fadd - fscale - ret -} } - - -static INLINE double __declspec(naked) libc_pow(double b, double e) { __asm { - fld qword ptr [esp+12] - fld qword ptr [esp+4] - fyl2x -// following is a copy of libc_exp: - fst st(1) - frndint - fxch - fsub st(0), st(1) - f2xm1 - fld1 - fadd - fscale - ret -} } - - - -#else // NEED_MINILIBC == 0 - -#define libc_malloc malloc -#define libc_calloc calloc -#define libc_realloc realloc -#define libc_free free - -#define libc_memset memset -#define libc_memcpy memcpy -#define libc_memmove memmove - -#define libc_frexp frexp -#define libc_exp exp -#define libc_pow pow - -#endif // NEED_MINILIBC - -#endif//__LIBC_H_INCLUDED__ diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c index 4484cb73d..ffee84917 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.c @@ -1,2692 +1,4 @@ -/* - * MPEG Audio Layer III decoder - * Copyright (c) 2001, 2002 Fabrice Bellard, - * (c) 2007 Martin J. Fiedler - * - * This file is a stripped-down version of the MPEG Audio decoder from - * the FFmpeg libavcodec library. - * - * FFmpeg and minimp3 are free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg and minimp3 are distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "libc.h" +/* #define MINIMP3_NO_SIMD */ +#define MINIMP3_IMPLEMENTATION #include "minimp3.h" -#define MP3_FRAME_SIZE 1152 -#define MP3_MAX_CODED_FRAME_SIZE 1792 -#define MP3_MAX_CHANNELS 2 -#define SBLIMIT 32 - -#define MP3_STEREO 0 -#define MP3_JSTEREO 1 -#define MP3_DUAL 2 -#define MP3_MONO 3 - -#define SAME_HEADER_MASK \ - (0xffe00000 | (3 << 17) | (0xf << 12) | (3 << 10) | (3 << 19)) - -#define FRAC_BITS 15 -#define WFRAC_BITS 14 - -#define OUT_MAX (32767) -#define OUT_MIN (-32768) -#define OUT_SHIFT (WFRAC_BITS + FRAC_BITS - 15) - -#define MODE_EXT_MS_STEREO 2 -#define MODE_EXT_I_STEREO 1 - -#define FRAC_ONE (1 << FRAC_BITS) -#define FIX(a) ((int)((a) * FRAC_ONE)) -#define FIXR(a) ((int)((a) * FRAC_ONE + 0.5)) -#define FRAC_RND(a) (((a) + (FRAC_ONE/2)) >> FRAC_BITS) -#define FIXHR(a) ((int)((a) * (1LL<<32) + 0.5)) - -#if !(defined(_MSC_VER) && defined(_M_IX86)) // OpenMPT - #define MULL(a,b) (((int64_t)(a) * (int64_t)(b)) >> FRAC_BITS) - #define MULH(a,b) (((int64_t)(a) * (int64_t)(b)) >> 32) -#else - static INLINE int MULL(int a, int b) { - int res; - __asm { - mov eax, a - imul b - shr eax, 15 - shl edx, 17 - or eax, edx - mov res, eax - } - return res; - } - static INLINE int MULH(int a, int b) { - int res; - __asm { - mov eax, a - imul b - mov res, edx - } - return res; - } -#endif -#define MULS(ra, rb) ((ra) * (rb)) - -#define ISQRT2 FIXR(0.70710678118654752440) - -#define HEADER_SIZE 4 -#define BACKSTEP_SIZE 512 -#define EXTRABYTES 24 - -#define VLC_TYPE int16_t - -//////////////////////////////////////////////////////////////////////////////// - -struct _granule; - -typedef struct _bitstream { - const uint8_t *buffer, *buffer_end; - int index; - int size_in_bits; -} bitstream_t; - -typedef struct _vlc { - int bits; - VLC_TYPE (*table)[2]; ///< code, bits - int table_size, table_allocated; -} vlc_t; - -typedef struct _mp3_context { - uint8_t last_buf[2*BACKSTEP_SIZE + EXTRABYTES]; - int last_buf_size; - int frame_size; - uint32_t free_format_next_header; - int error_protection; - int sample_rate; - int sample_rate_index; - int bit_rate; - bitstream_t gb; - bitstream_t in_gb; - int nb_channels; - int mode; - int mode_ext; - int lsf; - int16_t synth_buf[MP3_MAX_CHANNELS][512 * 2]; - int synth_buf_offset[MP3_MAX_CHANNELS]; - int32_t sb_samples[MP3_MAX_CHANNELS][36][SBLIMIT]; - int32_t mdct_buf[MP3_MAX_CHANNELS][SBLIMIT * 18]; - int dither_state; -} mp3_context_t; - -typedef struct _granule { - uint8_t scfsi; - int part2_3_length; - int big_values; - int global_gain; - int scalefac_compress; - uint8_t block_type; - uint8_t switch_point; - int table_select[3]; - int subblock_gain[3]; - uint8_t scalefac_scale; - uint8_t count1table_select; - int region_size[3]; - int preflag; - int short_start, long_end; - uint8_t scale_factors[40]; - int32_t sb_hybrid[SBLIMIT * 18]; -} granule_t; - -typedef struct _huff_table { - int xsize; - const uint8_t *bits; - const uint16_t *codes; -} huff_table_t; - -static vlc_t huff_vlc[16]; -static vlc_t huff_quad_vlc[2]; -static uint16_t band_index_long[9][23]; -#define TABLE_4_3_SIZE (8191 + 16)*4 -static int8_t *table_4_3_exp; -static uint32_t *table_4_3_value; -static uint32_t exp_table[512]; -static uint32_t expval_table[512][16]; -static int32_t is_table[2][16]; -static int32_t is_table_lsf[2][2][16]; -static int32_t csa_table[8][4]; -static float csa_table_float[8][4]; -static int32_t mdct_win[8][36]; -static int16_t window[512]; - -//////////////////////////////////////////////////////////////////////////////// - -static const uint16_t mp3_bitrate_tab[2][15] = { - {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }, - {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160} -}; - -static const uint16_t mp3_freq_tab[3] = { 44100, 48000, 32000 }; - -static const int32_t mp3_enwindow[257] = { - 0, -1, -1, -1, -1, -1, -1, -2, - -2, -2, -2, -3, -3, -4, -4, -5, - -5, -6, -7, -7, -8, -9, -10, -11, - -13, -14, -16, -17, -19, -21, -24, -26, - -29, -31, -35, -38, -41, -45, -49, -53, - -58, -63, -68, -73, -79, -85, -91, -97, - -104, -111, -117, -125, -132, -139, -147, -154, - -161, -169, -176, -183, -190, -196, -202, -208, - 213, 218, 222, 225, 227, 228, 228, 227, - 224, 221, 215, 208, 200, 189, 177, 163, - 146, 127, 106, 83, 57, 29, -2, -36, - -72, -111, -153, -197, -244, -294, -347, -401, - -459, -519, -581, -645, -711, -779, -848, -919, - -991, -1064, -1137, -1210, -1283, -1356, -1428, -1498, - -1567, -1634, -1698, -1759, -1817, -1870, -1919, -1962, - -2001, -2032, -2057, -2075, -2085, -2087, -2080, -2063, - 2037, 2000, 1952, 1893, 1822, 1739, 1644, 1535, - 1414, 1280, 1131, 970, 794, 605, 402, 185, - -45, -288, -545, -814, -1095, -1388, -1692, -2006, - -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788, - -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, - -7910, -8209, -8491, -8755, -8998, -9219, -9416, -9585, - -9727, -9838, -9916, -9959, -9966, -9935, -9863, -9750, - -9592, -9389, -9139, -8840, -8492, -8092, -7640, -7134, - 6574, 5959, 5288, 4561, 3776, 2935, 2037, 1082, - 70, -998, -2122, -3300, -4533, -5818, -7154, -8540, - -9975,-11455,-12980,-14548,-16155,-17799,-19478,-21189, --22929,-24694,-26482,-28289,-30112,-31947,-33791,-35640, --37489,-39336,-41176,-43006,-44821,-46617,-48390,-50137, --51853,-53534,-55178,-56778,-58333,-59838,-61289,-62684, --64019,-65290,-66494,-67629,-68692,-69679,-70590,-71420, --72169,-72835,-73415,-73908,-74313,-74630,-74856,-74992, - 75038, -}; - -static const uint8_t slen_table[2][16] = { - { 0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 }, - { 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3 }, -}; - -static const uint8_t lsf_nsf_table[6][3][4] = { - { { 6, 5, 5, 5 }, { 9, 9, 9, 9 }, { 6, 9, 9, 9 } }, - { { 6, 5, 7, 3 }, { 9, 9, 12, 6 }, { 6, 9, 12, 6 } }, - { { 11, 10, 0, 0 }, { 18, 18, 0, 0 }, { 15, 18, 0, 0 } }, - { { 7, 7, 7, 0 }, { 12, 12, 12, 0 }, { 6, 15, 12, 0 } }, - { { 6, 6, 6, 3 }, { 12, 9, 9, 6 }, { 6, 12, 9, 6 } }, - { { 8, 8, 5, 0 }, { 15, 12, 9, 0 }, { 6, 18, 9, 0 } }, -}; - -static const uint16_t mp3_huffcodes_1[4] = { - 0x0001, 0x0001, 0x0001, 0x0000, -}; - -static const uint8_t mp3_huffbits_1[4] = { - 1, 3, 2, 3, -}; - -static const uint16_t mp3_huffcodes_2[9] = { - 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0003, 0x0002, - 0x0000, -}; - -static const uint8_t mp3_huffbits_2[9] = { - 1, 3, 6, 3, 3, 5, 5, 5, - 6, -}; - -static const uint16_t mp3_huffcodes_3[9] = { - 0x0003, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0002, - 0x0000, -}; - -static const uint8_t mp3_huffbits_3[9] = { - 2, 2, 6, 3, 2, 5, 5, 5, - 6, -}; - -static const uint16_t mp3_huffcodes_5[16] = { - 0x0001, 0x0002, 0x0006, 0x0005, 0x0003, 0x0001, 0x0004, 0x0004, - 0x0007, 0x0005, 0x0007, 0x0001, 0x0006, 0x0001, 0x0001, 0x0000, -}; - -static const uint8_t mp3_huffbits_5[16] = { - 1, 3, 6, 7, 3, 3, 6, 7, - 6, 6, 7, 8, 7, 6, 7, 8, -}; - -static const uint16_t mp3_huffcodes_6[16] = { - 0x0007, 0x0003, 0x0005, 0x0001, 0x0006, 0x0002, 0x0003, 0x0002, - 0x0005, 0x0004, 0x0004, 0x0001, 0x0003, 0x0003, 0x0002, 0x0000, -}; - -static const uint8_t mp3_huffbits_6[16] = { - 3, 3, 5, 7, 3, 2, 4, 5, - 4, 4, 5, 6, 6, 5, 6, 7, -}; - -static const uint16_t mp3_huffcodes_7[36] = { - 0x0001, 0x0002, 0x000a, 0x0013, 0x0010, 0x000a, 0x0003, 0x0003, - 0x0007, 0x000a, 0x0005, 0x0003, 0x000b, 0x0004, 0x000d, 0x0011, - 0x0008, 0x0004, 0x000c, 0x000b, 0x0012, 0x000f, 0x000b, 0x0002, - 0x0007, 0x0006, 0x0009, 0x000e, 0x0003, 0x0001, 0x0006, 0x0004, - 0x0005, 0x0003, 0x0002, 0x0000, -}; - -static const uint8_t mp3_huffbits_7[36] = { - 1, 3, 6, 8, 8, 9, 3, 4, - 6, 7, 7, 8, 6, 5, 7, 8, - 8, 9, 7, 7, 8, 9, 9, 9, - 7, 7, 8, 9, 9, 10, 8, 8, - 9, 10, 10, 10, -}; - -static const uint16_t mp3_huffcodes_8[36] = { - 0x0003, 0x0004, 0x0006, 0x0012, 0x000c, 0x0005, 0x0005, 0x0001, - 0x0002, 0x0010, 0x0009, 0x0003, 0x0007, 0x0003, 0x0005, 0x000e, - 0x0007, 0x0003, 0x0013, 0x0011, 0x000f, 0x000d, 0x000a, 0x0004, - 0x000d, 0x0005, 0x0008, 0x000b, 0x0005, 0x0001, 0x000c, 0x0004, - 0x0004, 0x0001, 0x0001, 0x0000, -}; - -static const uint8_t mp3_huffbits_8[36] = { - 2, 3, 6, 8, 8, 9, 3, 2, - 4, 8, 8, 8, 6, 4, 6, 8, - 8, 9, 8, 8, 8, 9, 9, 10, - 8, 7, 8, 9, 10, 10, 9, 8, - 9, 9, 11, 11, -}; - -static const uint16_t mp3_huffcodes_9[36] = { - 0x0007, 0x0005, 0x0009, 0x000e, 0x000f, 0x0007, 0x0006, 0x0004, - 0x0005, 0x0005, 0x0006, 0x0007, 0x0007, 0x0006, 0x0008, 0x0008, - 0x0008, 0x0005, 0x000f, 0x0006, 0x0009, 0x000a, 0x0005, 0x0001, - 0x000b, 0x0007, 0x0009, 0x0006, 0x0004, 0x0001, 0x000e, 0x0004, - 0x0006, 0x0002, 0x0006, 0x0000, -}; - -static const uint8_t mp3_huffbits_9[36] = { - 3, 3, 5, 6, 8, 9, 3, 3, - 4, 5, 6, 8, 4, 4, 5, 6, - 7, 8, 6, 5, 6, 7, 7, 8, - 7, 6, 7, 7, 8, 9, 8, 7, - 8, 8, 9, 9, -}; - -static const uint16_t mp3_huffcodes_10[64] = { - 0x0001, 0x0002, 0x000a, 0x0017, 0x0023, 0x001e, 0x000c, 0x0011, - 0x0003, 0x0003, 0x0008, 0x000c, 0x0012, 0x0015, 0x000c, 0x0007, - 0x000b, 0x0009, 0x000f, 0x0015, 0x0020, 0x0028, 0x0013, 0x0006, - 0x000e, 0x000d, 0x0016, 0x0022, 0x002e, 0x0017, 0x0012, 0x0007, - 0x0014, 0x0013, 0x0021, 0x002f, 0x001b, 0x0016, 0x0009, 0x0003, - 0x001f, 0x0016, 0x0029, 0x001a, 0x0015, 0x0014, 0x0005, 0x0003, - 0x000e, 0x000d, 0x000a, 0x000b, 0x0010, 0x0006, 0x0005, 0x0001, - 0x0009, 0x0008, 0x0007, 0x0008, 0x0004, 0x0004, 0x0002, 0x0000, -}; - -static const uint8_t mp3_huffbits_10[64] = { - 1, 3, 6, 8, 9, 9, 9, 10, - 3, 4, 6, 7, 8, 9, 8, 8, - 6, 6, 7, 8, 9, 10, 9, 9, - 7, 7, 8, 9, 10, 10, 9, 10, - 8, 8, 9, 10, 10, 10, 10, 10, - 9, 9, 10, 10, 11, 11, 10, 11, - 8, 8, 9, 10, 10, 10, 11, 11, - 9, 8, 9, 10, 10, 11, 11, 11, -}; - -static const uint16_t mp3_huffcodes_11[64] = { - 0x0003, 0x0004, 0x000a, 0x0018, 0x0022, 0x0021, 0x0015, 0x000f, - 0x0005, 0x0003, 0x0004, 0x000a, 0x0020, 0x0011, 0x000b, 0x000a, - 0x000b, 0x0007, 0x000d, 0x0012, 0x001e, 0x001f, 0x0014, 0x0005, - 0x0019, 0x000b, 0x0013, 0x003b, 0x001b, 0x0012, 0x000c, 0x0005, - 0x0023, 0x0021, 0x001f, 0x003a, 0x001e, 0x0010, 0x0007, 0x0005, - 0x001c, 0x001a, 0x0020, 0x0013, 0x0011, 0x000f, 0x0008, 0x000e, - 0x000e, 0x000c, 0x0009, 0x000d, 0x000e, 0x0009, 0x0004, 0x0001, - 0x000b, 0x0004, 0x0006, 0x0006, 0x0006, 0x0003, 0x0002, 0x0000, -}; - -static const uint8_t mp3_huffbits_11[64] = { - 2, 3, 5, 7, 8, 9, 8, 9, - 3, 3, 4, 6, 8, 8, 7, 8, - 5, 5, 6, 7, 8, 9, 8, 8, - 7, 6, 7, 9, 8, 10, 8, 9, - 8, 8, 8, 9, 9, 10, 9, 10, - 8, 8, 9, 10, 10, 11, 10, 11, - 8, 7, 7, 8, 9, 10, 10, 10, - 8, 7, 8, 9, 10, 10, 10, 10, -}; - -static const uint16_t mp3_huffcodes_12[64] = { - 0x0009, 0x0006, 0x0010, 0x0021, 0x0029, 0x0027, 0x0026, 0x001a, - 0x0007, 0x0005, 0x0006, 0x0009, 0x0017, 0x0010, 0x001a, 0x000b, - 0x0011, 0x0007, 0x000b, 0x000e, 0x0015, 0x001e, 0x000a, 0x0007, - 0x0011, 0x000a, 0x000f, 0x000c, 0x0012, 0x001c, 0x000e, 0x0005, - 0x0020, 0x000d, 0x0016, 0x0013, 0x0012, 0x0010, 0x0009, 0x0005, - 0x0028, 0x0011, 0x001f, 0x001d, 0x0011, 0x000d, 0x0004, 0x0002, - 0x001b, 0x000c, 0x000b, 0x000f, 0x000a, 0x0007, 0x0004, 0x0001, - 0x001b, 0x000c, 0x0008, 0x000c, 0x0006, 0x0003, 0x0001, 0x0000, -}; - -static const uint8_t mp3_huffbits_12[64] = { - 4, 3, 5, 7, 8, 9, 9, 9, - 3, 3, 4, 5, 7, 7, 8, 8, - 5, 4, 5, 6, 7, 8, 7, 8, - 6, 5, 6, 6, 7, 8, 8, 8, - 7, 6, 7, 7, 8, 8, 8, 9, - 8, 7, 8, 8, 8, 9, 8, 9, - 8, 7, 7, 8, 8, 9, 9, 10, - 9, 8, 8, 9, 9, 9, 9, 10, -}; - -static const uint16_t mp3_huffcodes_13[256] = { - 0x0001, 0x0005, 0x000e, 0x0015, 0x0022, 0x0033, 0x002e, 0x0047, - 0x002a, 0x0034, 0x0044, 0x0034, 0x0043, 0x002c, 0x002b, 0x0013, - 0x0003, 0x0004, 0x000c, 0x0013, 0x001f, 0x001a, 0x002c, 0x0021, - 0x001f, 0x0018, 0x0020, 0x0018, 0x001f, 0x0023, 0x0016, 0x000e, - 0x000f, 0x000d, 0x0017, 0x0024, 0x003b, 0x0031, 0x004d, 0x0041, - 0x001d, 0x0028, 0x001e, 0x0028, 0x001b, 0x0021, 0x002a, 0x0010, - 0x0016, 0x0014, 0x0025, 0x003d, 0x0038, 0x004f, 0x0049, 0x0040, - 0x002b, 0x004c, 0x0038, 0x0025, 0x001a, 0x001f, 0x0019, 0x000e, - 0x0023, 0x0010, 0x003c, 0x0039, 0x0061, 0x004b, 0x0072, 0x005b, - 0x0036, 0x0049, 0x0037, 0x0029, 0x0030, 0x0035, 0x0017, 0x0018, - 0x003a, 0x001b, 0x0032, 0x0060, 0x004c, 0x0046, 0x005d, 0x0054, - 0x004d, 0x003a, 0x004f, 0x001d, 0x004a, 0x0031, 0x0029, 0x0011, - 0x002f, 0x002d, 0x004e, 0x004a, 0x0073, 0x005e, 0x005a, 0x004f, - 0x0045, 0x0053, 0x0047, 0x0032, 0x003b, 0x0026, 0x0024, 0x000f, - 0x0048, 0x0022, 0x0038, 0x005f, 0x005c, 0x0055, 0x005b, 0x005a, - 0x0056, 0x0049, 0x004d, 0x0041, 0x0033, 0x002c, 0x002b, 0x002a, - 0x002b, 0x0014, 0x001e, 0x002c, 0x0037, 0x004e, 0x0048, 0x0057, - 0x004e, 0x003d, 0x002e, 0x0036, 0x0025, 0x001e, 0x0014, 0x0010, - 0x0035, 0x0019, 0x0029, 0x0025, 0x002c, 0x003b, 0x0036, 0x0051, - 0x0042, 0x004c, 0x0039, 0x0036, 0x0025, 0x0012, 0x0027, 0x000b, - 0x0023, 0x0021, 0x001f, 0x0039, 0x002a, 0x0052, 0x0048, 0x0050, - 0x002f, 0x003a, 0x0037, 0x0015, 0x0016, 0x001a, 0x0026, 0x0016, - 0x0035, 0x0019, 0x0017, 0x0026, 0x0046, 0x003c, 0x0033, 0x0024, - 0x0037, 0x001a, 0x0022, 0x0017, 0x001b, 0x000e, 0x0009, 0x0007, - 0x0022, 0x0020, 0x001c, 0x0027, 0x0031, 0x004b, 0x001e, 0x0034, - 0x0030, 0x0028, 0x0034, 0x001c, 0x0012, 0x0011, 0x0009, 0x0005, - 0x002d, 0x0015, 0x0022, 0x0040, 0x0038, 0x0032, 0x0031, 0x002d, - 0x001f, 0x0013, 0x000c, 0x000f, 0x000a, 0x0007, 0x0006, 0x0003, - 0x0030, 0x0017, 0x0014, 0x0027, 0x0024, 0x0023, 0x0035, 0x0015, - 0x0010, 0x0017, 0x000d, 0x000a, 0x0006, 0x0001, 0x0004, 0x0002, - 0x0010, 0x000f, 0x0011, 0x001b, 0x0019, 0x0014, 0x001d, 0x000b, - 0x0011, 0x000c, 0x0010, 0x0008, 0x0001, 0x0001, 0x0000, 0x0001, -}; - -static const uint8_t mp3_huffbits_13[256] = { - 1, 4, 6, 7, 8, 9, 9, 10, - 9, 10, 11, 11, 12, 12, 13, 13, - 3, 4, 6, 7, 8, 8, 9, 9, - 9, 9, 10, 10, 11, 12, 12, 12, - 6, 6, 7, 8, 9, 9, 10, 10, - 9, 10, 10, 11, 11, 12, 13, 13, - 7, 7, 8, 9, 9, 10, 10, 10, - 10, 11, 11, 11, 11, 12, 13, 13, - 8, 7, 9, 9, 10, 10, 11, 11, - 10, 11, 11, 12, 12, 13, 13, 14, - 9, 8, 9, 10, 10, 10, 11, 11, - 11, 11, 12, 11, 13, 13, 14, 14, - 9, 9, 10, 10, 11, 11, 11, 11, - 11, 12, 12, 12, 13, 13, 14, 14, - 10, 9, 10, 11, 11, 11, 12, 12, - 12, 12, 13, 13, 13, 14, 16, 16, - 9, 8, 9, 10, 10, 11, 11, 12, - 12, 12, 12, 13, 13, 14, 15, 15, - 10, 9, 10, 10, 11, 11, 11, 13, - 12, 13, 13, 14, 14, 14, 16, 15, - 10, 10, 10, 11, 11, 12, 12, 13, - 12, 13, 14, 13, 14, 15, 16, 17, - 11, 10, 10, 11, 12, 12, 12, 12, - 13, 13, 13, 14, 15, 15, 15, 16, - 11, 11, 11, 12, 12, 13, 12, 13, - 14, 14, 15, 15, 15, 16, 16, 16, - 12, 11, 12, 13, 13, 13, 14, 14, - 14, 14, 14, 15, 16, 15, 16, 16, - 13, 12, 12, 13, 13, 13, 15, 14, - 14, 17, 15, 15, 15, 17, 16, 16, - 12, 12, 13, 14, 14, 14, 15, 14, - 15, 15, 16, 16, 19, 18, 19, 16, -}; - -static const uint16_t mp3_huffcodes_15[256] = { - 0x0007, 0x000c, 0x0012, 0x0035, 0x002f, 0x004c, 0x007c, 0x006c, - 0x0059, 0x007b, 0x006c, 0x0077, 0x006b, 0x0051, 0x007a, 0x003f, - 0x000d, 0x0005, 0x0010, 0x001b, 0x002e, 0x0024, 0x003d, 0x0033, - 0x002a, 0x0046, 0x0034, 0x0053, 0x0041, 0x0029, 0x003b, 0x0024, - 0x0013, 0x0011, 0x000f, 0x0018, 0x0029, 0x0022, 0x003b, 0x0030, - 0x0028, 0x0040, 0x0032, 0x004e, 0x003e, 0x0050, 0x0038, 0x0021, - 0x001d, 0x001c, 0x0019, 0x002b, 0x0027, 0x003f, 0x0037, 0x005d, - 0x004c, 0x003b, 0x005d, 0x0048, 0x0036, 0x004b, 0x0032, 0x001d, - 0x0034, 0x0016, 0x002a, 0x0028, 0x0043, 0x0039, 0x005f, 0x004f, - 0x0048, 0x0039, 0x0059, 0x0045, 0x0031, 0x0042, 0x002e, 0x001b, - 0x004d, 0x0025, 0x0023, 0x0042, 0x003a, 0x0034, 0x005b, 0x004a, - 0x003e, 0x0030, 0x004f, 0x003f, 0x005a, 0x003e, 0x0028, 0x0026, - 0x007d, 0x0020, 0x003c, 0x0038, 0x0032, 0x005c, 0x004e, 0x0041, - 0x0037, 0x0057, 0x0047, 0x0033, 0x0049, 0x0033, 0x0046, 0x001e, - 0x006d, 0x0035, 0x0031, 0x005e, 0x0058, 0x004b, 0x0042, 0x007a, - 0x005b, 0x0049, 0x0038, 0x002a, 0x0040, 0x002c, 0x0015, 0x0019, - 0x005a, 0x002b, 0x0029, 0x004d, 0x0049, 0x003f, 0x0038, 0x005c, - 0x004d, 0x0042, 0x002f, 0x0043, 0x0030, 0x0035, 0x0024, 0x0014, - 0x0047, 0x0022, 0x0043, 0x003c, 0x003a, 0x0031, 0x0058, 0x004c, - 0x0043, 0x006a, 0x0047, 0x0036, 0x0026, 0x0027, 0x0017, 0x000f, - 0x006d, 0x0035, 0x0033, 0x002f, 0x005a, 0x0052, 0x003a, 0x0039, - 0x0030, 0x0048, 0x0039, 0x0029, 0x0017, 0x001b, 0x003e, 0x0009, - 0x0056, 0x002a, 0x0028, 0x0025, 0x0046, 0x0040, 0x0034, 0x002b, - 0x0046, 0x0037, 0x002a, 0x0019, 0x001d, 0x0012, 0x000b, 0x000b, - 0x0076, 0x0044, 0x001e, 0x0037, 0x0032, 0x002e, 0x004a, 0x0041, - 0x0031, 0x0027, 0x0018, 0x0010, 0x0016, 0x000d, 0x000e, 0x0007, - 0x005b, 0x002c, 0x0027, 0x0026, 0x0022, 0x003f, 0x0034, 0x002d, - 0x001f, 0x0034, 0x001c, 0x0013, 0x000e, 0x0008, 0x0009, 0x0003, - 0x007b, 0x003c, 0x003a, 0x0035, 0x002f, 0x002b, 0x0020, 0x0016, - 0x0025, 0x0018, 0x0011, 0x000c, 0x000f, 0x000a, 0x0002, 0x0001, - 0x0047, 0x0025, 0x0022, 0x001e, 0x001c, 0x0014, 0x0011, 0x001a, - 0x0015, 0x0010, 0x000a, 0x0006, 0x0008, 0x0006, 0x0002, 0x0000, -}; - -static const uint8_t mp3_huffbits_15[256] = { - 3, 4, 5, 7, 7, 8, 9, 9, - 9, 10, 10, 11, 11, 11, 12, 13, - 4, 3, 5, 6, 7, 7, 8, 8, - 8, 9, 9, 10, 10, 10, 11, 11, - 5, 5, 5, 6, 7, 7, 8, 8, - 8, 9, 9, 10, 10, 11, 11, 11, - 6, 6, 6, 7, 7, 8, 8, 9, - 9, 9, 10, 10, 10, 11, 11, 11, - 7, 6, 7, 7, 8, 8, 9, 9, - 9, 9, 10, 10, 10, 11, 11, 11, - 8, 7, 7, 8, 8, 8, 9, 9, - 9, 9, 10, 10, 11, 11, 11, 12, - 9, 7, 8, 8, 8, 9, 9, 9, - 9, 10, 10, 10, 11, 11, 12, 12, - 9, 8, 8, 9, 9, 9, 9, 10, - 10, 10, 10, 10, 11, 11, 11, 12, - 9, 8, 8, 9, 9, 9, 9, 10, - 10, 10, 10, 11, 11, 12, 12, 12, - 9, 8, 9, 9, 9, 9, 10, 10, - 10, 11, 11, 11, 11, 12, 12, 12, - 10, 9, 9, 9, 10, 10, 10, 10, - 10, 11, 11, 11, 11, 12, 13, 12, - 10, 9, 9, 9, 10, 10, 10, 10, - 11, 11, 11, 11, 12, 12, 12, 13, - 11, 10, 9, 10, 10, 10, 11, 11, - 11, 11, 11, 11, 12, 12, 13, 13, - 11, 10, 10, 10, 10, 11, 11, 11, - 11, 12, 12, 12, 12, 12, 13, 13, - 12, 11, 11, 11, 11, 11, 11, 11, - 12, 12, 12, 12, 13, 13, 12, 13, - 12, 11, 11, 11, 11, 11, 11, 12, - 12, 12, 12, 12, 13, 13, 13, 13, -}; - -static const uint16_t mp3_huffcodes_16[256] = { - 0x0001, 0x0005, 0x000e, 0x002c, 0x004a, 0x003f, 0x006e, 0x005d, - 0x00ac, 0x0095, 0x008a, 0x00f2, 0x00e1, 0x00c3, 0x0178, 0x0011, - 0x0003, 0x0004, 0x000c, 0x0014, 0x0023, 0x003e, 0x0035, 0x002f, - 0x0053, 0x004b, 0x0044, 0x0077, 0x00c9, 0x006b, 0x00cf, 0x0009, - 0x000f, 0x000d, 0x0017, 0x0026, 0x0043, 0x003a, 0x0067, 0x005a, - 0x00a1, 0x0048, 0x007f, 0x0075, 0x006e, 0x00d1, 0x00ce, 0x0010, - 0x002d, 0x0015, 0x0027, 0x0045, 0x0040, 0x0072, 0x0063, 0x0057, - 0x009e, 0x008c, 0x00fc, 0x00d4, 0x00c7, 0x0183, 0x016d, 0x001a, - 0x004b, 0x0024, 0x0044, 0x0041, 0x0073, 0x0065, 0x00b3, 0x00a4, - 0x009b, 0x0108, 0x00f6, 0x00e2, 0x018b, 0x017e, 0x016a, 0x0009, - 0x0042, 0x001e, 0x003b, 0x0038, 0x0066, 0x00b9, 0x00ad, 0x0109, - 0x008e, 0x00fd, 0x00e8, 0x0190, 0x0184, 0x017a, 0x01bd, 0x0010, - 0x006f, 0x0036, 0x0034, 0x0064, 0x00b8, 0x00b2, 0x00a0, 0x0085, - 0x0101, 0x00f4, 0x00e4, 0x00d9, 0x0181, 0x016e, 0x02cb, 0x000a, - 0x0062, 0x0030, 0x005b, 0x0058, 0x00a5, 0x009d, 0x0094, 0x0105, - 0x00f8, 0x0197, 0x018d, 0x0174, 0x017c, 0x0379, 0x0374, 0x0008, - 0x0055, 0x0054, 0x0051, 0x009f, 0x009c, 0x008f, 0x0104, 0x00f9, - 0x01ab, 0x0191, 0x0188, 0x017f, 0x02d7, 0x02c9, 0x02c4, 0x0007, - 0x009a, 0x004c, 0x0049, 0x008d, 0x0083, 0x0100, 0x00f5, 0x01aa, - 0x0196, 0x018a, 0x0180, 0x02df, 0x0167, 0x02c6, 0x0160, 0x000b, - 0x008b, 0x0081, 0x0043, 0x007d, 0x00f7, 0x00e9, 0x00e5, 0x00db, - 0x0189, 0x02e7, 0x02e1, 0x02d0, 0x0375, 0x0372, 0x01b7, 0x0004, - 0x00f3, 0x0078, 0x0076, 0x0073, 0x00e3, 0x00df, 0x018c, 0x02ea, - 0x02e6, 0x02e0, 0x02d1, 0x02c8, 0x02c2, 0x00df, 0x01b4, 0x0006, - 0x00ca, 0x00e0, 0x00de, 0x00da, 0x00d8, 0x0185, 0x0182, 0x017d, - 0x016c, 0x0378, 0x01bb, 0x02c3, 0x01b8, 0x01b5, 0x06c0, 0x0004, - 0x02eb, 0x00d3, 0x00d2, 0x00d0, 0x0172, 0x017b, 0x02de, 0x02d3, - 0x02ca, 0x06c7, 0x0373, 0x036d, 0x036c, 0x0d83, 0x0361, 0x0002, - 0x0179, 0x0171, 0x0066, 0x00bb, 0x02d6, 0x02d2, 0x0166, 0x02c7, - 0x02c5, 0x0362, 0x06c6, 0x0367, 0x0d82, 0x0366, 0x01b2, 0x0000, - 0x000c, 0x000a, 0x0007, 0x000b, 0x000a, 0x0011, 0x000b, 0x0009, - 0x000d, 0x000c, 0x000a, 0x0007, 0x0005, 0x0003, 0x0001, 0x0003, -}; - -static const uint8_t mp3_huffbits_16[256] = { - 1, 4, 6, 8, 9, 9, 10, 10, - 11, 11, 11, 12, 12, 12, 13, 9, - 3, 4, 6, 7, 8, 9, 9, 9, - 10, 10, 10, 11, 12, 11, 12, 8, - 6, 6, 7, 8, 9, 9, 10, 10, - 11, 10, 11, 11, 11, 12, 12, 9, - 8, 7, 8, 9, 9, 10, 10, 10, - 11, 11, 12, 12, 12, 13, 13, 10, - 9, 8, 9, 9, 10, 10, 11, 11, - 11, 12, 12, 12, 13, 13, 13, 9, - 9, 8, 9, 9, 10, 11, 11, 12, - 11, 12, 12, 13, 13, 13, 14, 10, - 10, 9, 9, 10, 11, 11, 11, 11, - 12, 12, 12, 12, 13, 13, 14, 10, - 10, 9, 10, 10, 11, 11, 11, 12, - 12, 13, 13, 13, 13, 15, 15, 10, - 10, 10, 10, 11, 11, 11, 12, 12, - 13, 13, 13, 13, 14, 14, 14, 10, - 11, 10, 10, 11, 11, 12, 12, 13, - 13, 13, 13, 14, 13, 14, 13, 11, - 11, 11, 10, 11, 12, 12, 12, 12, - 13, 14, 14, 14, 15, 15, 14, 10, - 12, 11, 11, 11, 12, 12, 13, 14, - 14, 14, 14, 14, 14, 13, 14, 11, - 12, 12, 12, 12, 12, 13, 13, 13, - 13, 15, 14, 14, 14, 14, 16, 11, - 14, 12, 12, 12, 13, 13, 14, 14, - 14, 16, 15, 15, 15, 17, 15, 11, - 13, 13, 11, 12, 14, 14, 13, 14, - 14, 15, 16, 15, 17, 15, 14, 11, - 9, 8, 8, 9, 9, 10, 10, 10, - 11, 11, 11, 11, 11, 11, 11, 8, -}; - -static const uint16_t mp3_huffcodes_24[256] = { - 0x000f, 0x000d, 0x002e, 0x0050, 0x0092, 0x0106, 0x00f8, 0x01b2, - 0x01aa, 0x029d, 0x028d, 0x0289, 0x026d, 0x0205, 0x0408, 0x0058, - 0x000e, 0x000c, 0x0015, 0x0026, 0x0047, 0x0082, 0x007a, 0x00d8, - 0x00d1, 0x00c6, 0x0147, 0x0159, 0x013f, 0x0129, 0x0117, 0x002a, - 0x002f, 0x0016, 0x0029, 0x004a, 0x0044, 0x0080, 0x0078, 0x00dd, - 0x00cf, 0x00c2, 0x00b6, 0x0154, 0x013b, 0x0127, 0x021d, 0x0012, - 0x0051, 0x0027, 0x004b, 0x0046, 0x0086, 0x007d, 0x0074, 0x00dc, - 0x00cc, 0x00be, 0x00b2, 0x0145, 0x0137, 0x0125, 0x010f, 0x0010, - 0x0093, 0x0048, 0x0045, 0x0087, 0x007f, 0x0076, 0x0070, 0x00d2, - 0x00c8, 0x00bc, 0x0160, 0x0143, 0x0132, 0x011d, 0x021c, 0x000e, - 0x0107, 0x0042, 0x0081, 0x007e, 0x0077, 0x0072, 0x00d6, 0x00ca, - 0x00c0, 0x00b4, 0x0155, 0x013d, 0x012d, 0x0119, 0x0106, 0x000c, - 0x00f9, 0x007b, 0x0079, 0x0075, 0x0071, 0x00d7, 0x00ce, 0x00c3, - 0x00b9, 0x015b, 0x014a, 0x0134, 0x0123, 0x0110, 0x0208, 0x000a, - 0x01b3, 0x0073, 0x006f, 0x006d, 0x00d3, 0x00cb, 0x00c4, 0x00bb, - 0x0161, 0x014c, 0x0139, 0x012a, 0x011b, 0x0213, 0x017d, 0x0011, - 0x01ab, 0x00d4, 0x00d0, 0x00cd, 0x00c9, 0x00c1, 0x00ba, 0x00b1, - 0x00a9, 0x0140, 0x012f, 0x011e, 0x010c, 0x0202, 0x0179, 0x0010, - 0x014f, 0x00c7, 0x00c5, 0x00bf, 0x00bd, 0x00b5, 0x00ae, 0x014d, - 0x0141, 0x0131, 0x0121, 0x0113, 0x0209, 0x017b, 0x0173, 0x000b, - 0x029c, 0x00b8, 0x00b7, 0x00b3, 0x00af, 0x0158, 0x014b, 0x013a, - 0x0130, 0x0122, 0x0115, 0x0212, 0x017f, 0x0175, 0x016e, 0x000a, - 0x028c, 0x015a, 0x00ab, 0x00a8, 0x00a4, 0x013e, 0x0135, 0x012b, - 0x011f, 0x0114, 0x0107, 0x0201, 0x0177, 0x0170, 0x016a, 0x0006, - 0x0288, 0x0142, 0x013c, 0x0138, 0x0133, 0x012e, 0x0124, 0x011c, - 0x010d, 0x0105, 0x0200, 0x0178, 0x0172, 0x016c, 0x0167, 0x0004, - 0x026c, 0x012c, 0x0128, 0x0126, 0x0120, 0x011a, 0x0111, 0x010a, - 0x0203, 0x017c, 0x0176, 0x0171, 0x016d, 0x0169, 0x0165, 0x0002, - 0x0409, 0x0118, 0x0116, 0x0112, 0x010b, 0x0108, 0x0103, 0x017e, - 0x017a, 0x0174, 0x016f, 0x016b, 0x0168, 0x0166, 0x0164, 0x0000, - 0x002b, 0x0014, 0x0013, 0x0011, 0x000f, 0x000d, 0x000b, 0x0009, - 0x0007, 0x0006, 0x0004, 0x0007, 0x0005, 0x0003, 0x0001, 0x0003, -}; - -static const uint8_t mp3_huffbits_24[256] = { - 4, 4, 6, 7, 8, 9, 9, 10, - 10, 11, 11, 11, 11, 11, 12, 9, - 4, 4, 5, 6, 7, 8, 8, 9, - 9, 9, 10, 10, 10, 10, 10, 8, - 6, 5, 6, 7, 7, 8, 8, 9, - 9, 9, 9, 10, 10, 10, 11, 7, - 7, 6, 7, 7, 8, 8, 8, 9, - 9, 9, 9, 10, 10, 10, 10, 7, - 8, 7, 7, 8, 8, 8, 8, 9, - 9, 9, 10, 10, 10, 10, 11, 7, - 9, 7, 8, 8, 8, 8, 9, 9, - 9, 9, 10, 10, 10, 10, 10, 7, - 9, 8, 8, 8, 8, 9, 9, 9, - 9, 10, 10, 10, 10, 10, 11, 7, - 10, 8, 8, 8, 9, 9, 9, 9, - 10, 10, 10, 10, 10, 11, 11, 8, - 10, 9, 9, 9, 9, 9, 9, 9, - 9, 10, 10, 10, 10, 11, 11, 8, - 10, 9, 9, 9, 9, 9, 9, 10, - 10, 10, 10, 10, 11, 11, 11, 8, - 11, 9, 9, 9, 9, 10, 10, 10, - 10, 10, 10, 11, 11, 11, 11, 8, - 11, 10, 9, 9, 9, 10, 10, 10, - 10, 10, 10, 11, 11, 11, 11, 8, - 11, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 11, 11, 11, 11, 11, 8, - 11, 10, 10, 10, 10, 10, 10, 10, - 11, 11, 11, 11, 11, 11, 11, 8, - 12, 10, 10, 10, 10, 10, 10, 11, - 11, 11, 11, 11, 11, 11, 11, 8, - 8, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 8, 8, 8, 8, 4, -}; - -static const huff_table_t mp3_huff_tables[16] = { -{ 1, NULL, NULL }, -{ 2, mp3_huffbits_1, mp3_huffcodes_1 }, -{ 3, mp3_huffbits_2, mp3_huffcodes_2 }, -{ 3, mp3_huffbits_3, mp3_huffcodes_3 }, -{ 4, mp3_huffbits_5, mp3_huffcodes_5 }, -{ 4, mp3_huffbits_6, mp3_huffcodes_6 }, -{ 6, mp3_huffbits_7, mp3_huffcodes_7 }, -{ 6, mp3_huffbits_8, mp3_huffcodes_8 }, -{ 6, mp3_huffbits_9, mp3_huffcodes_9 }, -{ 8, mp3_huffbits_10, mp3_huffcodes_10 }, -{ 8, mp3_huffbits_11, mp3_huffcodes_11 }, -{ 8, mp3_huffbits_12, mp3_huffcodes_12 }, -{ 16, mp3_huffbits_13, mp3_huffcodes_13 }, -{ 16, mp3_huffbits_15, mp3_huffcodes_15 }, -{ 16, mp3_huffbits_16, mp3_huffcodes_16 }, -{ 16, mp3_huffbits_24, mp3_huffcodes_24 }, -}; - -static const uint8_t mp3_huff_data[32][2] = { -{ 0, 0 }, -{ 1, 0 }, -{ 2, 0 }, -{ 3, 0 }, -{ 0, 0 }, -{ 4, 0 }, -{ 5, 0 }, -{ 6, 0 }, -{ 7, 0 }, -{ 8, 0 }, -{ 9, 0 }, -{ 10, 0 }, -{ 11, 0 }, -{ 12, 0 }, -{ 0, 0 }, -{ 13, 0 }, -{ 14, 1 }, -{ 14, 2 }, -{ 14, 3 }, -{ 14, 4 }, -{ 14, 6 }, -{ 14, 8 }, -{ 14, 10 }, -{ 14, 13 }, -{ 15, 4 }, -{ 15, 5 }, -{ 15, 6 }, -{ 15, 7 }, -{ 15, 8 }, -{ 15, 9 }, -{ 15, 11 }, -{ 15, 13 }, -}; - -static const uint8_t mp3_quad_codes[2][16] = { - { 1, 5, 4, 5, 6, 5, 4, 4, 7, 3, 6, 0, 7, 2, 3, 1, }, - { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, }, -}; - -static const uint8_t mp3_quad_bits[2][16] = { - { 1, 4, 4, 5, 4, 6, 5, 6, 4, 5, 5, 6, 5, 6, 6, 6, }, - { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, }, -}; - -static const uint8_t band_size_long[9][22] = { -{ 4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, - 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158, }, /* 44100 */ -{ 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 10, - 12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192, }, /* 48000 */ -{ 4, 4, 4, 4, 4, 4, 6, 6, 8, 10, 12, - 16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26, }, /* 32000 */ -{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, - 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 22050 */ -{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, - 18, 22, 26, 32, 38, 46, 52, 64, 70, 76, 36, }, /* 24000 */ -{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, - 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 16000 */ -{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, - 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 11025 */ -{ 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, - 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, }, /* 12000 */ -{ 12, 12, 12, 12, 12, 12, 16, 20, 24, 28, 32, - 40, 48, 56, 64, 76, 90, 2, 2, 2, 2, 2, }, /* 8000 */ -}; - -static const uint8_t band_size_short[9][13] = { -{ 4, 4, 4, 4, 6, 8, 10, 12, 14, 18, 22, 30, 56, }, /* 44100 */ -{ 4, 4, 4, 4, 6, 6, 10, 12, 14, 16, 20, 26, 66, }, /* 48000 */ -{ 4, 4, 4, 4, 6, 8, 12, 16, 20, 26, 34, 42, 12, }, /* 32000 */ -{ 4, 4, 4, 6, 6, 8, 10, 14, 18, 26, 32, 42, 18, }, /* 22050 */ -{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 32, 44, 12, }, /* 24000 */ -{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18, }, /* 16000 */ -{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18, }, /* 11025 */ -{ 4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18, }, /* 12000 */ -{ 8, 8, 8, 12, 16, 20, 24, 28, 36, 2, 2, 2, 26, }, /* 8000 */ -}; - -static const uint8_t mp3_pretab[2][22] = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0 }, -}; - -static const float ci_table[8] = { - -0.6f, -0.535f, -0.33f, -0.185f, -0.095f, -0.041f, -0.0142f, -0.0037f, -}; - -#define C1 FIXHR(0.98480775301220805936/2) -#define C2 FIXHR(0.93969262078590838405/2) -#define C3 FIXHR(0.86602540378443864676/2) -#define C4 FIXHR(0.76604444311897803520/2) -#define C5 FIXHR(0.64278760968653932632/2) -#define C6 FIXHR(0.5/2) -#define C7 FIXHR(0.34202014332566873304/2) -#define C8 FIXHR(0.17364817766693034885/2) - -static const int icos36[9] = { - FIXR(0.50190991877167369479), - FIXR(0.51763809020504152469), //0 - FIXR(0.55168895948124587824), - FIXR(0.61038729438072803416), - FIXR(0.70710678118654752439), //1 - FIXR(0.87172339781054900991), - FIXR(1.18310079157624925896), - FIXR(1.93185165257813657349), //2 - FIXR(5.73685662283492756461), -}; - -static const int icos36h[9] = { - FIXHR(0.50190991877167369479/2), - FIXHR(0.51763809020504152469/2), //0 - FIXHR(0.55168895948124587824/2), - FIXHR(0.61038729438072803416/2), - FIXHR(0.70710678118654752439/2), //1 - FIXHR(0.87172339781054900991/2), - FIXHR(1.18310079157624925896/4), - FIXHR(1.93185165257813657349/4), //2 -// FIXHR(5.73685662283492756461), -}; - -//////////////////////////////////////////////////////////////////////////////// - -static INLINE int unaligned32_be(const uint8_t *p) -{ - return (((p[0]<<8) | p[1])<<16) | (p[2]<<8) | (p[3]); -} - -#define MIN_CACHE_BITS 25 - -#define NEG_SSR32(a,s) ((( int32_t)(a))>>(32-(s))) -#define NEG_USR32(a,s) (((uint32_t)(a))>>(32-(s))) - -#define OPEN_READER(name, gb) \ - int name##_index= (gb)->index;\ - int name##_cache= 0;\ - -#define CLOSE_READER(name, gb)\ - (gb)->index= name##_index;\ - -#define UPDATE_CACHE(name, gb)\ - name##_cache= unaligned32_be(&((gb)->buffer[name##_index>>3])) << (name##_index&0x07); \ - -#define SKIP_CACHE(name, gb, num)\ - name##_cache <<= (num); - -#define SKIP_COUNTER(name, gb, num)\ - name##_index += (num);\ - -#define SKIP_BITS(name, gb, num)\ - {\ - SKIP_CACHE(name, gb, num)\ - SKIP_COUNTER(name, gb, num)\ - }\ - -#define LAST_SKIP_BITS(name, gb, num) SKIP_COUNTER(name, gb, num) -#define LAST_SKIP_CACHE(name, gb, num) ; - -#define SHOW_UBITS(name, gb, num)\ - NEG_USR32(name##_cache, num) - -#define SHOW_SBITS(name, gb, num)\ - NEG_SSR32(name##_cache, num) - -#define GET_CACHE(name, gb)\ - ((uint32_t)name##_cache) - -static INLINE int get_bits_count(bitstream_t *s){ - return s->index; -} - -static INLINE void skip_bits_long(bitstream_t *s, int n){ - s->index += n; -} -#define skip_bits skip_bits_long - -static void init_get_bits(bitstream_t *s, const uint8_t *buffer, int bit_size) { - int buffer_size= (bit_size+7)>>3; - if(buffer_size < 0 || bit_size < 0) { - buffer_size = bit_size = 0; - buffer = NULL; - } - s->buffer= buffer; - s->size_in_bits= bit_size; - s->buffer_end= buffer + buffer_size; - s->index=0; -} - -static INLINE unsigned int get_bits(bitstream_t *s, int n){ - register int tmp; - OPEN_READER(re, s) - UPDATE_CACHE(re, s) - tmp= SHOW_UBITS(re, s, n); - LAST_SKIP_BITS(re, s, n) - CLOSE_READER(re, s) - return tmp; -} - -static INLINE int get_bitsz(bitstream_t *s, int n) -{ - if (n == 0) - return 0; - else - return get_bits(s, n); -} - -static INLINE unsigned int get_bits1(bitstream_t *s){ - int index= s->index; - uint8_t result= s->buffer[ index>>3 ]; - result<<= (index&0x07); - result>>= 8 - 1; - index++; - s->index= index; - return result; -} - -static INLINE void align_get_bits(bitstream_t *s) -{ - int n= (-get_bits_count(s)) & 7; - if(n) skip_bits(s, n); -} - -#if 0 // OpenMPT -#define GET_DATA(v, table, i, wrap, size) \ -{\ - const uint8_t *ptr = (const uint8_t *)table + i * wrap;\ - switch(size) {\ - case 1:\ - v = *(const uint8_t *)ptr;\ - break;\ - case 2:\ - v = *(const uint16_t *)ptr;\ - break;\ - default:\ - v = *(const uint32_t *)ptr;\ - break;\ - }\ -} -#else // OpenMPT -#define GET_DATA(v, table, i, wrap, size) \ -{\ - const uint8_t *ptr = (const uint8_t *)table + i * wrap;\ - switch(size) {\ - case 1: {\ - uint8_t result = 0;\ - memcpy(&result, ptr, 1);\ - v = result;\ - } break;\ - case 2: {\ - uint16_t result = 0;\ - memcpy(&result, ptr, 2);\ - v = result;\ - } break;\ - default: {\ - uint32_t result = 0;\ - memcpy(&result, ptr, 4);\ - v = result;\ - } break;\ - }\ -} // OpenMPT -#endif // OpenMPT - -static INLINE int alloc_table(vlc_t *vlc, int size) { - int index; - index = vlc->table_size; - vlc->table_size += size; - if (vlc->table_size > vlc->table_allocated) { - vlc->table_allocated += (1 << vlc->bits); - vlc->table = libc_realloc(vlc->table, sizeof(VLC_TYPE) * 2 * vlc->table_allocated); - if (!vlc->table) - return -1; - } - return index; -} - -static int build_table( - vlc_t *vlc, int table_nb_bits, - int nb_codes, - const void *bits, int bits_wrap, int bits_size, - const void *codes, int codes_wrap, int codes_size, - uint32_t code_prefix, int n_prefix -) { - int i, j, k, n, table_size, table_index, nb, n1, index, code_prefix2; - uint32_t code; - VLC_TYPE (*table)[2]; - - table_size = 1 << table_nb_bits; - table_index = alloc_table(vlc, table_size); - if (table_index < 0) - return -1; - table = &vlc->table[table_index]; - - for(i=0;i> n; -#if 0 // OpenMPT - if (n > 0 && code_prefix2 == code_prefix) { -#else // OpenMPT - if (n > 0 && (uint32_t)code_prefix2 == code_prefix) { -#endif // OpenMPT - if (n <= table_nb_bits) { - j = (code << (table_nb_bits - n)) & (table_size - 1); - nb = 1 << (table_nb_bits - n); - for(k=0;k> n) & ((1 << table_nb_bits) - 1); - n1 = -table[j][1]; //bits - if (n > n1) - n1 = n; - table[j][1] = -n1; //bits - } - } - } - for(i=0;i table_nb_bits) { - n = table_nb_bits; - table[i][1] = -n; //bits - } - index = build_table(vlc, n, nb_codes, - bits, bits_wrap, bits_size, - codes, codes_wrap, codes_size, - (code_prefix << table_nb_bits) | i, - n_prefix + table_nb_bits); - if (index < 0) - return -1; - table = &vlc->table[table_index]; - table[i][0] = index; //code - } - } - return table_index; -} - -static INLINE int init_vlc( - vlc_t *vlc, int nb_bits, int nb_codes, - const void *bits, int bits_wrap, int bits_size, - const void *codes, int codes_wrap, int codes_size -) { - vlc->bits = nb_bits; - if (build_table(vlc, nb_bits, nb_codes, - bits, bits_wrap, bits_size, - codes, codes_wrap, codes_size, - 0, 0) < 0) { - libc_free(vlc->table); - return -1; - } - return 0; -} - -#define GET_VLC(code, name, gb, table, bits, max_depth)\ -{\ - int n, index, nb_bits;\ -\ - index= SHOW_UBITS(name, gb, bits);\ - code = table[index][0];\ - n = table[index][1];\ -\ - if(max_depth > 1 && n < 0){\ - LAST_SKIP_BITS(name, gb, bits)\ - UPDATE_CACHE(name, gb)\ -\ - nb_bits = -n;\ -\ - index= SHOW_UBITS(name, gb, nb_bits) + code;\ - code = table[index][0];\ - n = table[index][1];\ - if(max_depth > 2 && n < 0){\ - LAST_SKIP_BITS(name, gb, nb_bits)\ - UPDATE_CACHE(name, gb)\ -\ - nb_bits = -n;\ -\ - index= SHOW_UBITS(name, gb, nb_bits) + code;\ - code = table[index][0];\ - n = table[index][1];\ - }\ - }\ - SKIP_BITS(name, gb, n)\ -} - -static INLINE int get_vlc2(bitstream_t *s, VLC_TYPE (*table)[2], int bits, int max_depth) { - int code; - - OPEN_READER(re, s) - UPDATE_CACHE(re, s) - - GET_VLC(code, re, s, table, bits, max_depth) - - CLOSE_READER(re, s) - return code; -} - -static void switch_buffer(mp3_context_t *s, int *pos, int *end_pos, int *end_pos2) { - if(s->in_gb.buffer && *pos >= s->gb.size_in_bits){ - s->gb= s->in_gb; - s->in_gb.buffer=NULL; - skip_bits_long(&s->gb, *pos - *end_pos); - *end_pos2= - *end_pos= *end_pos2 + get_bits_count(&s->gb) - *pos; - *pos= get_bits_count(&s->gb); - } -} - -//////////////////////////////////////////////////////////////////////////////// - -static INLINE int mp3_check_header(uint32_t header){ - /* header */ - if ((header & 0xffe00000) != 0xffe00000) - return -1; - /* layer check */ - if ((header & (3<<17)) != (1 << 17)) - return -1; - /* bit rate */ - if ((header & (0xf<<12)) == 0xf<<12) - return -1; - /* frequency */ - if ((header & (3<<10)) == 3<<10) - return -1; - return 0; -} - - -static void lsf_sf_expand( - int *slen, int sf, int n1, int n2, int n3 -) { - if (n3) { - slen[3] = sf % n3; - sf /= n3; - } else { - slen[3] = 0; - } - if (n2) { - slen[2] = sf % n2; - sf /= n2; - } else { - slen[2] = 0; - } - slen[1] = sf % n1; - sf /= n1; - slen[0] = sf; -} - -static INLINE int l3_unscale(int value, int exponent) -{ - unsigned int m; - int e; - - e = table_4_3_exp [4*value + (exponent&3)]; - m = table_4_3_value[4*value + (exponent&3)]; - e -= (exponent >> 2); - if (e > 31) - return 0; - m = (m + (1 << (e-1))) >> e; - - return m; -} - -static INLINE int round_sample(int *sum) { - int sum1; - sum1 = (*sum) >> OUT_SHIFT; - *sum &= (1< OUT_MAX) - sum1 = OUT_MAX; - return sum1; -} - -static void exponents_from_scale_factors( - mp3_context_t *s, granule_t *g, int16_t *exponents -) { - const uint8_t *bstab, *pretab; - int len, i, j, k, l, v0, shift, gain, gains[3]; - int16_t *exp_ptr; - - exp_ptr = exponents; - gain = g->global_gain - 210; - shift = g->scalefac_scale + 1; - - bstab = band_size_long[s->sample_rate_index]; - pretab = mp3_pretab[g->preflag]; - for(i=0;ilong_end;i++) { - v0 = gain - ((g->scale_factors[i] + pretab[i]) << shift) + 400; - len = bstab[i]; - for(j=len;j>0;j--) - *exp_ptr++ = v0; - } - - if (g->short_start < 13) { - bstab = band_size_short[s->sample_rate_index]; - gains[0] = gain - (g->subblock_gain[0] << 3); - gains[1] = gain - (g->subblock_gain[1] << 3); - gains[2] = gain - (g->subblock_gain[2] << 3); - k = g->long_end; - for(i=g->short_start;i<13;i++) { - len = bstab[i]; - for(l=0;l<3;l++) { - v0 = gains[l] - (g->scale_factors[k++] << shift) + 400; - for(j=len;j>0;j--) - *exp_ptr++ = v0; - } - } - } -} - -static void reorder_block(mp3_context_t *s, granule_t *g) -{ - int i, j, len; - int32_t *ptr, *dst, *ptr1; - int32_t tmp[576]; - - if (g->block_type != 2) - return; - - if (g->switch_point) { - if (s->sample_rate_index != 8) { - ptr = g->sb_hybrid + 36; - } else { - ptr = g->sb_hybrid + 48; - } - } else { - ptr = g->sb_hybrid; - } - - for(i=g->short_start;i<13;i++) { - len = band_size_short[s->sample_rate_index][i]; - ptr1 = ptr; - dst = tmp; - for(j=len;j>0;j--) { - *dst++ = ptr[0*len]; - *dst++ = ptr[1*len]; - *dst++ = ptr[2*len]; - ptr++; - } - ptr+=2*len; - libc_memcpy(ptr1, tmp, len * 3 * sizeof(*ptr1)); - } -} - -static void compute_antialias(mp3_context_t *s, granule_t *g) { - int32_t *ptr, *csa; - int n, i; - - /* we antialias only "long" bands */ - if (g->block_type == 2) { - if (!g->switch_point) - return; - /* XXX: check this for 8000Hz case */ - n = 1; - } else { - n = SBLIMIT - 1; - } - - ptr = g->sb_hybrid + 18; - for(i = n;i > 0;i--) { - int tmp0, tmp1, tmp2; - csa = &csa_table[0][0]; -#define INT_AA(j) \ - tmp0 = ptr[-1-j];\ - tmp1 = ptr[ j];\ - tmp2= MULH(tmp0 + tmp1, csa[0+4*j]);\ - ptr[-1-j] = 4*(tmp2 - MULH(tmp1, csa[2+4*j]));\ - ptr[ j] = 4*(tmp2 + MULH(tmp0, csa[3+4*j])); - - INT_AA(0) - INT_AA(1) - INT_AA(2) - INT_AA(3) - INT_AA(4) - INT_AA(5) - INT_AA(6) - INT_AA(7) - - ptr += 18; - } -} - -static void compute_stereo( - mp3_context_t *s, granule_t *g0, granule_t *g1 -) { - int i, j, k, l; - int32_t v1, v2; - int sf_max, tmp0, tmp1, sf, len, non_zero_found; - int32_t (*is_tab)[16]; - int32_t *tab0, *tab1; - int non_zero_found_short[3]; - - if (s->mode_ext & MODE_EXT_I_STEREO) { - if (!s->lsf) { - is_tab = is_table; - sf_max = 7; - } else { - is_tab = is_table_lsf[g1->scalefac_compress & 1]; - sf_max = 16; - } - - tab0 = g0->sb_hybrid + 576; - tab1 = g1->sb_hybrid + 576; - - non_zero_found_short[0] = 0; - non_zero_found_short[1] = 0; - non_zero_found_short[2] = 0; - k = (13 - g1->short_start) * 3 + g1->long_end - 3; - for(i = 12;i >= g1->short_start;i--) { - /* for last band, use previous scale factor */ - if (i != 11) - k -= 3; - len = band_size_short[s->sample_rate_index][i]; - for(l=2;l>=0;l--) { - tab0 -= len; - tab1 -= len; - if (!non_zero_found_short[l]) { - /* test if non zero band. if so, stop doing i-stereo */ - for(j=0;jscale_factors[k + l]; - if (sf >= sf_max) - goto found1; - - v1 = is_tab[0][sf]; - v2 = is_tab[1][sf]; - for(j=0;jmode_ext & MODE_EXT_MS_STEREO) { - /* lower part of the spectrum : do ms stereo - if enabled */ - for(j=0;jlong_end - 1;i >= 0;i--) { - len = band_size_long[s->sample_rate_index][i]; - tab0 -= len; - tab1 -= len; - /* test if non zero band. if so, stop doing i-stereo */ - if (!non_zero_found) { - for(j=0;jscale_factors[k]; - if (sf >= sf_max) - goto found2; - v1 = is_tab[0][sf]; - v2 = is_tab[1][sf]; - for(j=0;jmode_ext & MODE_EXT_MS_STEREO) { - /* lower part of the spectrum : do ms stereo - if enabled */ - for(j=0;jmode_ext & MODE_EXT_MS_STEREO) { - /* ms stereo ONLY */ - /* NOTE: the 1/sqrt(2) normalization factor is included in the - global gain */ - tab0 = g0->sb_hybrid; - tab1 = g1->sb_hybrid; - for(i=0;i<576;i++) { - tmp0 = tab0[i]; - tmp1 = tab1[i]; - tab0[i] = tmp0 + tmp1; - tab1[i] = tmp0 - tmp1; - } - } -} - -static int huffman_decode( - mp3_context_t *s, granule_t *g, int16_t *exponents, int end_pos2 -) { - int s_index; - int i; - int last_pos, bits_left; - vlc_t *vlc; - int end_pos= s->gb.size_in_bits; - if (end_pos2 < end_pos) end_pos = end_pos2; - - /* low frequencies (called big values) */ - s_index = 0; - for(i=0;i<3;i++) { - int j, k, l, linbits; - j = g->region_size[i]; - if (j == 0) - continue; - /* select vlc table */ - k = g->table_select[i]; - l = mp3_huff_data[k][0]; - linbits = mp3_huff_data[k][1]; - vlc = &huff_vlc[l]; - - if(!l){ - libc_memset(&g->sb_hybrid[s_index], 0, sizeof(*g->sb_hybrid)*2*j); - s_index += 2*j; - continue; - } - - /* read huffcode and compute each couple */ - for(;j>0;j--) { - int exponent, x, y, v; - int pos= get_bits_count(&s->gb); - - if (pos >= end_pos){ - switch_buffer(s, &pos, &end_pos, &end_pos2); - if(pos >= end_pos) - break; - } - y = get_vlc2(&s->gb, vlc->table, 7, 3); - - if(!y){ - g->sb_hybrid[s_index ] = - g->sb_hybrid[s_index+1] = 0; - s_index += 2; - continue; - } - - exponent= exponents[s_index]; - - if(y&16){ - x = y >> 5; - y = y & 0x0f; - if (x < 15){ - v = expval_table[ exponent ][ x ]; - }else{ - x += get_bitsz(&s->gb, linbits); - v = l3_unscale(x, exponent); - } - if (get_bits1(&s->gb)) - v = -v; - g->sb_hybrid[s_index] = v; - if (y < 15){ - v = expval_table[ exponent ][ y ]; - }else{ - y += get_bitsz(&s->gb, linbits); - v = l3_unscale(y, exponent); - } - if (get_bits1(&s->gb)) - v = -v; - g->sb_hybrid[s_index+1] = v; - }else{ - x = y >> 5; - y = y & 0x0f; - x += y; - if (x < 15){ - v = expval_table[ exponent ][ x ]; - }else{ - x += get_bitsz(&s->gb, linbits); - v = l3_unscale(x, exponent); - } - if (get_bits1(&s->gb)) - v = -v; - g->sb_hybrid[s_index+!!y] = v; - g->sb_hybrid[s_index+ !y] = 0; - } - s_index+=2; - } - } - - /* high frequencies */ - vlc = &huff_quad_vlc[g->count1table_select]; - last_pos=0; - while (s_index <= 572) { - int pos, code; - pos = get_bits_count(&s->gb); - if (pos >= end_pos) { - if (pos > end_pos2 && last_pos){ - /* some encoders generate an incorrect size for this - part. We must go back into the data */ - s_index -= 4; - skip_bits_long(&s->gb, last_pos - pos); - break; - } - switch_buffer(s, &pos, &end_pos, &end_pos2); - if(pos >= end_pos) - break; - } - last_pos= pos; - - code = get_vlc2(&s->gb, vlc->table, vlc->bits, 1); - g->sb_hybrid[s_index+0]= - g->sb_hybrid[s_index+1]= - g->sb_hybrid[s_index+2]= - g->sb_hybrid[s_index+3]= 0; - while(code){ - const static int idxtab[16]={3,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0}; - int v; - int pos= s_index+idxtab[code]; - code ^= 8>>idxtab[code]; - v = exp_table[ exponents[pos] ]; - if(get_bits1(&s->gb)) - v = -v; - g->sb_hybrid[pos] = v; - } - s_index+=4; - } - libc_memset(&g->sb_hybrid[s_index], 0, sizeof(*g->sb_hybrid)*(576 - s_index)); - - /* skip extension bits */ - bits_left = end_pos2 - get_bits_count(&s->gb); - if (bits_left < 0) { - return -1; - } - skip_bits_long(&s->gb, bits_left); - - i= get_bits_count(&s->gb); - switch_buffer(s, &i, &end_pos, &end_pos2); - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// - -static void imdct12(int *out, int *in) -{ - int in0, in1, in2, in3, in4, in5, t1, t2; - - in0= in[0*3]; - in1= in[1*3] + in[0*3]; - in2= in[2*3] + in[1*3]; - in3= in[3*3] + in[2*3]; - in4= in[4*3] + in[3*3]; - in5= in[5*3] + in[4*3]; - in5 += in3; - in3 += in1; - - in2= MULH(2*in2, C3); - in3= MULH(4*in3, C3); - - t1 = in0 - in4; - t2 = MULH(2*(in1 - in5), icos36h[4]); - - out[ 7]= - out[10]= t1 + t2; - out[ 1]= - out[ 4]= t1 - t2; - - in0 += in4>>1; - in4 = in0 + in2; - in5 += 2*in1; - in1 = MULH(in5 + in3, icos36h[1]); - out[ 8]= - out[ 9]= in4 + in1; - out[ 2]= - out[ 3]= in4 - in1; - - in0 -= in2; - in5 = MULH(2*(in5 - in3), icos36h[7]); - out[ 0]= - out[ 5]= in0 - in5; - out[ 6]= - out[11]= in0 + in5; -} - -static void imdct36(int *out, int *buf, int *in, int *win) -{ - int i, j, t0, t1, t2, t3, s0, s1, s2, s3; - int tmp[18], *tmp1, *in1; - - for(i=17;i>=1;i--) - in[i] += in[i-1]; - for(i=17;i>=3;i-=2) - in[i] += in[i-2]; - - for(j=0;j<2;j++) { - tmp1 = tmp + j; - in1 = in + j; - t2 = in1[2*4] + in1[2*8] - in1[2*2]; - - t3 = in1[2*0] + (in1[2*6]>>1); - t1 = in1[2*0] - in1[2*6]; - tmp1[ 6] = t1 - (t2>>1); - tmp1[16] = t1 + t2; - - t0 = MULH(2*(in1[2*2] + in1[2*4]), C2); - t1 = MULH( in1[2*4] - in1[2*8] , -2*C8); - t2 = MULH(2*(in1[2*2] + in1[2*8]), -C4); - - tmp1[10] = t3 - t0 - t2; - tmp1[ 2] = t3 + t0 + t1; - tmp1[14] = t3 + t2 - t1; - - tmp1[ 4] = MULH(2*(in1[2*5] + in1[2*7] - in1[2*1]), -C3); - t2 = MULH(2*(in1[2*1] + in1[2*5]), C1); - t3 = MULH( in1[2*5] - in1[2*7] , -2*C7); - t0 = MULH(2*in1[2*3], C3); - - t1 = MULH(2*(in1[2*1] + in1[2*7]), -C5); - - tmp1[ 0] = t2 + t3 + t0; - tmp1[12] = t2 + t1 - t0; - tmp1[ 8] = t3 - t1 - t0; - } - - i = 0; - for(j=0;j<4;j++) { - t0 = tmp[i]; - t1 = tmp[i + 2]; - s0 = t1 + t0; - s2 = t1 - t0; - - t2 = tmp[i + 1]; - t3 = tmp[i + 3]; - s1 = MULH(2*(t3 + t2), icos36h[j]); - s3 = MULL(t3 - t2, icos36[8 - j]); - - t0 = s0 + s1; - t1 = s0 - s1; - out[(9 + j)*SBLIMIT] = MULH(t1, win[9 + j]) + buf[9 + j]; - out[(8 - j)*SBLIMIT] = MULH(t1, win[8 - j]) + buf[8 - j]; - buf[9 + j] = MULH(t0, win[18 + 9 + j]); - buf[8 - j] = MULH(t0, win[18 + 8 - j]); - - t0 = s2 + s3; - t1 = s2 - s3; - out[(9 + 8 - j)*SBLIMIT] = MULH(t1, win[9 + 8 - j]) + buf[9 + 8 - j]; - out[( j)*SBLIMIT] = MULH(t1, win[ j]) + buf[ j]; - buf[9 + 8 - j] = MULH(t0, win[18 + 9 + 8 - j]); - buf[ + j] = MULH(t0, win[18 + j]); - i += 4; - } - - s0 = tmp[16]; - s1 = MULH(2*tmp[17], icos36h[4]); - t0 = s0 + s1; - t1 = s0 - s1; - out[(9 + 4)*SBLIMIT] = MULH(t1, win[9 + 4]) + buf[9 + 4]; - out[(8 - 4)*SBLIMIT] = MULH(t1, win[8 - 4]) + buf[8 - 4]; - buf[9 + 4] = MULH(t0, win[18 + 9 + 4]); - buf[8 - 4] = MULH(t0, win[18 + 8 - 4]); -} - -static void compute_imdct( - mp3_context_t *s, granule_t *g, int32_t *sb_samples, int32_t *mdct_buf -) { - int32_t *ptr, *win, *win1, *buf, *out_ptr, *ptr1; - int32_t out2[12]; - int i, j, mdct_long_end, v, sblimit; - - /* find last non zero block */ - ptr = g->sb_hybrid + 576; - ptr1 = g->sb_hybrid + 2 * 18; - while (ptr >= ptr1) { - ptr -= 6; - v = ptr[0] | ptr[1] | ptr[2] | ptr[3] | ptr[4] | ptr[5]; - if (v != 0) - break; - } - sblimit = ((ptr - g->sb_hybrid) / 18) + 1; - - if (g->block_type == 2) { - /* XXX: check for 8000 Hz */ - if (g->switch_point) - mdct_long_end = 2; - else - mdct_long_end = 0; - } else { - mdct_long_end = sblimit; - } - - buf = mdct_buf; - ptr = g->sb_hybrid; - for(j=0;jswitch_point && j < 2) - win1 = mdct_win[0]; - else - win1 = mdct_win[g->block_type]; - /* select frequency inversion */ - win = win1 + ((4 * 36) & -(j & 1)); - imdct36(out_ptr, buf, ptr, win); - out_ptr += 18*SBLIMIT; - ptr += 18; - buf += 18; - } - for(j=mdct_long_end;j 32767) - v = 32767; - else if (v < -32768) - v = -32768; - synth_buf[j] = v; - } - /* copy to avoid wrap */ - libc_memcpy(synth_buf + 512, synth_buf, 32 * sizeof(int16_t)); - - samples2 = samples + 31 * incr; - w = window; - w2 = window + 31; - - sum = *dither_state; - p = synth_buf + 16; - SUM8(sum, +=, w, p); - p = synth_buf + 48; - SUM8(sum, -=, w + 32, p); - *samples = round_sample(&sum); - samples += incr; - w++; - - /* we calculate two samples at the same time to avoid one memory - access per two sample */ - for(j=1;j<16;j++) { - sum2 = 0; - p = synth_buf + 16 + j; - SUM8P2(sum, +=, sum2, -=, w, w2, p); - p = synth_buf + 48 - j; - SUM8P2(sum, -=, sum2, -=, w + 32, w2 + 32, p); - - *samples = round_sample(&sum); - samples += incr; - sum += sum2; - *samples2 = round_sample(&sum); - samples2 -= incr; - w++; - w2--; - } - - p = synth_buf + 32; - SUM8(sum, -=, w + 32, p); - *samples = round_sample(&sum); - *dither_state= sum; - - offset = (offset - 32) & 511; - *synth_buf_offset = offset; -} - -//////////////////////////////////////////////////////////////////////////////// - -static int decode_header(mp3_context_t *s, uint32_t header) { - int sample_rate, frame_size, mpeg25, padding; - int sample_rate_index, bitrate_index; - if (header & (1<<20)) { - s->lsf = (header & (1<<19)) ? 0 : 1; - mpeg25 = 0; - } else { - s->lsf = 1; - mpeg25 = 1; - } - - sample_rate_index = (header >> 10) & 3; - sample_rate = mp3_freq_tab[sample_rate_index] >> (s->lsf + mpeg25); - sample_rate_index += 3 * (s->lsf + mpeg25); - s->sample_rate_index = sample_rate_index; - s->error_protection = ((header >> 16) & 1) ^ 1; - s->sample_rate = sample_rate; - - bitrate_index = (header >> 12) & 0xf; - padding = (header >> 9) & 1; - s->mode = (header >> 6) & 3; - s->mode_ext = (header >> 4) & 3; - s->nb_channels = (s->mode == MP3_MONO) ? 1 : 2; - - if (bitrate_index != 0) { - frame_size = mp3_bitrate_tab[s->lsf][bitrate_index]; - s->bit_rate = frame_size * 1000; - s->frame_size = (frame_size * 144000) / (sample_rate << s->lsf) + padding; - } else { - /* if no frame size computed, signal it */ - return 1; - } - return 0; -} - -static int mp_decode_layer3(mp3_context_t *s) { - int nb_granules, main_data_begin, private_bits; - int gr, ch, blocksplit_flag, i, j, k, n, bits_pos; - granule_t *g; - static granule_t granules[2][2]; - static int16_t exponents[576]; - const uint8_t *ptr; - - if (s->lsf) { - main_data_begin = get_bits(&s->gb, 8); - private_bits = get_bits(&s->gb, s->nb_channels); - nb_granules = 1; - } else { - main_data_begin = get_bits(&s->gb, 9); - if (s->nb_channels == 2) - private_bits = get_bits(&s->gb, 3); - else - private_bits = get_bits(&s->gb, 5); - nb_granules = 2; - for(ch=0;chnb_channels;ch++) { - granules[ch][0].scfsi = 0; /* all scale factors are transmitted */ - granules[ch][1].scfsi = get_bits(&s->gb, 4); - } - } - - for(gr=0;grnb_channels;ch++) { - g = &granules[ch][gr]; - g->part2_3_length = get_bits(&s->gb, 12); - g->big_values = get_bits(&s->gb, 9); - g->global_gain = get_bits(&s->gb, 8); - /* if MS stereo only is selected, we precompute the - 1/sqrt(2) renormalization factor */ - if ((s->mode_ext & (MODE_EXT_MS_STEREO | MODE_EXT_I_STEREO)) == - MODE_EXT_MS_STEREO) - g->global_gain -= 2; - if (s->lsf) - g->scalefac_compress = get_bits(&s->gb, 9); - else - g->scalefac_compress = get_bits(&s->gb, 4); - blocksplit_flag = get_bits(&s->gb, 1); - if (blocksplit_flag) { - g->block_type = get_bits(&s->gb, 2); - if (g->block_type == 0) - return -1; - g->switch_point = get_bits(&s->gb, 1); - for(i=0;i<2;i++) - g->table_select[i] = get_bits(&s->gb, 5); - for(i=0;i<3;i++) - g->subblock_gain[i] = get_bits(&s->gb, 3); - /* compute huffman coded region sizes */ - if (g->block_type == 2) - g->region_size[0] = (36 / 2); - else { - if (s->sample_rate_index <= 2) - g->region_size[0] = (36 / 2); - else if (s->sample_rate_index != 8) - g->region_size[0] = (54 / 2); - else - g->region_size[0] = (108 / 2); - } - g->region_size[1] = (576 / 2); - } else { - int region_address1, region_address2, l; - g->block_type = 0; - g->switch_point = 0; - for(i=0;i<3;i++) - g->table_select[i] = get_bits(&s->gb, 5); - /* compute huffman coded region sizes */ - region_address1 = get_bits(&s->gb, 4); - region_address2 = get_bits(&s->gb, 3); - g->region_size[0] = - band_index_long[s->sample_rate_index][region_address1 + 1] >> 1; - l = region_address1 + region_address2 + 2; - /* should not overflow */ - if (l > 22) - l = 22; - g->region_size[1] = - band_index_long[s->sample_rate_index][l] >> 1; - } - /* convert region offsets to region sizes and truncate - size to big_values */ - g->region_size[2] = (576 / 2); - j = 0; - for(i=0;i<3;i++) { - k = g->region_size[i]; - if (g->big_values < k) k = g->big_values; - g->region_size[i] = k - j; - j = k; - } - - /* compute band indexes */ - if (g->block_type == 2) { - if (g->switch_point) { - /* if switched mode, we handle the 36 first samples as - long blocks. For 8000Hz, we handle the 48 first - exponents as long blocks (XXX: check this!) */ - if (s->sample_rate_index <= 2) - g->long_end = 8; - else if (s->sample_rate_index != 8) - g->long_end = 6; - else - g->long_end = 4; /* 8000 Hz */ - - g->short_start = 2 + (s->sample_rate_index != 8); - } else { - g->long_end = 0; - g->short_start = 0; - } - } else { - g->short_start = 13; - g->long_end = 22; - } - - g->preflag = 0; - if (!s->lsf) - g->preflag = get_bits(&s->gb, 1); - g->scalefac_scale = get_bits(&s->gb, 1); - g->count1table_select = get_bits(&s->gb, 1); - } - } - - ptr = s->gb.buffer + (get_bits_count(&s->gb)>>3); - /* now we get bits from the main_data_begin offset */ - if(main_data_begin > s->last_buf_size){ - s->last_buf_size= main_data_begin; - } - - memcpy(s->last_buf + s->last_buf_size, ptr, EXTRABYTES); - s->in_gb= s->gb; - init_get_bits(&s->gb, s->last_buf + s->last_buf_size - main_data_begin, main_data_begin*8); - - for(gr=0;grnb_channels;ch++) { - g = &granules[ch][gr]; - - bits_pos = get_bits_count(&s->gb); - - if (!s->lsf) { - uint8_t *sc; - int slen, slen1, slen2; - - /* MPEG1 scale factors */ - slen1 = slen_table[0][g->scalefac_compress]; - slen2 = slen_table[1][g->scalefac_compress]; - if (g->block_type == 2) { - n = g->switch_point ? 17 : 18; - j = 0; - if(slen1){ - for(i=0;iscale_factors[j++] = get_bits(&s->gb, slen1); - }else{ - libc_memset((void*) &g->scale_factors[j], 0, n); - j += n; -// for(i=0;iscale_factors[j++] = 0; - } - if(slen2){ - for(i=0;i<18;i++) - g->scale_factors[j++] = get_bits(&s->gb, slen2); - for(i=0;i<3;i++) - g->scale_factors[j++] = 0; - }else{ - for(i=0;i<21;i++) - g->scale_factors[j++] = 0; - } - } else { - sc = granules[ch][0].scale_factors; - j = 0; - for(k=0;k<4;k++) { - n = (k == 0 ? 6 : 5); - if ((g->scfsi & (0x8 >> k)) == 0) { - slen = (k < 2) ? slen1 : slen2; - if(slen){ - for(i=0;iscale_factors[j++] = get_bits(&s->gb, slen); - }else{ - libc_memset((void*) &g->scale_factors[j], 0, n); - j += n; -// for(i=0;iscale_factors[j++] = 0; - } - } else { - /* simply copy from last granule */ - for(i=0;iscale_factors[j] = sc[j]; - j++; - } - } - } - g->scale_factors[j++] = 0; - } - } else { - int tindex, tindex2, slen[4], sl, sf; - - /* LSF scale factors */ - if (g->block_type == 2) { - tindex = g->switch_point ? 2 : 1; - } else { - tindex = 0; - } - sf = g->scalefac_compress; - if ((s->mode_ext & MODE_EXT_I_STEREO) && ch == 1) { - /* intensity stereo case */ - sf >>= 1; - if (sf < 180) { - lsf_sf_expand(slen, sf, 6, 6, 0); - tindex2 = 3; - } else if (sf < 244) { - lsf_sf_expand(slen, sf - 180, 4, 4, 0); - tindex2 = 4; - } else { - lsf_sf_expand(slen, sf - 244, 3, 0, 0); - tindex2 = 5; - } - } else { - /* normal case */ - if (sf < 400) { - lsf_sf_expand(slen, sf, 5, 4, 4); - tindex2 = 0; - } else if (sf < 500) { - lsf_sf_expand(slen, sf - 400, 5, 4, 0); - tindex2 = 1; - } else { - lsf_sf_expand(slen, sf - 500, 3, 0, 0); - tindex2 = 2; - g->preflag = 1; - } - } - - j = 0; - for(k=0;k<4;k++) { - n = lsf_nsf_table[tindex2][tindex][k]; - sl = slen[k]; - if(sl){ - for(i=0;iscale_factors[j++] = get_bits(&s->gb, sl); - }else{ - libc_memset((void*) &g->scale_factors[j], 0, n); - j += n; -// for(i=0;iscale_factors[j++] = 0; - } - } - /* XXX: should compute exact size */ - libc_memset((void*) &g->scale_factors[j], 0, 40 - j); -// for(;j<40;j++) -// g->scale_factors[j] = 0; - } - - exponents_from_scale_factors(s, g, exponents); - - /* read Huffman coded residue */ - if (huffman_decode(s, g, exponents, - bits_pos + g->part2_3_length) < 0) - return -1; - } /* ch */ - - if (s->nb_channels == 2) - compute_stereo(s, &granules[0][gr], &granules[1][gr]); - - for(ch=0;chnb_channels;ch++) { - g = &granules[ch][gr]; - reorder_block(s, g); - compute_antialias(s, g); - compute_imdct(s, g, &s->sb_samples[ch][18 * gr][0], s->mdct_buf[ch]); - } - } /* gr */ - return nb_granules * 18; -} - -static int mp3_decode_main( - mp3_context_t *s, - int16_t *samples, const uint8_t *buf, int buf_size -) { - int i, nb_frames, ch; - int16_t *samples_ptr; - - init_get_bits(&s->gb, buf + HEADER_SIZE, (buf_size - HEADER_SIZE)*8); - - if (s->error_protection) - get_bits(&s->gb, 16); - - nb_frames = mp_decode_layer3(s); - - s->last_buf_size=0; - if(s->in_gb.buffer){ - align_get_bits(&s->gb); - i= (s->gb.size_in_bits - get_bits_count(&s->gb))>>3; - if(i >= 0 && i <= BACKSTEP_SIZE){ - libc_memmove(s->last_buf, s->gb.buffer + (get_bits_count(&s->gb)>>3), i); - s->last_buf_size=i; - } - s->gb= s->in_gb; - } - - align_get_bits(&s->gb); - i= (s->gb.size_in_bits - get_bits_count(&s->gb))>>3; - - if(i<0 || i > BACKSTEP_SIZE || nb_frames<0){ - i = buf_size - HEADER_SIZE; - if (BACKSTEP_SIZE < i) i = BACKSTEP_SIZE; - } - libc_memcpy(s->last_buf + s->last_buf_size, s->gb.buffer + buf_size - HEADER_SIZE - i, i); - s->last_buf_size += i; - - /* apply the synthesis filter */ - for(ch=0;chnb_channels;ch++) { - samples_ptr = samples + ch; - for(i=0;isynth_buf[ch], &(s->synth_buf_offset[ch]), - window, &s->dither_state, - samples_ptr, s->nb_channels, - s->sb_samples[ch][i] - ); - samples_ptr += 32 * s->nb_channels; - } - } - return nb_frames * 32 * sizeof(uint16_t) * s->nb_channels; -} - -//////////////////////////////////////////////////////////////////////////////// - -static int mp3_decode_init(mp3_context_t *s) { - static int init=0; - int i, j, k; - - if (!init) { - /* synth init */ - for(i=0;i<257;i++) { - int v; - v = mp3_enwindow[i]; - #if WFRAC_BITS < 16 - v = (v + (1 << (16 - WFRAC_BITS - 1))) >> (16 - WFRAC_BITS); - #endif - window[i] = v; - if ((i & 63) != 0) - v = -v; - if (i != 0) - window[512 - i] = v; - } - - /* huffman decode tables */ - for(i=1;i<16;i++) { - const huff_table_t *h = &mp3_huff_tables[i]; - int xsize, x, y; - unsigned int n; - uint8_t tmp_bits [512]; - uint16_t tmp_codes[512]; - - libc_memset(tmp_bits , 0, sizeof(tmp_bits )); - libc_memset(tmp_codes, 0, sizeof(tmp_codes)); - - xsize = h->xsize; - n = xsize * xsize; - - j = 0; - for(x=0;xbits [j ]; - tmp_codes[(x << 5) | y | ((x&&y)<<4)]= h->codes[j++]; - } - } - - init_vlc(&huff_vlc[i], 7, 512, - tmp_bits, 1, 1, tmp_codes, 2, 2); - } - for(i=0;i<2;i++) { - init_vlc(&huff_quad_vlc[i], i == 0 ? 7 : 4, 16, - mp3_quad_bits[i], 1, 1, mp3_quad_codes[i], 1, 1); - } - - for(i=0;i<9;i++) { - k = 0; - for(j=0;j<22;j++) { - band_index_long[i][j] = k; - k += band_size_long[i][j]; - } - band_index_long[i][22] = k; - } - - /* compute n ^ (4/3) and store it in mantissa/exp format */ - table_4_3_exp= libc_malloc(TABLE_4_3_SIZE * sizeof(table_4_3_exp[0])); - if(!table_4_3_exp) - return -1; - table_4_3_value= libc_malloc(TABLE_4_3_SIZE * sizeof(table_4_3_value[0])); - if(!table_4_3_value) - return -1; - - for(i=1;i>4); - double f= libc_pow(i&15, 4.0 / 3.0) * libc_pow(2, (exponent-400)*0.25 + FRAC_BITS + 5); - expval_table[exponent][i&15]= f; - if((i&15)==1) - exp_table[exponent]= f; - } - - for(i=0;i<7;i++) { - float f; - int v; - if (i != 6) { - f = tan((double)i * M_PI / 12.0); - v = FIXR(f / (1.0 + f)); - } else { - v = FIXR(1.0); - } - is_table[0][i] = v; - is_table[1][6 - i] = v; - } - for(i=7;i<16;i++) - is_table[0][i] = is_table[1][i] = 0.0; - - for(i=0;i<16;i++) { - double f; - int e, k; - - for(j=0;j<2;j++) { - e = -(j + 1) * ((i + 1) >> 1); - f = libc_pow(2.0, e / 4.0); - k = i & 1; - is_table_lsf[j][k ^ 1][i] = FIXR(f); - is_table_lsf[j][k][i] = FIXR(1.0); - } - } - - for(i=0;i<8;i++) { - float ci, cs, ca; - ci = ci_table[i]; - cs = 1.0 / sqrt(1.0 + ci * ci); - ca = cs * ci; - csa_table[i][0] = FIXHR(cs/4); - csa_table[i][1] = FIXHR(ca/4); - csa_table[i][2] = FIXHR(ca/4) + FIXHR(cs/4); - csa_table[i][3] = FIXHR(ca/4) - FIXHR(cs/4); - csa_table_float[i][0] = cs; - csa_table_float[i][1] = ca; - csa_table_float[i][2] = ca + cs; - csa_table_float[i][3] = ca - cs; - } - - /* compute mdct windows */ - for(i=0;i<36;i++) { - for(j=0; j<4; j++){ - double d; - - if(j==2 && i%3 != 1) - continue; - - d= sin(M_PI * (i + 0.5) / 36.0); - if(j==1){ - if (i>=30) d= 0; - else if(i>=24) d= sin(M_PI * (i - 18 + 0.5) / 12.0); - else if(i>=18) d= 1; - }else if(j==3){ - if (i< 6) d= 0; - else if(i< 12) d= sin(M_PI * (i - 6 + 0.5) / 12.0); - else if(i< 18) d= 1; - } - d*= 0.5 / cos(M_PI*(2*i + 19)/72); - if(j==2) - mdct_win[j][i/3] = FIXHR((d / (1<<5))); - else - mdct_win[j][i ] = FIXHR((d / (1<<5))); - } - } - for(j=0;j<4;j++) { - for(i=0;i<36;i+=2) { - mdct_win[j + 4][i] = mdct_win[j][i]; - mdct_win[j + 4][i + 1] = -mdct_win[j][i + 1]; - } - } - init = 1; - } - return 0; -} - -static int mp3_decode_frame( - mp3_context_t *s, - int16_t *out_samples, int *data_size, - uint8_t *buf, int buf_size -) { - uint32_t header; - int out_size; - int extra_bytes = 0; - -retry: - if(buf_size < HEADER_SIZE) - return -1; - - header = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - if(mp3_check_header(header) < 0){ - buf++; - buf_size--; - extra_bytes++; - goto retry; - } - - if (decode_header(s, header) == 1) { - s->frame_size = -1; - return -1; - } - - if(s->frame_size<=0 || s->frame_size > buf_size){ - return -1; // incomplete frame - } - if(s->frame_size < buf_size) { - buf_size = s->frame_size; - } - - out_size = mp3_decode_main(s, out_samples, buf, buf_size); - if(out_size>=0) - *data_size = out_size; - // else: Error while decoding MPEG audio frame. - s->frame_size += extra_bytes; - return buf_size; -} - -//////////////////////////////////////////////////////////////////////////////// - -#if 1 // OpenMPT -mp3_decoder_t *mp3_create(void) { // OpenMPT -#else // OpenMPT -mp3_decoder_t mp3_create(void) { -#endif // OpenMPT - void *dec = libc_calloc(sizeof(mp3_context_t), 1); - if (dec) mp3_decode_init((mp3_context_t*) dec); -#if 1 // OpenMPT - return (mp3_decoder_t*) dec; // OpenMPT -#else // OpenMPT - return (mp3_decoder_t) dec; -#endif // OpenMPT -} - -void mp3_done(mp3_decoder_t *dec) { - if (dec) libc_free(dec); -} - -int mp3_decode(mp3_decoder_t *dec, void *buf, int bytes, signed short *out, mp3_info_t *info) { - int res, size = -1; - mp3_context_t *s = (mp3_context_t*) dec; - if (!s) return 0; - res = mp3_decode_frame(s, (int16_t*) out, &size, buf, bytes); - if (res < 0) return 0; - if (info) { - info->sample_rate = s->sample_rate; - info->channels = s->nb_channels; - info->audio_bytes = size; - } - return s->frame_size; -} diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h index 06f49ebb6..a878b12db 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h @@ -1,23 +1,1807 @@ -#ifndef __MINIMP3_H_INCLUDED__ -#define __MINIMP3_H_INCLUDED__ +#ifndef MINIMP3_H +#define MINIMP3_H +/* + https://github.com/lieff/minimp3 + To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. + This software is distributed without any warranty. + See . +*/ +#include -#define MP3_MAX_SAMPLES_PER_FRAME (1152*2) +#define MINIMP3_MAX_SAMPLES_PER_FRAME (1152*2) -typedef struct _mp3_info { - int sample_rate; - int channels; - int audio_bytes; // generated amount of audio per frame -} mp3_info_t; +typedef struct +{ + int frame_bytes, channels, hz, layer, bitrate_kbps; +} mp3dec_frame_info_t; -typedef void* mp3_decoder_t; +typedef struct +{ + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + unsigned char header[4], reserv_buf[511]; +} mp3dec_t; -#if 1 // OpenMPT -extern mp3_decoder_t *mp3_create(void); // OpenMPT -#else // OpenMPT -extern mp3_decoder_t mp3_create(void); -#endif // OpenMPT -extern int mp3_decode(mp3_decoder_t *dec, void *buf, int bytes, signed short *out, mp3_info_t *info); -extern void mp3_done(mp3_decoder_t *dec); -#define mp3_free(dec) do { mp3_done(dec); dec = NULL; } while(0) +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ -#endif//__MINIMP3_H_INCLUDED__ +void mp3dec_init(mp3dec_t *dec); +#ifndef MINIMP3_FLOAT_OUTPUT +typedef int16_t mp3d_sample_t; +#else /* MINIMP3_FLOAT_OUTPUT */ +typedef float mp3d_sample_t; +void mp3dec_f32_to_s16(const float *in, int16_t *out, int num_samples); +#endif /* MINIMP3_FLOAT_OUTPUT */ +int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MINIMP3_H */ +#if defined(MINIMP3_IMPLEMENTATION) && !defined(_MINIMP3_IMPLEMENTATION_GUARD) +#define _MINIMP3_IMPLEMENTATION_GUARD + +#include +#include + +#define MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#ifndef MAX_FRAME_SYNC_MATCHES +#define MAX_FRAME_SYNC_MATCHES 10 +#endif /* MAX_FRAME_SYNC_MATCHES */ + +#define MAX_L3_FRAME_PAYLOAD_BYTES MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + +#define MAX_BITRESERVOIR_BYTES 511 +#define SHORT_BLOCK_TYPE 2 +#define STOP_BLOCK_TYPE 3 +#define MODE_MONO 3 +#define MODE_JOINT_STEREO 1 +#define HDR_SIZE 4 +#define HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define HDR_IS_CRC(h) (!((h[1]) & 1)) +#define HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define HDR_GET_MY_SAMPLE_RATE(h) (HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) + +#define BITS_DEQUANTIZER_OUT -1 +#define MAX_SCF (255 + BITS_DEQUANTIZER_OUT*4 - 210) +#define MAX_SCFI ((MAX_SCF + 3) & ~3) + +#define MINIMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MINIMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) + +#if !defined(MINIMP3_NO_SIMD) + +#if !defined(MINIMP3_ONLY_SIMD) && (defined(_M_X64) || defined(_M_ARM64) || defined(__x86_64__) || defined(__aarch64__)) +/* x64 always have SSE2, arm64 always have neon, no need for generic code */ +#define MINIMP3_ONLY_SIMD +#endif /* SIMD checks... */ + +#if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) +#if defined(_MSC_VER) +#include +#endif /* defined(_MSC_VER) */ +#include +#define HAVE_SSE 1 +#define HAVE_SIMD 1 +#define VSTORE _mm_storeu_ps +#define VLD _mm_loadu_ps +#define VSET _mm_set1_ps +#define VADD _mm_add_ps +#define VSUB _mm_sub_ps +#define VMUL _mm_mul_ps +#define VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 f4; +#if defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) +#define minimp3_cpuid __cpuid +#else /* defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) */ +static __inline__ __attribute__((always_inline)) void minimp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else /* defined(__x86_64__) */ + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif /* defined(__x86_64__) */ + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else /* defined(__PIC__) */ + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif /* defined(__PIC__)*/ +} +#endif /* defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) */ +static int have_simd() +{ +#ifdef MINIMP3_ONLY_SIMD + return 1; +#else /* MINIMP3_ONLY_SIMD */ + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; +#endif /* MINIMP3_TEST */ + if (g_have_simd) + goto end; + minimp3_cpuid(CPUInfo, 0); + g_have_simd = 1; + if (CPUInfo[0] > 0) + { + minimp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ + } +end: + return g_have_simd - 1; +#endif /* MINIMP3_ONLY_SIMD */ +} +#elif defined(__ARM_NEON) || defined(__aarch64__) +#include +#define HAVE_SIMD 1 +#define VSTORE vst1q_f32 +#define VLD vld1q_f32 +#define VSET vmovq_n_f32 +#define VADD vaddq_f32 +#define VSUB vsubq_f32 +#define VMUL vmulq_f32 +#define VMAC(a, x, y) vmlaq_f32(a, x, y) +#define VMSB(a, x, y) vmlsq_f32(a, x, y) +#define VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t f4; +static int have_simd() +{ /* TODO: detect neon for !MINIMP3_ONLY_SIMD */ + return 1; +} +#else /* SIMD checks... */ +#define HAVE_SIMD 0 +#ifdef MINIMP3_ONLY_SIMD +#error MINIMP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif /* MINIMP3_ONLY_SIMD */ +#endif /* SIMD checks... */ +#else /* !defined(MINIMP3_NO_SIMD) */ +#define HAVE_SIMD 0 +#endif /* !defined(MINIMP3_NO_SIMD) */ + +typedef struct +{ + const uint8_t *buf; + int pos, limit; +} bs_t; + +typedef struct +{ + float scf[3*64]; + uint8_t total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} L12_scale_info; + +typedef struct +{ + uint8_t tab_offset, code_tab_width, band_count; +} L12_subband_alloc_t; + +typedef struct +{ + const uint8_t *sfbtab; + uint16_t part_23_length, big_values, scalefac_compress; + uint8_t global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + uint8_t table_select[3], region_count[3], subblock_gain[3]; + uint8_t preflag, scalefac_scale, count1_table, scfsi; +} L3_gr_info_t; + +typedef struct +{ + bs_t bs; + uint8_t maindata[MAX_BITRESERVOIR_BYTES + MAX_L3_FRAME_PAYLOAD_BYTES]; + L3_gr_info_t gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + uint8_t ist_pos[2][39]; +} mp3dec_scratch_t; + +static void bs_init(bs_t *bs, const uint8_t *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} + +static uint32_t get_bits(bs_t *bs, int n) +{ + uint32_t next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const uint8_t *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} + +static int hdr_valid(const uint8_t *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (HDR_GET_LAYER(h) != 0) && + (HDR_GET_BITRATE(h) != 15) && + (HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int hdr_compare(const uint8_t *h1, const uint8_t *h2) +{ + return hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(HDR_IS_FREE_FORMAT(h1) ^ HDR_IS_FREE_FORMAT(h2)); +} + +static unsigned hdr_bitrate_kbps(const uint8_t *h) +{ + static const uint8_t halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!HDR_TEST_MPEG1(h)][HDR_GET_LAYER(h) - 1][HDR_GET_BITRATE(h)]; +} + +static unsigned hdr_sample_rate_hz(const uint8_t *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[HDR_GET_SAMPLE_RATE(h)] >> (int)!HDR_TEST_MPEG1(h) >> (int)!HDR_TEST_NOT_MPEG25(h); +} + +static unsigned hdr_frame_samples(const uint8_t *h) +{ + return HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)HDR_IS_FRAME_576(h)); +} + +static int hdr_frame_bytes(const uint8_t *h, int free_format_size) +{ + int frame_bytes = hdr_frame_samples(h)*hdr_bitrate_kbps(h)*125/hdr_sample_rate_hz(h); + if (HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; /* slot align */ + } + return frame_bytes ? frame_bytes : free_format_size; +} + +static int hdr_padding(const uint8_t *h) +{ + return HDR_TEST_PADDING(h) ? (HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} + +#ifndef MINIMP3_ONLY_MP3 +static const L12_subband_alloc_t *L12_subband_alloc_table(const uint8_t *hdr, L12_scale_info *sci) +{ + const L12_subband_alloc_t *alloc; + int mode = HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == MODE_MONO) ? 0 : (mode == MODE_JOINT_STEREO) ? (HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + + if (HDR_IS_LAYER_1(hdr)) + { + static const L12_subband_alloc_t g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!HDR_TEST_MPEG1(hdr)) + { + static const L12_subband_alloc_t g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const L12_subband_alloc_t g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = hdr_bitrate_kbps(hdr) >> (int)(mode != MODE_MONO); + if (!kbps) /* free-format */ + { + kbps = 192; + } + + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const L12_subband_alloc_t g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + + sci->total_bands = (uint8_t)nbands; + sci->stereo_bands = (uint8_t)MINIMP3_MIN(stereo_bands, nbands); + + return alloc; +} + +static void L12_read_scalefactors(bs_t *bs, uint8_t *pba, uint8_t *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + DQ(3),DQ(7),DQ(15),DQ(31),DQ(63),DQ(127),DQ(255),DQ(511),DQ(1023),DQ(2047),DQ(4095),DQ(8191),DQ(16383),DQ(32767),DQ(65535),DQ(3),DQ(5),DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} + +static void L12_read_scale_info(const uint8_t *hdr, bs_t *bs, L12_scale_info *sci) +{ + static const uint8_t g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const L12_subband_alloc_t *subband_alloc = L12_subband_alloc_table(hdr, sci); + + int i, k = 0, ba_bits = 0; + const uint8_t *ba_code_tab = g_bitalloc_code_tab; + + for (i = 0; i < sci->total_bands; i++) + { + uint8_t ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = sci->bitalloc[i] ? HDR_IS_LAYER_1(hdr) ? 2 : get_bits(bs, 2) : 6; + } + + L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} + +static int L12_dequantize_granule(float *grbuf, bs_t *bs, L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ + unsigned code = get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} + +static void L12_apply_scf_384(L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + memcpy(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif /* MINIMP3_ONLY_MP3 */ + +static int L3_read_side_info(bs_t *bs, L3_gr_info_t *gr, const uint8_t *hdr) +{ + static const uint8_t g_scf_long[8][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const uint8_t g_scf_short[8][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const uint8_t g_scf_mixed[8][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int sr_idx = HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + int gr_count = HDR_IS_MONO(hdr) ? 1 : 2; + + if (HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = get_bits(bs, 9); + scfsi = get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = get_bits(bs, 8 + gr_count) >> gr_count; + } + + do + { + if (HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (uint16_t)get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (uint16_t)get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (uint8_t)get_bits(bs, 8); + gr->scalefac_compress = (uint16_t)get_bits(bs, HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (get_bits(bs, 1)) + { + gr->block_type = (uint8_t)get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (uint8_t)get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (uint8_t)get_bits(bs, 3); + gr->subblock_gain[1] = (uint8_t)get_bits(bs, 3); + gr->subblock_gain[2] = (uint8_t)get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = get_bits(bs, 15); + gr->region_count[0] = (uint8_t)get_bits(bs, 4); + gr->region_count[1] = (uint8_t)get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (uint8_t)(tables >> 10); + gr->table_select[1] = (uint8_t)((tables >> 5) & 31); + gr->table_select[2] = (uint8_t)((tables) & 31); + gr->preflag = HDR_TEST_MPEG1(hdr) ? get_bits(bs, 1) : (gr->scalefac_compress >= 500); + gr->scalefac_scale = (uint8_t)get_bits(bs, 1); + gr->count1_table = (uint8_t)get_bits(bs, 1); + gr->scfsi = (uint8_t)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + + return main_data_begin; +} + +static void L3_read_scalefactors(uint8_t *scf, uint8_t *ist_pos, const uint8_t *scf_size, const uint8_t *scf_count, bs_t *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + memcpy(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + memset(scf, 0, cnt); + memset(ist_pos, 0, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = get_bits(bitbuf, bits); + ist_pos[k] = (s == max_scf ? -1 : s); + scf[k] = s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} + +static float L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = MINIMP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} + +static void L3_decode_scalefactors(const uint8_t *hdr, uint8_t *ist_pos, bs_t *bs, const L3_gr_info_t *gr, float *scf, int ch) +{ + static const uint8_t g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const uint8_t *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + uint8_t scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + + if (HDR_TEST_MPEG1(hdr)) + { + static const uint8_t g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (uint8_t)(part >> 2); + scf_size[3] = scf_size[2] = (uint8_t)(part & 3); + } else + { + static const uint8_t g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (uint8_t)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] += gr->subblock_gain[0] << sh; + iscf[gr->n_long_sfb + i + 1] += gr->subblock_gain[1] << sh; + iscf[gr->n_long_sfb + i + 2] += gr->subblock_gain[2] << sh; + } + } else if (gr->preflag) + { + static const uint8_t g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] += g_preamp[i]; + } + } + + gain_exp = gr->global_gain + BITS_DEQUANTIZER_OUT*4 - 210 - (HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = L3_ldexp_q2(1 << (MAX_SCFI/4), MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} + +static const float g_pow43[129 + 16] = { + 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f +}; + +static float L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + + if (x < 129) + { + return g_pow43[16 + x]; + } + + if (x < 1024) + { + mult = 16; + x <<= 3; + } + + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} + +static void L3_huffman(float *dst, bs_t *bs, const L3_gr_info_t *gr_info, const float *scf, int layer3gr_limit) +{ + static const int16_t tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, + -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const uint8_t tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205 }; + static const uint8_t tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const int16_t tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const uint8_t g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; + +#define PEEK_BITS(n) (bs_cache >> (32 - n)) +#define FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define CHECK_BITS while (bs_sh >= 0) { bs_cache |= (uint32_t)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const uint8_t *sfb = gr_info->sfbtab; + const uint8_t *bs_next_ptr = bs->buf + bs->pos/8; + uint32_t bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const int16_t *codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + do + { + np = *sfb++ / 2; + pairs_to_decode = MINIMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[PEEK_BITS(w)]; + while (leaf < 0) + { + FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[PEEK_BITS(w) - (leaf >> 3)]; + } + FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15 && linbits) + { + lsb += PEEK_BITS(linbits); + FLUSH_BITS(linbits); + CHECK_BITS; + *dst = one*L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + FLUSH_BITS(lsb ? 1 : 0); + } + CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + + for (np = 1 - big_val_cnt;; dst += 4) + { + const uint8_t *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + FLUSH_BITS(leaf & 7); + if (BSPOS > layer3gr_limit) + { + break; + } +#define RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((int32_t)bs_cache < 0) ? -one : one; FLUSH_BITS(1) } + RELOAD_SCALEFACTOR; + DEQ_COUNT1(0); + DEQ_COUNT1(1); + RELOAD_SCALEFACTOR; + DEQ_COUNT1(2); + DEQ_COUNT1(3); + CHECK_BITS; + } + + bs->pos = layer3gr_limit; +} + +static void L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if HAVE_SIMD + if (have_simd()) for (; i < n - 3; i += 4) + { + f4 vl = VLD(left + i); + f4 vr = VLD(right + i); + VSTORE(left + i, VADD(vl, vr)); + VSTORE(right + i, VSUB(vl, vr)); + } +#endif /* HAVE_SIMD */ + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} + +static void L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} + +static void L3_stereo_top_band(const float *right, const uint8_t *sfb, int nbands, int max_band[3]) +{ + int i, k; + + max_band[0] = max_band[1] = max_band[2] = -1; + + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} + +static void L3_stereo_process(float *left, const uint8_t *ist_pos, const uint8_t *sfb, const uint8_t *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = HDR_TEST_MPEG1(hdr) ? 7 : 64; + + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (HDR_TEST_MS_STEREO(hdr)) + { + L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} + +static void L3_intensity_stereo(float *left, uint8_t *ist_pos, const L3_gr_info_t *gr, const uint8_t *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + + L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = MINIMP3_MAX(MINIMP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = max_band[i] >= prev ? default_pos : ist_pos[prev]; + } + L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} + +static void L3_reorder(float *grbuf, float *scratch, const uint8_t *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + memcpy(grbuf, scratch, (dst - scratch)*sizeof(float)); +} + +static void L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if HAVE_SIMD + if (have_simd()) for (; i < 8; i += 4) + { + f4 vu = VLD(grbuf + 18 + i); + f4 vd = VLD(grbuf + 14 - i); + f4 vc0 = VLD(g_aa[0] + i); + f4 vc1 = VLD(g_aa[1] + i); + vd = VREV(vd); + VSTORE(grbuf + 18 + i, VSUB(VMUL(vu, vc0), VMUL(vd, vc1))); + vd = VADD(VMUL(vu, vc1), VMUL(vd, vc0)); + VSTORE(grbuf + 14 - i, VREV(vd)); + } +#endif /* HAVE_SIMD */ +#ifndef MINIMP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif /* MINIMP3_ONLY_SIMD */ + } +} + +static void L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} + +static void L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + L3_dct3_9(co); + L3_dct3_9(si); + + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + + i = 0; + +#if HAVE_SIMD + if (have_simd()) for (; i < 8; i += 4) + { + f4 vovl = VLD(overlap + i); + f4 vc = VLD(co + i); + f4 vs = VLD(si + i); + f4 vr0 = VLD(g_twid9 + i); + f4 vr1 = VLD(g_twid9 + 9 + i); + f4 vw0 = VLD(window + i); + f4 vw1 = VLD(window + 9 + i); + f4 vsum = VADD(VMUL(vc, vr1), VMUL(vs, vr0)); + VSTORE(overlap + i, VSUB(VMUL(vc, vr0), VMUL(vs, vr1))); + VSTORE(grbuf + i, VSUB(VMUL(vovl, vw0), VMUL(vsum, vw1))); + vsum = VADD(VMUL(vovl, vw1), VMUL(vsum, vw0)); + VSTORE(grbuf + 14 - i, VREV(vsum)); + } +#endif /* HAVE_SIMD */ + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} + +static void L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} + +static void L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + + L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} + +static void L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + memcpy(tmp, grbuf, sizeof(tmp)); + memcpy(grbuf, overlap, 6*sizeof(float)); + L3_imdct12(tmp, grbuf + 6, overlap + 6); + L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} + +static void L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} + +static void L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == SHORT_BLOCK_TYPE) + L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + L3_imdct36(grbuf, overlap, g_mdct_window[block_type == STOP_BLOCK_TYPE], 32 - n_long_bands); +} + +static void L3_save_reservoir(mp3dec_t *h, mp3dec_scratch_t *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > MAX_BITRESERVOIR_BYTES) + { + pos += remains - MAX_BITRESERVOIR_BYTES; + remains = MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + memmove(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} + +static int L3_restore_reservoir(mp3dec_t *h, bs_t *bs, mp3dec_scratch_t *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = MINIMP3_MIN(h->reserv, main_data_begin); + memcpy(s->maindata, h->reserv_buf + MINIMP3_MAX(0, h->reserv - main_data_begin), MINIMP3_MIN(h->reserv, main_data_begin)); + memcpy(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} + +static void L3_decode(mp3dec_t *h, mp3dec_scratch_t *s, L3_gr_info_t *gr_info, int nch) +{ + int ch; + + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + + if (HDR_TEST_I_STEREO(h->header)) + { + L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (HDR_IS_MS_STEREO(h->header)) + { + L3_midside_stereo(s->grbuf[0], 576); + } + + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + + L3_antialias(s->grbuf[ch], aa_bands); + L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + L3_change_sign(s->grbuf[ch]); + } +} + +static void mp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if HAVE_SIMD + if (have_simd()) for (; k < n; k += 4) + { + f4 t[4][8], *x; + float *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + f4 x0 = VLD(&y[i*18]); + f4 x1 = VLD(&y[(15 - i)*18]); + f4 x2 = VLD(&y[(16 + i)*18]); + f4 x3 = VLD(&y[(31 - i)*18]); + f4 t0 = VADD(x0, x3); + f4 t1 = VADD(x1, x2); + f4 t2 = VMUL_S(VSUB(x1, x2), g_sec[3*i + 0]); + f4 t3 = VMUL_S(VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = VADD(t0, t1); + x[8] = VMUL_S(VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = VADD(t3, t2); + x[24] = VMUL_S(VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = VSUB(x0, x7); x0 = VADD(x0, x7); + x7 = VSUB(x1, x6); x1 = VADD(x1, x6); + x6 = VSUB(x2, x5); x2 = VADD(x2, x5); + x5 = VSUB(x3, x4); x3 = VADD(x3, x4); + x4 = VSUB(x0, x3); x0 = VADD(x0, x3); + x3 = VSUB(x1, x2); x1 = VADD(x1, x2); + x[0] = VADD(x0, x1); + x[4] = VMUL_S(VSUB(x0, x1), 0.70710677f); + x5 = VADD(x5, x6); + x6 = VMUL_S(VADD(x6, x7), 0.70710677f); + x7 = VADD(x7, xt); + x3 = VMUL_S(VADD(x3, x4), 0.70710677f); + x5 = VSUB(x5, VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ + x7 = VADD(x7, VMUL_S(x5, 0.382683432f)); + x5 = VSUB(x5, VMUL_S(x7, 0.198912367f)); + x0 = VSUB(xt, x6); xt = VADD(xt, x6); + x[1] = VMUL_S(VADD(xt, x7), 0.50979561f); + x[2] = VMUL_S(VADD(x4, x3), 0.54119611f); + x[3] = VMUL_S(VSUB(x0, x5), 0.60134488f); + x[5] = VMUL_S(VADD(x0, x5), 0.89997619f); + x[6] = VMUL_S(VSUB(x4, x3), 1.30656302f); + x[7] = VMUL_S(VSUB(xt, x7), 2.56291556f); + } + + if (k > n - 3) + { +#if HAVE_SSE +#define VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else /* HAVE_SSE */ +#define VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) +#endif /* HAVE_SSE */ + for (i = 0; i < 7; i++, y += 4*18) + { + f4 s = VADD(t[3][i], t[3][i + 1]); + VSAVE2(0, t[0][i]); + VSAVE2(1, VADD(t[2][i], s)); + VSAVE2(2, VADD(t[1][i], t[1][i + 1])); + VSAVE2(3, VADD(t[2][1 + i], s)); + } + VSAVE2(0, t[0][7]); + VSAVE2(1, VADD(t[2][7], t[3][7])); + VSAVE2(2, t[1][7]); + VSAVE2(3, t[3][7]); + } else + { +#define VSAVE4(i, v) VSTORE(&y[i*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + f4 s = VADD(t[3][i], t[3][i + 1]); + VSAVE4(0, t[0][i]); + VSAVE4(1, VADD(t[2][i], s)); + VSAVE4(2, VADD(t[1][i], t[1][i + 1])); + VSAVE4(3, VADD(t[2][1 + i], s)); + } + VSAVE4(0, t[0][7]); + VSAVE4(1, VADD(t[2][7], t[3][7])); + VSAVE4(2, t[1][7]); + VSAVE4(3, t[3][7]); + } + } else +#endif /* HAVE_SIMD */ +#ifdef MINIMP3_ONLY_SIMD + {} +#else /* MINIMP3_ONLY_SIMD */ + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; /* rotate by PI/8 */ + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif /* MINIMP3_ONLY_SIMD */ +} + +#ifndef MINIMP3_FLOAT_OUTPUT +static int16_t mp3d_scale_pcm(float sample) +{ + if (sample >= 32766.5) return (int16_t) 32767; + if (sample <= -32767.5) return (int16_t)-32768; + int16_t s = (int16_t)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + return s; +} +#else /* MINIMP3_FLOAT_OUTPUT */ +static float mp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif /* MINIMP3_FLOAT_OUTPUT */ + +static void mp3d_synth_pair(mp3d_sample_t *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = mp3d_scale_pcm(a); + + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = mp3d_scale_pcm(a); +} + +static void mp3d_synth(float *xl, mp3d_sample_t *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + mp3d_sample_t *dstr = dstl + (nch - 1); + + static const float g_win[] = { + -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + + mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + mp3d_synth_pair(dstl, nch, lins + 4*15); + mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); + +#if HAVE_SIMD + if (have_simd()) for (i = 14; i >= 0; i--) + { +#define VLOAD(k) f4 w0 = VSET(*w++); f4 w1 = VSET(*w++); f4 vz = VLD(&zlin[4*i - 64*k]); f4 vy = VLD(&zlin[4*i - 64*(15 - k)]); +#define V0(k) { VLOAD(k) b = VADD(VMUL(vz, w1), VMUL(vy, w0)) ; a = VSUB(VMUL(vz, w0), VMUL(vy, w1)); } +#define V1(k) { VLOAD(k) b = VADD(b, VADD(VMUL(vz, w1), VMUL(vy, w0))); a = VADD(a, VSUB(VMUL(vz, w0), VMUL(vy, w1))); } +#define V2(k) { VLOAD(k) b = VADD(b, VADD(VMUL(vz, w1), VMUL(vy, w0))); a = VADD(a, VSUB(VMUL(vy, w1), VMUL(vz, w0))); } + f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + + V0(0) V2(1) V1(2) V2(3) V1(4) V2(5) V1(6) V2(7) + + { +#ifndef MINIMP3_FLOAT_OUTPUT +#if HAVE_SSE + static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + dstr[(15 - i)*nch] = _mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = _mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = _mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = _mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = _mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = _mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = _mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = _mm_extract_epi16(pcm8, 6); +#else /* HAVE_SSE */ + int16x4_t pcma, pcmb; + a = VADD(a, VSET(0.5f)); + b = VADD(b, VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif /* HAVE_SSE */ + +#else /* MINIMP3_FLOAT_OUTPUT */ + + static const f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + a = VMUL(a, g_scale); + b = VMUL(b, g_scale); +#if HAVE_SSE + _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); +#else /* HAVE_SSE */ + vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); + vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); + vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); + vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); + vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); + vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); + vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); + vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); +#endif /* HAVE_SSE */ +#endif /* MINIMP3_FLOAT_OUTPUT */ + } + } else +#endif /* HAVE_SIMD */ +#ifdef MINIMP3_ONLY_SIMD + {} +#else /* MINIMP3_ONLY_SIMD */ + for (i = 14; i >= 0; i--) + { +#define LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define S0(k) { int j; LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define S1(k) { int j; LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define S2(k) { int j; LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + + S0(0) S2(1) S1(2) S2(3) S1(4) S2(5) S1(6) S2(7) + + dstr[(15 - i)*nch] = mp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = mp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = mp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = mp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = mp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = mp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = mp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = mp3d_scale_pcm(b[2]); + } +#endif /* MINIMP3_ONLY_SIMD */ +} + +static void mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, mp3d_sample_t *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + mp3d_DCT_II(grbuf + 576*i, nbands); + } + + memcpy(lins, qmf_state, sizeof(float)*15*64); + + for (i = 0; i < nbands; i += 2) + { + mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef MINIMP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif /* MINIMP3_NONSTANDARD_BUT_LOGICAL */ + { + memcpy(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} + +static int mp3d_match_frame(const uint8_t *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += hdr_frame_bytes(hdr + i, frame_bytes) + hdr_padding(hdr + i); + if (i + HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} + +static int mp3d_find_frame(const uint8_t *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - HDR_SIZE; i++, mp3++) + { + if (hdr_valid(mp3)) + { + int frame_bytes = hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + hdr_padding(mp3); + + for (k = HDR_SIZE; !frame_bytes && k < MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - HDR_SIZE; k++) + { + if (hdr_compare(mp3, mp3 + k)) + { + int fb = k - hdr_padding(mp3); + int nextfb = fb + hdr_padding(mp3 + k); + if (i + k + nextfb + HDR_SIZE > mp3_bytes || !hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return i; +} + +void mp3dec_init(mp3dec_t *dec) +{ + dec->header[0] = 0; +} + +int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const uint8_t *hdr; + bs_t bs_frame[1]; + mp3dec_scratch_t scratch; + + if (mp3_bytes > 4 && dec->header[0] == 0xff && hdr_compare(dec->header, mp3)) + { + frame_size = hdr_frame_bytes(mp3, dec->free_format_bytes) + hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + HDR_SIZE > mp3_bytes || !hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + memset(dec, 0, sizeof(mp3dec_t)); + i = mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + + hdr = mp3 + i; + memcpy(dec->header, hdr, HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = hdr_sample_rate_hz(hdr); + info->layer = 4 - HDR_GET_LAYER(hdr); + info->bitrate_kbps = hdr_bitrate_kbps(hdr); + + if (!pcm) + { + return hdr_frame_samples(hdr); + } + + bs_init(bs_frame, hdr + HDR_SIZE, frame_size - HDR_SIZE); + if (HDR_IS_CRC(hdr)) + { + get_bits(bs_frame, 16); + } + + if (info->layer == 3) + { + int main_data_begin = L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + mp3dec_init(dec); + return 0; + } + success = L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success) + { + for (igr = 0; igr < (HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels) + { + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]); + } + } + L3_save_reservoir(dec, &scratch); + } else + { +#ifdef MINIMP3_ONLY_MP3 + return 0; +#else /* MINIMP3_ONLY_MP3 */ + L12_scale_info sci[1]; + L12_read_scale_info(hdr, bs_frame, sci); + + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]); + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + pcm += 384*info->channels; + } + if (bs_frame->pos > bs_frame->limit) + { + mp3dec_init(dec); + return 0; + } + } +#endif /* MINIMP3_ONLY_MP3 */ + } + return success*hdr_frame_samples(dec->header); +} + +#ifdef MINIMP3_FLOAT_OUTPUT +void mp3dec_f32_to_s16(const float *in, int16_t *out, int num_samples) +{ + if(num_samples > 0) + { + int i = 0; +#if HAVE_SIMD + int aligned_count = num_samples & ~7; + + for(;i < aligned_count;i+=8) + { + static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f }; + f4 a = VMUL(VLD(&in[i ]), g_scale); + f4 b = VMUL(VLD(&in[i+4]), g_scale); +#if HAVE_SSE + static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + out[i ] = _mm_extract_epi16(pcm8, 0); + out[i+1] = _mm_extract_epi16(pcm8, 1); + out[i+2] = _mm_extract_epi16(pcm8, 2); + out[i+3] = _mm_extract_epi16(pcm8, 3); + out[i+4] = _mm_extract_epi16(pcm8, 4); + out[i+5] = _mm_extract_epi16(pcm8, 5); + out[i+6] = _mm_extract_epi16(pcm8, 6); + out[i+7] = _mm_extract_epi16(pcm8, 7); +#else /* HAVE_SSE */ + int16x4_t pcma, pcmb; + a = VADD(a, VSET(0.5f)); + b = VADD(b, VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0))))); + vst1_lane_s16(out+i , pcma, 0); + vst1_lane_s16(out+i+1, pcma, 1); + vst1_lane_s16(out+i+2, pcma, 2); + vst1_lane_s16(out+i+3, pcma, 3); + vst1_lane_s16(out+i+4, pcmb, 0); + vst1_lane_s16(out+i+5, pcmb, 1); + vst1_lane_s16(out+i+6, pcmb, 2); + vst1_lane_s16(out+i+7, pcmb, 3); +#endif /* HAVE_SSE */ + } +#endif /* HAVE_SIMD */ + for(; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5) + out[i] = (int16_t) 32767; + else if (sample <= -32767.5) + out[i] = (int16_t)-32768; + else + { + int16_t s = (int16_t)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; + } + } + } +} +#endif /* MINIMP3_FLOAT_OUTPUT */ +#endif /* MINIMP3_IMPLEMENTATION && !_MINIMP3_IMPLEMENTATION_GUARD */ diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt index 0ab05f666..bfc81b2bd 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/OpenMPT.txt @@ -14,4 +14,5 @@ Modifications for OpenMPT: alignment from 1 to 4 [-Wcast-align]` has been fixed. * Prototypes of `tdefl_compressor_alloc` and `tinfl_decompressor_alloc` have beeen fixed + * Missing #ifndef MINIZ_NO_STDIO has been added to miniz.h. No further changes have been made. diff --git a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h index 11ab4038c..44ee43010 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h +++ b/Frameworks/OpenMPT/OpenMPT/include/miniz/miniz.h @@ -1250,7 +1250,9 @@ mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); /* Misc utils/helpers, valid for ZIP reading or writing */ mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO // OpenMPT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif // OpenMPT /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ mz_bool mz_zip_end(mz_zip_archive *pZip); @@ -1327,6 +1329,7 @@ mz_bool mz_zip_writer_end(mz_zip_archive *pZip); /* -------- Misc. high-level helper functions: */ +#ifndef MINIZ_NO_STDIO // OpenMPT /* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ /* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ @@ -1339,6 +1342,7 @@ mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, co /* Returns NULL on failure. */ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif // OpenMPT #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h index 46ea02c16..8136fa9b1 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h @@ -4,13 +4,28 @@ * Authors: Kenton Varda (C interface wrapper) */ -#ifndef MODPLUG_H__INCLUDED -#define MODPLUG_H__INCLUDED +#ifndef MODPLUG_MODPLUG_H +#define MODPLUG_MODPLUG_H #ifdef __cplusplus extern "C" { #endif +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +/* FIXME: USE VISIBILITY ATTRIBUTES HERE */ +#elif defined(MODPLUG_BUILD) +#define MODPLUG_EXPORT +#else +#define MODPLUG_EXPORT +#endif + struct _ModPlugFile; typedef struct _ModPlugFile ModPlugFile; @@ -29,21 +44,21 @@ typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); /* Load a mod file. [data] should point to a block of memory containing the complete * file, and [size] should be the size of that block. * Return the loaded mod file on success, or NULL on failure. */ -ModPlugFile* ModPlug_Load(const void* data, int size); +MODPLUG_EXPORT ModPlugFile* ModPlug_Load(const void* data, int size); /* Unload a mod file. */ -void ModPlug_Unload(ModPlugFile* file); +MODPLUG_EXPORT void ModPlug_Unload(ModPlugFile* file); /* Read sample data into the buffer. Returns the number of bytes read. If the end * of the mod has been reached, zero is returned. */ -int ModPlug_Read(ModPlugFile* file, void* buffer, int size); +MODPLUG_EXPORT int ModPlug_Read(ModPlugFile* file, void* buffer, int size); /* Get the name of the mod. The returned buffer is stored within the ModPlugFile * structure and will remain valid until you unload the file. */ -const char* ModPlug_GetName(ModPlugFile* file); +MODPLUG_EXPORT const char* ModPlug_GetName(ModPlugFile* file); /* Get the length of the mod, in milliseconds. Note that this result is not always * accurate, especially in the case of mods with loops. */ -int ModPlug_GetLength(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetLength(ModPlugFile* file); /* Seek to a particular position in the song. Note that seeking and MODs don't mix very * well. Some mods will be missing instruments for a short time after a seek, as ModPlug @@ -51,7 +66,7 @@ int ModPlug_GetLength(ModPlugFile* file); * playing at that time. (Doing so would be difficult and not very reliable.) Also, * note that seeking is not very exact in some mods -- especially those for which * ModPlug_GetLength() does not report the full length. */ -void ModPlug_Seek(ModPlugFile* file, int millisecond); +MODPLUG_EXPORT void ModPlug_Seek(ModPlugFile* file, int millisecond); enum _ModPlug_Flags { @@ -97,24 +112,24 @@ typedef struct _ModPlug_Settings /* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, * sampling rate, and loop count, will take effect immediately. Those options which don't * take effect immediately will take effect the next time you load a mod. */ -void ModPlug_GetSettings(ModPlug_Settings* settings); -void ModPlug_SetSettings(const ModPlug_Settings* settings); +MODPLUG_EXPORT void ModPlug_GetSettings(ModPlug_Settings* settings); +MODPLUG_EXPORT void ModPlug_SetSettings(const ModPlug_Settings* settings); /* New ModPlug API Functions */ /* NOTE: Master Volume (1-512) */ -unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; -void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; +MODPLUG_EXPORT unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +MODPLUG_EXPORT void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; -int ModPlug_GetCurrentSpeed(ModPlugFile* file); -int ModPlug_GetCurrentTempo(ModPlugFile* file); -int ModPlug_GetCurrentOrder(ModPlugFile* file); -int ModPlug_GetCurrentPattern(ModPlugFile* file); -int ModPlug_GetCurrentRow(ModPlugFile* file); -int ModPlug_GetPlayingChannels(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentSpeed(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentTempo(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentOrder(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentPattern(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentRow(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetPlayingChannels(ModPlugFile* file); -void ModPlug_SeekOrder(ModPlugFile* file,int order); -int ModPlug_GetModuleType(ModPlugFile* file); -char* ModPlug_GetMessage(ModPlugFile* file); +MODPLUG_EXPORT void ModPlug_SeekOrder(ModPlugFile* file,int order); +MODPLUG_EXPORT int ModPlug_GetModuleType(ModPlugFile* file); +MODPLUG_EXPORT char* ModPlug_GetMessage(ModPlugFile* file); #ifndef MODPLUG_NO_FILESAVE @@ -122,29 +137,29 @@ char* ModPlug_GetMessage(ModPlugFile* file); * EXPERIMENTAL Export Functions */ /*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ -char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); +MODPLUG_EXPORT char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); /*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ -char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); +MODPLUG_EXPORT char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); /*Export to a Amiga MOD file. EXPERIMENTAL.*/ -char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); +MODPLUG_EXPORT char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); /*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ -char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +MODPLUG_EXPORT char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); #endif // MODPLUG_NO_FILESAVE -unsigned int ModPlug_NumInstruments(ModPlugFile* file); -unsigned int ModPlug_NumSamples(ModPlugFile* file); -unsigned int ModPlug_NumPatterns(ModPlugFile* file); -unsigned int ModPlug_NumChannels(ModPlugFile* file); -unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); -unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); +MODPLUG_EXPORT unsigned int ModPlug_NumInstruments(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumSamples(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumPatterns(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumChannels(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); +MODPLUG_EXPORT unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); /* * Retrieve pattern note-data */ -ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); +MODPLUG_EXPORT ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); /* * ================= @@ -161,8 +176,8 @@ ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* nu * * (Samples are signed 32-bit integers) */ -void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; -void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; +MODPLUG_EXPORT void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +MODPLUG_EXPORT void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; #ifdef __cplusplus } /* extern "C" */ diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug_0.8.7.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug_0.8.7.h deleted file mode 100644 index 8e02ad8a8..000000000 --- a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug_0.8.7.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Kenton Varda (C interface wrapper) - */ - -#ifndef MODPLUG_H__INCLUDED -#define MODPLUG_H__INCLUDED - -#ifdef __cplusplus -extern "C" { -#endif - -struct _ModPlugFile; -typedef struct _ModPlugFile ModPlugFile; - -struct _ModPlugNote { - unsigned char Note; - unsigned char Instrument; - unsigned char VolumeEffect; - unsigned char Effect; - unsigned char Volume; - unsigned char Parameter; -}; -typedef struct _ModPlugNote ModPlugNote; - -typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); - -/* Load a mod file. [data] should point to a block of memory containing the complete - * file, and [size] should be the size of that block. - * Return the loaded mod file on success, or NULL on failure. */ -ModPlugFile* ModPlug_Load(const void* data, int size); -/* Unload a mod file. */ -void ModPlug_Unload(ModPlugFile* file); - -/* Read sample data into the buffer. Returns the number of bytes read. If the end - * of the mod has been reached, zero is returned. */ -int ModPlug_Read(ModPlugFile* file, void* buffer, int size); - -/* Get the name of the mod. The returned buffer is stored within the ModPlugFile - * structure and will remain valid until you unload the file. */ -const char* ModPlug_GetName(ModPlugFile* file); - -/* Get the length of the mod, in milliseconds. Note that this result is not always - * accurate, especially in the case of mods with loops. */ -int ModPlug_GetLength(ModPlugFile* file); - -/* Seek to a particular position in the song. Note that seeking and MODs don't mix very - * well. Some mods will be missing instruments for a short time after a seek, as ModPlug - * does not scan the sequence backwards to find out which instruments were supposed to be - * playing at that time. (Doing so would be difficult and not very reliable.) Also, - * note that seeking is not very exact in some mods -- especially those for which - * ModPlug_GetLength() does not report the full length. */ -void ModPlug_Seek(ModPlugFile* file, int millisecond); - -enum _ModPlug_Flags -{ - MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */ - MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */ - MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */ - MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */ - MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */ -}; - -enum _ModPlug_ResamplingMode -{ - MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */ - MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */ - MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */ - MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */ -}; - -typedef struct _ModPlug_Settings -{ - int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */ - - /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then - * down-mixes to the settings you choose. */ - int mChannels; /* Number of channels - 1 for mono or 2 for stereo */ - int mBits; /* Bits per sample - 8, 16, or 32 */ - int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */ - int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */ - - int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */ - int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */ - int mBassAmount; /* XBass level 0(quiet)-100(loud) */ - int mBassRange; /* XBass cutoff in Hz 10-100 */ - int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */ - int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */ - int mLoopCount; /* Number of times to loop. Zero prevents looping. - -1 loops forever. */ -} ModPlug_Settings; - -/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, - * sampling rate, and loop count, will take effect immediately. Those options which don't - * take effect immediately will take effect the next time you load a mod. */ -void ModPlug_GetSettings(ModPlug_Settings* settings); -void ModPlug_SetSettings(const ModPlug_Settings* settings); - -/* New ModPlug API Functions */ -/* NOTE: Master Volume (1-512) */ -unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; -void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; - -int ModPlug_GetCurrentSpeed(ModPlugFile* file); -int ModPlug_GetCurrentTempo(ModPlugFile* file); -int ModPlug_GetCurrentOrder(ModPlugFile* file); -int ModPlug_GetCurrentPattern(ModPlugFile* file); -int ModPlug_GetCurrentRow(ModPlugFile* file); -int ModPlug_GetPlayingChannels(ModPlugFile* file); - -void ModPlug_SeekOrder(ModPlugFile* file,int order); -int ModPlug_GetModuleType(ModPlugFile* file); -char* ModPlug_GetMessage(ModPlugFile* file); - - -#ifndef MODPLUG_NO_FILESAVE -/* - * EXPERIMENTAL Export Functions - */ -/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ -char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); - -/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ -char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); - -/*Export to a Amiga MOD file. EXPERIMENTAL.*/ -char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); - -/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ -char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); -#endif // MODPLUG_NO_FILESAVE - -unsigned int ModPlug_NumInstruments(ModPlugFile* file); -unsigned int ModPlug_NumSamples(ModPlugFile* file); -unsigned int ModPlug_NumPatterns(ModPlugFile* file); -unsigned int ModPlug_NumChannels(ModPlugFile* file); -unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); -unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); - -/* - * Retrieve pattern note-data - */ -ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); - -/* - * ================= - * Mixer callback - * ================= - * - * Use this callback if you want to 'modify' the mixed data of LibModPlug. - * - * void proc(int* buffer,unsigned long channels,unsigned long nsamples) ; - * - * 'buffer': A buffer of mixed samples - * 'channels': N. of channels in the buffer - * 'nsamples': N. of samples in the buffeer (without taking care of n.channels) - * - * (Samples are signed 32-bit integers) - */ -void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; -void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h index ed7d47e55..0273191f3 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h @@ -10,8 +10,8 @@ #define CONFIG_H_INCLUDED 1 #endif -#ifndef __SNDFILE_H -#define __SNDFILE_H +#ifndef MODPLUG_SNDFILE_H +#define MODPLUG_SNDFILE_H #ifdef UNDER_CE int _strnicmp(const char *str1,const char *str2, int n); @@ -456,7 +456,7 @@ typedef struct _MODCOMMAND // Mix Plugins #define MIXPLUG_MIXREADY 0x01 // Set when cleared -class IMixPlugin +class MODPLUG_EXPORT IMixPlugin { public: virtual ~IMixPlugin(); @@ -535,7 +535,7 @@ typedef VOID (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffe //============== -class CSoundFile +class MODPLUG_EXPORT CSoundFile //============== { public: // Static Members diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h index b1b69e772..735b8df5b 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h +++ b/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h @@ -6,8 +6,8 @@ * Adam Goode (endian and char fixes for PPC) */ -#ifndef _STDAFX_H_ -#define _STDAFX_H_ +#ifndef MODPLUG_STDAFX_H +#define MODPLUG_STDAFX_H /* Autoconf detection of stdint/inttypes */ #if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) @@ -48,6 +48,10 @@ inline void ProcessPlugins(int n) {} #define strnicmp(a,b,c) strncasecmp(a,b,c) #define HAVE_SINF 1 +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + #else #include @@ -121,6 +125,21 @@ inline void ProcessPlugins(int /* n */ ) {} #endif // _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +/* FIXME: USE VISIBILITY ATTRIBUTES HERE */ +#elif defined(MODPLUG_BUILD) +#define MODPLUG_EXPORT +#else +#define MODPLUG_EXPORT +#endif + #endif diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi index 849049f00..059e0ae5b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi @@ -1037,7 +1037,9 @@ Declare Function openmpt_module_get_metadata_keys_ Alias "openmpt_module_get_met \param key Metadata item key to query. Use openmpt_module_get_metadata_keys to check for available keys. Possible keys are: - type: Module format extension (e.g. it) - - type_long: Tracker name associated with the module format (e.g. Impulse Tracker) + - type_long: Format name associated with the module format (e.g. Impulse Tracker) + - originaltype: Module format extension (e.g. it) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) + - originaltype_long: Format name associated with the module format (e.g. Impulse Tracker) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) - container: Container format the module file is embedded in, if any (e.g. umx) - container_long: Full container name if the module is embedded in a container (e.g. Unreal Music) - tracker: Tracker that was (most likely) used to save the module file, if known @@ -1347,9 +1349,14 @@ Declare Function openmpt_module_highlight_pattern_row_channel_ Alias "openmpt_mo - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - seek.sync_samples: Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. - subsong: The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. + - play.at_end: Chooses the behaviour when the end of song is reached: + - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. + - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. + - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: - 0: No dithering. - 1: Default mode. Chosen by OpenMPT code, might change. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt index f4bc98fb4..327dd1fa2 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt @@ -1 +1,9 @@ + +foo_openmpt +=========== + +foo_openmpt is a module file (https://en.wikipedia.org/wiki/Module_file) decoder +component for foobar2000 >= 1.3.0. foo_openmpt is based on libopenmpt. + + See https://lib.openmpt.org/ for documentation, FAQ and other details. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/in_openmpt.txt b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/in_openmpt.txt index f4bc98fb4..d5035f98f 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/in_openmpt.txt +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/in_openmpt.txt @@ -1 +1,17 @@ + +in_openmpt +========== + +in_openmpt is a module file (https://en.wikipedia.org/wiki/Module_file) input +plugin for Winamp >= 2.0 (or compatible players). in_openmpt is based on +libopenmpt. + + +Installation +------------ + +"in_openmpt.dll" and "openmpt-mpg123.dll" must both be placed into the Winamp +"Plugins" directory. + + See https://lib.openmpt.org/ for documentation, FAQ and other details. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/xmp-openmpt.txt b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/xmp-openmpt.txt index f4bc98fb4..6720bee2d 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/xmp-openmpt.txt +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/xmp-openmpt.txt @@ -1 +1,16 @@ + +xmp-openmpt +=========== + +xmp-openmpt is a module file (https://en.wikipedia.org/wiki/Module_file) input +plugin for XMPlay >= 3.8.0.0. xmp-openmpt is based on libopenmpt. + + +Installation +------------ + +"xmp-openmpt.dll" and "openmpt-mpg123.dll" must both be placed into the XMPlay +plugins directory. + + See https://lib.openmpt.org/ for documentation, FAQ and other details. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index 939b4fd91..3dfb7d9b9 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,163 +5,161 @@ Changelog {#changelog} For fully detailed change log, please see the source repository directly. This is just a high-level summary. -### libopenmpt 0.3.12 (2018-09-24) +### libopenmpt 0.4.2 (2019-01-22) - * [**Bug**] openmpt123: Prevent libsdl2 and libsdl from being enabled at the - same time because they conflict with each other. - * [**Bug**] Make building the JavaScript package work with Emscripten version - 1.38.1 or later by disabling WebAssembly generation which generates a - different set of output files. Emscripten 1.38.1 changed the default to - WebAssembly. - * [**Bug**] libmodplug: Setting `SNDMIX_NORESAMPLING` in the C++ API always - resulted in linear interpolation instead of nearest neighbour. + * [**Sec**] DSM: Assertion failure during file parsing with debug STLs + (r11209). + * [**Sec**] J2B: Assertion failure during file parsing with debug STLs + (r11216). - * [**Change**] The old Emscripten configuration which is compatible with - Emscripten before 1.38.0 has been renamed to `CONFIG=emscripten-old`. + * S3M: Allow volume change of OPL instruments after Note Cut. - * libopenmpt now compiles without warnings with GCC 8. +### libopenmpt 0.4.1 (2019-01-06) - * Jump commands on the same row as the end of a pattern loop covering the - restart position of the module could cause the module to loop even when - looping was disabled. - * MO3: Reject overly long MP3 and Vorbis samples. - * `play_note` from the libopenmpt_ext interface sometimes silenced the start - of a triggered sample. + * [**Bug**] Binaries compiled for winold (Windows XP, Vista, 7, for CPUs + without SSE2 support) did not actually work on CPUs without SSE2 support. + * [**Bug**] libmodplug: Public symbols of the C++ API had `visibility=hidden` + set on non-MSVC systems, which made them not publicly accessible. + * [**Bug**] Project files for Windows 10 desktop builds on ARM and ARM64 + (`build/vs2017win10`) were missing from Windows source package. + * [**Bug**] MSVC project files in Windows source package lacked additional + files required to build DLLs. -### libopenmpt 0.3.11 (2018-07-28) + * MO3: Apply playback changes based on "ModPlug-made" header flag. - * [**Sec**] Crash with some malformed custom tunings in MPTM files (r10615). + * minimp3: Update to commit e9df0760e94044caded36a55d70ab4152134adc5 + (2018-12-23). - * Channels whose volume envelope was playing at volume 0 while being moved to - a NNA background channel were cut off completely since libopenmpt 0.3.8. - * AMF (ASYLUM): Convert 7-bit panning to 8-bit panning for playback. +### libopenmpt 0.4.0 (2018-12-23) -### libopenmpt 0.3.10 (2018-06-17) + * [**New**] libopenmpt now includes emulation of the OPL chip and thus plays + OPL instruments in S3M, C67 and MPTM files. OPL chip emulation volume can be + changed with the new ctl `render.opl.volume_factor`. + * [**New**] libopenmpt now supports CDFM / Composer 670 module files. + * [**New**] Autotools `configure` and plain `Makefile` now honor the variable + `CXXSTDLIB_PCLIBSPRIVATE` which serves the sole purpose of listing the + standard library (or libraries) required for static linking. The contents of + this variable will be put in `libopenmpt.pc` `Libs.private` and used for + nothing else. See \ref libopenmpt_c_staticlinking . + * [**New**] foo_openmpt: foo_openmpt now also works on Windows XP. + * [**New**] libopenmpt Emscripten builds now ship with MP3 support by + default, based on minimp3 by Lion (github.com/lieff). + * [**New**] libopenmpt: New ctl `play.at_end` can be used to change what + happens when the song end is reached: + * "fadeout": Fades the module out for a short while. Subsequent reads + after the fadeout will return 0 rendered frames. This is the default and + identical to the behaviour in previous libopenmpt versions. + * "continue": Returns 0 rendered frames when the song end is reached. + Subsequent reads will continue playing from the song start or loop + start. This can be used for custom loop logic, such as loop + auto-detection and longer fadeouts. + * "stop": Returns 0 rendered frames when the song end is reached. + Subsequent reads will return 0 rendered frames. + * [**New**] Add new metadata fields `"originaltype"` and `"originaltype_long"` + which allow more clearly reflecting what is going on with converted formats + like MO3 and GDM. + * [**New**] `Makefile` `CONFIG=emscripten` now can generate WebAssembly via + the additional option `EMSCRIPTEN_TARGET=wasm`. + * [**New**] Compiling for DOS is now experimentally supported via DJGPP GCC + 7.2 or later. + * [**Change**] minimp3: Instead of the LGPL-2.1-licensed minimp3 by KeyJ, + libopenmpt now uses the CC0-1.0-licensed minimp3 by Lion (github.com/lieff) + as a fallback if libmpg123 is unavailable. The `USE_MINIMP3` `Makefile` + option is gone and minimp3 will be used automatically in the `Makefile` + build system if libmpg123 is not available. + * [**Change**] openmpt123: openmpt123 now rejects `--output-type` in `--ui` + and `--batch` modes and also rejects `--output` in `--render` mode. These + combinations of options really made no sense and were rather confusing. + * [**Change**] Android NDK build system now uses libc++ (`c++_shared`) instead + of GNU libstdc++ (`gnustl_shared`), as recommended by Android NDK r16b. + * [**Change**] xmp-openmpt: `openmpt-mpg123.dll` is no longer optional and + must be placed into the same directory as `xmp-openmpt.dll`. + * [**Change**] in_openmpt: `openmpt-mpg123.dll` is no longer optional and must + be placed either into the directory of the player itself or into the same + directory as `in_openmpt.dll`. This is dependent on how the player loads its + plugins. For WinAMP 5, `openmpt-mpg123.dll` needs to be in the directory + which contains `winamp.exe`. `in_openmpt.dll` needs to be in the `Plugins` + directory. + * [**Change**] foo_openmpt: foo_openmpt is now packaged as a fb2k-component + package for easier installation. + * [**Change**] When building libopenmpt with MinGW-w64, it is now recommended + to use the posix thread model (as opposed to the win32 threading model), + because the former does support std::mutex while the latter does not. When + building with win32 threading model with the Autotools build system, it is + recommended to provide the `mingw-std-threads` package. Building libopenmpt + with MinGW-w64 without any `std::thread`/`std::mutex` support is deprecated + and support for such configurations will be removed in libopenmpt 0.5. + * [**Change**] `Makefile` `CONFIG=emscripten` now has 4 `EMSCRIPTEN_TARGET=` + settings: `wasm` generates WebAssembly, `asmjs128m` generates asm.js with a + fixed size 128MB heap, `asmjs` generates asm.js with a fixed default size + heap (as of Emscripten 1.38.11, this amounts to 16MB), `js` generates + JavaScript with dynamic heap growth and with compatibility for older VMs. + * [**Change**] libmodplug: Update public headers to libmodplug 0.8.8.5. This + adds support for kind-of automatic MODPLUG_EXPORT decoration on Windows. + + * [**Regression**] Support for Clang 3.4, 3.5 has been removed. + * [**Regression**] Building with Android NDK older than NDK r16b is not + supported any more. + * [**Regression**] Support for Emscripten versions older than 1.38.5 has been + removed. + * [**Regression**] Support for libmpg123 older than 1.14.0 has been removed. + * [**Regression**] Using MediaFoundation to decode MP3 samples is no longer + supported. Use libmpg123 or minimp3 instead. + * [**Regression**] libmodplug: Support for emulating libmodplug 0.8.7 API/ABI + has been removed. + + * [**Bug**] xmp-openmpt: Sample rate and number of output channels were not + applied correctly when using per-file settings. * [**Bug**] Internal mixer state was not initialized properly when initially rendering in 44100kHz stereo format. - * [**Bug**] AMF: Undefined behaviour in loader code could lead to files - playing silent. + * [**Bug**] openmpt123: Prevent libsdl2 and libsdl from being enabled at the + same time because they conflict with each other. + * [**Bug**] libmodplug: Setting `SNDMIX_NORESAMPLING` in the C++ API always + resulted in linear interpolation instead of nearest neighbour - * Switching between instruments with portamento did not update the NNA - settings for the new instrument. - * FAR: Properly import volume commands. - -### libopenmpt 0.3.9 (2018-04-29) - - * [**Sec**] Possible write near address 0 in out-of-memory situations when - reading AMS files (r10149). - - * [**Bug**] openmpt123: Fixed build failure in C++17 due to use of removed - feature `std::random_shuffle`. - - * STM: Having both Bxx and Cxx commands in a pattern imported the Bxx command - incorrectly. + * IT: In Compatible Gxx mode, allow sample changes next to a tone portamento + effect if a previous sample has already stopped playing. + * IT: Fix broken volume envelopes with negative values as found in breakdwn.it + by Elysis. + * MOD: Slides and delayed notes are executed on every repetition of a row with + row delay (fixes "ode to protracker"). + * XM: If the sustain point of the panning envelope is reached before key-off, + it is never released. + * XM: Do not default recall volume / panning for delayed instrument-less notes + * XM :E60 loop bug was not considered in song length calucation. + * S3M: Notes without instrument number use previous note's sample offset. + * Tighten M15 and MOD file rejection heuristics. + * J2B: Ignore frequency limits from file header. Fixes Medivo.j2b, broken + since libopenmpt-0.2.6401-beta17. + * STM: More accurate tempo calculation. + * STM: Better support for early format revisions (no such files have been + found in the wild, though). * STM: Last character of sample name was missing. - * Speed up reading of truncated ULT files. - * ULT: Portamento import was sometimes broken. - * The resonant filter was sometimes unstable when combining low-volume - samples, low cutoff and high mixing rates. - -### libopenmpt 0.3.8 (2018-04-08) - - * [**Sec**] Possible out-of-bounds memory read with IT and MO3 files - containing many nested pattern loops (r10028). - + * SFX: Work around bad conversions of the "Operation Stealth" soundtrack by + turning pattern breaks into note stops. + * IMF: Filter cutoff was upside down and the cutoff range was too small. + * ParamEq plugin center frequency was not limited correctly. * Keep track of active SFx macro during seeking. * The "note cut" duplicate note action did not volume-ramp the previously playing sample. * A song starting with non-existing patterns could not be played. * DSM: Support restart position and 16-bit samples. * DTM: Import global volume. + * MOD: Support notes in octave 2, like in FastTracker 2 (fixes DOPE.MOD). + * Do not apply Amiga playback heuristics to MOD files that have clearly been + written with a PC tracker. + * MPTM: More logical release node behaviour. + * Subsong search is now less thorough. It could previously find many subsongs + that are technically correct (unplayed rows at the beginning of patterns + that have been jumped over due to pattern breaks), but so far no real-world + module that would require such a thorough subsong detection was found. The + old mechanism caused way more false positives than intended with real-world + modules, though. + * Restrict the unpacked size of compressed DMF, IT, MDL and MO3 samples to + avoid huge allocations with malformed small files. -### libopenmpt 0.3.7 (2018-03-11) - - * [**Bug**] libopenmpt did not build with NDK r13b on armeabi due to missing - `-latomic`. - * [**Bug**] xmp-openmpt: Sample rate and number of output channels were not - applied correctly when using per-file settings. - - * [**Change**] foo_openmpt: foo_openmpt is now packaged as a fb2k-component - package for easier installation. - - * IT: More accurate song length calculation for pattern loops that have no - start command and are following another pattern loop. - * IMF: Filter cutoff was upside down and the cutoff range was too small. - * MED: Correctly import patterns with less channels than the maximum used - amount. Import "STP" note stop command. - * DBM: Key Off and Set Envelope Position were imported incorrectly. - High sample offset (E7x) is now supported. - * DIGI / DBM: Arpeggio should not return to base note at end of row. - * Some filter changes through MIDI macros were not applied if the note volume - was set to 0 on the same row. - -### libopenmpt 0.3.6 (2018-02-03) - - * [**Sec**] Possible out-of-bounds memory read with malformed STP files. - (r9576) - - * [**Bug**] Small memory leak with malformed STP files. - - * STM: Accurate emulation of Scream Tracker 2 tempo mode. - * STM: Better support for early format revisions (no such files have been - found in the wild, though). - * Fine volume slides are now supported when seeking with seek.sync_samples=1 - enabled. - -### libopenmpt 0.3.5 (2018-01-28) - - * [**New**] Support MOD files from the Inconexia demo by Iguana. - * [**Bug**] xmp-openmpt: Saved settings were not applied instantly. - - * XM E60 loop bug was not considered in song length calucation. - * Tighten M15 and MOD file rejection heuristics. - * J2B: Ignore frequency limits from file header. Fixes Medivo.j2b, broken - since libopenmpt-0.2.6401-beta17. - * ParamEq plugin center frequency was not limited correctly. - * libopenmpt_ext C API was not included in the documentation. - -### libopenmpt 0.3.4 (2017-12-17) - - * IT: Fix broken volume envelopes with negative values as found in breakdwn.it - by Elysis. - -### libopenmpt 0.3.3 (2017-11-19) - - * [**New**] foo_openmpt: foo_openmpt now also works on Windows XP. - - * [**Bug**] All VS2015 and VS2017 project files targetting Windows XP did not - set compiler option `/Zc:threadSafeInit-` which caused at least the player - plugins `in_openmpt` and `xmp-openmpt` to fail to load. - -### libopenmpt 0.3.2 (2017-11-04) - - * [**New**] Autotools `configure` and plain `Makefile` now honor the variable - `CXXSTDLIB_PCLIBSPRIVATE` which serves the sole purpose of listing the - standard library (or libraries) required for static linking. The contents of - this variable will be put in `libopenmpt.pc` `Libs.private` and used for - nothing else. See \ref libopenmpt_c_staticlinking . - - * [**Change**] Windows bin and dev release packages now use zip archives - instead of 7z archives as it had originally been intended for the 0.3.0 - release. - * [**Change**] openmpt123: The following combinations of options are now - deprecated because they made no real sense in the first place: - `--render --output`, `--ui --output-type`, `--batch --output-type` - - * [**Bug**] libopenmpt did not build on Android NDK 15c (and possibly - other versions between 12b and 15c as well). - - * IT: In Compatible Gxx mode, allow sample changes next to a tone portamento - effect if a previous sample has already stopped playing. - * MOD: Slides and delayed notes are executed on every repetition of a row with - row delay (fixes "ode to protracker"). - -### libopenmpt 0.3.1 (2017-09-28) - - * [**Bug**] Windows: libopenmpt resource did not compile for release versions. - -### libopenmpt 0.3.0 (2017-09-27, not released) +### libopenmpt 0.3 (2017-09-27) * [**New**] New error handling functionality in the C API, which in particular allows distinguishing potentially transient out-of-memory errors from parse diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md index c06634bbd..936619017 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md @@ -9,29 +9,33 @@ Dependencies ### libopenmpt * Supported compilers for building libopenmpt: - * **Microsoft Visual Studio 2015** or higher + * **Microsoft Visual Studio 2015** or higher, running on a x86-64 build + system (other target systems are supported) * **GCC 4.8** or higher - * **Clang 3.4** or higher - * **MinGW-W64 4.8** or higher - * **emscripten 1.31** or higher + * **Clang 3.6** or higher + * **MinGW-W64 4.8** or higher (it is recommended to preferably use + posix threading model as opposed to win32 threading model, or at least + have mingw-std-threads available otherwise) + * **emscripten 1.38.5** or higher + * **DJGPP GCC 7.2** or higher * any other **C++11 compliant** compiler (full standard compliant mode is - known to work with GCC >= 5.1 and Clang >= 3.5) + known to work with GCC >= 5.1 and Clang) libopenmpt makes the following assumptions about the C++ implementation used for building: - * `CHAR_BIT == 8` (enforced by static_assert) + * `std::numeric_limits::digits == 8` (enforced by + static_assert) * `sizeof(char) == 1` (enforced by static_assert) * existence of `std::uintptr_t` (enforced by static_assert) * `sizeof(float) == 4` (enforced by static_assert) * `sizeof(double) == 8` (enforced by static_assert) - * if `__BYTE_ORDER__` is provided by the compiler and - `__STDC_IEC_559__ == 1`, the endianness of integers is the same as - the endianness of floats (implicitly assumed) * `wchar_t` encoding is either UTF-16 or UTF-32 (implicitly assumed) * representation of basic source character set is ASCII (implicitly assumed) * representation of basic source character set is identical in char and `wchar_t` (implicitly assumed) + * libopenmpt also has experimental support for platforms without + `wchar_t` support like DJGPP libopenmpt does not rely on any specific implementation defined or undefined behaviour (if it does, that's a bug in libopenmpt). In @@ -50,13 +54,12 @@ Dependencies * **zlib** * **miniz** can be used internally if no zlib is available. * Built-in **MO3** support requires: - * **libmpg123 >= 1.13.0** + * **libmpg123 >= 1.14.0** * **libogg** * **libvorbis** * **libvorbisfile** - * Alternatively, **Media Foundation** can be used on Windows 7 or later - instead of libmpg123 to decode mp3 samples. It's also possible to use - **minimp3**. + * Instead of libmpg123, **minimp3 by Lion (github.com/lieff)** can be used + internally to decode MP3 samples. * Instead of libogg, libvorbis and libvorbisfile, **stb_vorbis** can be used internally to decode Vorbis samples. * Building on Unix-like systems requires: @@ -70,10 +73,12 @@ Dependencies ### openmpt123 * Supported compilers for building openmpt123: - * **Microsoft Visual Studio 2015** or higher + * **Microsoft Visual Studio 2015** or higher, running on a x86-64 build + system (other target systems are supported) * **GCC 4.8** or higher - * **Clang 3.4** or higher + * **Clang 3.6** or higher * **MinGW-W64 4.8** or higher + * **DJGPP GCC 7.2** or higher * any **C++11 compliant** compiler * Live sound output requires one of: * **PulseAudio** @@ -81,6 +86,7 @@ Dependencies * **SDL 1.2** * **PortAudio v19** * **Win32** + * **liballegro 4.2** on DJGPP/DOS Optional dependencies @@ -104,3 +110,19 @@ Optional dependencies * raw PCM has no external dependencies * **help2man** is required to build the documentation. + +Source packages +--------------- + +Building the source packages additionally requires: + * 7z (7-zip) + * autoconf + * autoconf-archive + * automake + * gzip + * help2man + * libtool + * subversion + * tar + * xpath (libxml-xpath-perl) + * zip diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md index 2b2b4188c..bc7563604 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md @@ -7,11 +7,11 @@ Quick Start {#quickstart} 1. Grab a `libopenmpt-VERSION.autotools.tar.gz` tarball. 2. Get dependencies: - - **gcc >= 4.8** or **clang >= 3.4** + - **gcc >= 4.8** or **clang >= 3.6** - **pkg-config >= 0.24** - **zlib** - **libogg**, **libvorbis**, **libvorbisfile** - - **libmpg123 >= 1.13.0** + - **libmpg123 >= 1.14.0** - **doxygen >= 1.8** - **libpulse**, **libpulse-simple** (required only by openmpt123) - **portaudio-v19** (required only by openmpt123) @@ -42,11 +42,11 @@ Quick Start {#quickstart} 1. Get dependencies: - **GNU make** - - **gcc >= 4.8** or **clang >= 3.4** + - **gcc >= 4.8** or **clang >= 3.6** - **pkg-config** - **zlib** - **libogg**, **libvorbis**, **libvorbisfile** - - **libmpg123 >= 1.13.0** + - **libmpg123 >= 1.14.0** - **libpulse**, **libpulse-simple** (required only by openmpt123) - **portaudio-v19** (required only by openmpt123) - **libFLAC** (required only by openmpt123) diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp index b8666483f..0172bdd0d 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp @@ -3,8 +3,15 @@ #pragma warning(disable:4091) #endif +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) +#endif #include "foobar2000/SDK/foobar2000.h" #include "foobar2000/helpers/helpers.h" +#if defined(_MSC_VER) +#pragma warning(pop) +#endif #include "libopenmpt.hpp" @@ -136,24 +143,10 @@ struct foo_openmpt_settings { -// Sample initquit implementation. See also: initquit class documentation in relevant header. - -class myinitquit : public initquit { -public: - void on_init() { - // console::print("Sample component: on_init()"); - } - void on_quit() { - // console::print("Sample component: on_quit()"); - } -}; - -static initquit_factory_t g_myinitquit_factory; - - - -// No inheritance. Our methods get called over input framework templates. See input_singletrack_impl for descriptions of what each method does. -class input_openmpt { +// Note that input class does *not* implement virtual methods or derive from interface classes. +// Our methods get called over input framework templates. See input_singletrack_impl for descriptions of what each method does. +// input_stubs just provides stub implementations of mundane methods that are irrelevant for most implementations. +class input_openmpt : public input_stubs { public: void open(service_ptr_t p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) { if ( p_reason == input_open_info_write ) { @@ -276,7 +269,15 @@ public: std::transform( ext.begin(), ext.end(), ext.begin(), tolower ); return std::find( extensions.begin(), extensions.end(), ext ) != extensions.end(); } -public: + static GUID g_get_guid() { + // {B0B7CCC3-4520-44D3-B5F9-22EB9EBA7575} + static const GUID foo_openmpt_guid = { 0xb0b7ccc3, 0x4520, 0x44d3, { 0xb5, 0xf9, 0x22, 0xeb, 0x9e, 0xba, 0x75, 0x75 } }; + return foo_openmpt_guid; + } + static const char * g_get_name() { + return "OpenMPT Module Decoder"; + } +private: service_ptr_t m_file; static const std::size_t buffersize = 1024; foo_openmpt_settings settings; @@ -286,6 +287,7 @@ public: std::vector rear_left; std::vector rear_right; std::vector buffer; +public: input_openmpt() : mod(0), left(buffersize), right(buffersize), rear_left(buffersize), rear_right(buffersize), buffer(4*buffersize) {} ~input_openmpt() { delete mod; mod = 0; } }; @@ -293,53 +295,29 @@ public: static input_singletrack_factory_t g_input_openmpt_factory; +class input_file_type_v2_impl_openmpt : public input_file_type_v2 { +public: + input_file_type_v2_impl_openmpt() + : extensions( openmpt::get_supported_extensions() ) + { } + unsigned get_count() { + return static_cast( extensions.size() ); + } + bool is_associatable( unsigned idx ) { + return true; + } + void get_format_name( unsigned idx, pfc::string_base & out, bool isPlural ) { + if ( isPlural ) { + out = "OpenMPT compatible module files"; + } else { + out = "OpenMPT compatible module file"; + } + } + void get_extensions( unsigned idx, pfc::string_base & out ) { + out = extensions[idx].c_str(); + } +private: + std::vector extensions; +}; -// copied table from soundlib/Tables.cpp -// the foobar2000 interface is stupid demanding to declare those statically - -DECLARE_FILE_TYPE("OpenMPT compatible module files", - "*.mod" ";" - "*.s3m" ";" - "*.xm" ";" - "*.it" ";" - "*.mptm" ";" - "*.stm" ";" - "*.nst" ";" - "*.m15" ";" - "*.stk" ";" - "*.st26" ";" - "*.pt36" ";" - "*.ice" ";" - "*.wow" ";" - "*.ult" ";" - "*.669" ";" - "*.mtm" ";" - "*.med" ";" - "*.far" ";" - "*.mdl" ";" - "*.ams" ";" - "*.ams" ";" - "*.dsm" ";" - "*.dtm" ";" - "*.amf" ";" - "*.amf" ";" - "*.okt" ";" - "*.dmf" ";" - "*.ptm" ";" - "*.psm" ";" - "*.mt2" ";" - "*.dbm" ";" - "*.digi" ";" - "*.imf" ";" - "*.j2b" ";" - "*.plm" ";" - "*.stp" ";" - "*.sfx" ";" - "*.sfx2" ";" - "*.mms" ";" - "*.gdm" ";" - "*.umx" ";" - "*.mo3" ";" - "*.xpk" ";" - "*.ppm" ";" - "*.mmcmp" ); +namespace { static service_factory_single_t g_filetypes; } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp index 0993eb8d4..f939860a7 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp @@ -37,27 +37,14 @@ #endif // _MSC_VER #include "libopenmpt.hpp" -#ifdef LIBOPENMPT_QUIRK_NO_CSTDINT -#include -namespace std { -typedef ::int8_t int8_t; -typedef ::int16_t int16_t; -typedef ::int32_t int32_t; -typedef ::int64_t int64_t; -typedef ::uint8_t uint8_t; -typedef ::uint16_t uint16_t; -typedef ::uint32_t uint32_t; -typedef ::uint64_t uint64_t; -} -#endif #include "libopenmpt_plugin_gui.hpp" #include "svn_version.h" #if defined(OPENMPT_VERSION_REVISION) -static char * in_openmpt_string = "in_openmpt " OPENMPT_API_VERSION_STRING "." OPENMPT_API_VERSION_STRINGIZE(OPENMPT_VERSION_REVISION); +static const char * in_openmpt_string = "in_openmpt " OPENMPT_API_VERSION_STRING "." OPENMPT_API_VERSION_STRINGIZE(OPENMPT_VERSION_REVISION); #else -static char * in_openmpt_string = "in_openmpt " OPENMPT_API_VERSION_STRING; +static const char * in_openmpt_string = "in_openmpt " OPENMPT_API_VERSION_STRING; #endif #ifndef NOMINMAX @@ -209,7 +196,7 @@ static void config( HWND hwndParent ) { static void about( HWND hwndParent ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2018 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2019 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; @@ -452,7 +439,7 @@ static DWORD WINAPI DecodeThread( LPVOID ) { In_Module inmod = { IN_VER, - in_openmpt_string, // SHORT_TITLE, + const_cast< char * >( in_openmpt_string ), // SHORT_TITLE, 0, // hMainWindow 0, // hDllInstance NULL, // filled later in Init() "mptm\0ModPlug Tracker Module (*.mptm)\0", diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h index f219b01b7..d41324885 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h @@ -167,8 +167,8 @@ /*! \defgroup libopenmpt_c libopenmpt C */ /*! \addtogroup libopenmpt_c - @{ -*/ + * @{ + */ #ifdef __cplusplus extern "C" { @@ -293,6 +293,9 @@ typedef int64_t (*openmpt_stream_tell_func)( void * stream ); /*! \brief Stream callbacks * * Stream callbacks used by libopenmpt for stream operations. + * \sa openmpt_stream_get_file_callbacks + * \sa openmpt_stream_get_fd_callbacks + * \sa openmpt_stream_get_buffer_callbacks */ typedef struct openmpt_stream_callbacks { @@ -1120,7 +1123,9 @@ LIBOPENMPT_API const char * openmpt_module_get_metadata_keys( openmpt_module * m * \param key Metadata item key to query. Use openmpt_module_get_metadata_keys to check for available keys. * Possible keys are: * - type: Module format extension (e.g. it) - * - type_long: Tracker name associated with the module format (e.g. Impulse Tracker) + * - type_long: Format name associated with the module format (e.g. Impulse Tracker) + * - originaltype: Module format extension (e.g. it) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) + * - originaltype_long: Format name associated with the module format (e.g. Impulse Tracker) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) * - container: Container format the module file is embedded in, if any (e.g. umx) * - container_long: Full container name if the module is embedded in a container (e.g. Unreal Music) * - tracker: Tracker that was (most likely) used to save the module file, if known @@ -1397,9 +1402,14 @@ LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmp * - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. * - seek.sync_samples: Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. * - subsong: The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. + * - play.at_end: Chooses the behaviour when the end of song is reached: + * - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. + * - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. + * - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. * - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. * - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - * - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + * - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + * - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. * - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: * - 0: No dithering. * - 1: Default mode. Chosen by OpenMPT code, might change. @@ -1432,8 +1442,8 @@ LIBOPENMPT_API int openmpt_module_ctl_set( openmpt_module * mod, const char * ct #endif /*! - @} -*/ + * @} + */ #endif /* LIBOPENMPT_H */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp index d6c6926e5..55097f830 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp @@ -150,7 +150,7 @@ public: exception & operator = ( const exception & other ) noexcept; exception & operator = ( exception && other ) noexcept; virtual ~exception() noexcept; - virtual const char * what() const noexcept; + const char * what() const noexcept override; }; // class exception #if defined(_MSC_VER) #pragma warning(pop) @@ -724,7 +724,9 @@ public: \param key Metadata item key to query. Use openmpt::module::get_metadata_keys to check for available keys. Possible keys are: - type: Module format extension (e.g. it) - - type_long: Tracker name associated with the module format (e.g. Impulse Tracker) + - type_long: Format name associated with the module format (e.g. Impulse Tracker) + - originaltype: Module format extension (e.g. it) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) + - originaltype_long: Format name associated with the module format (e.g. Impulse Tracker) of the original module in case the actual type is a converted format (e.g. mo3 or gdm) - container: Container format the module file is embedded in, if any (e.g. umx) - container_long: Full container name if the module is embedded in a container (e.g. Unreal Music) - tracker: Tracker that was (most likely) used to save the module file, if known @@ -966,9 +968,14 @@ public: - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - seek.sync_samples: Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row. - subsong: The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong. + - play.at_end: Chooses the behaviour when the end of song is reached: + - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. + - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. + - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt::module::read. Supported values are: - 0: No dithering. - 1: Default mode. Chosen by OpenMPT code, might change. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp index a39f44c50..845e643bf 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp @@ -50,10 +50,7 @@ public: logfunc_logger( openmpt_log_func func, void * user ) : m_logfunc(func), m_user(user) { return; } - virtual ~logfunc_logger() { - return; - } - virtual void log( const std::string & message ) const { + void log( const std::string & message ) const override { if ( m_logfunc ) { m_logfunc( message.c_str(), m_user ); } else { @@ -71,9 +68,8 @@ public: { return; } - virtual ~invalid_module_pointer() throw() { - return; - } + invalid_module_pointer(const invalid_module_pointer&) = default; + virtual ~invalid_module_pointer() noexcept = default; }; class argument_null_pointer : public openmpt::exception { @@ -83,9 +79,8 @@ public: { return; } - virtual ~argument_null_pointer() throw() { - return; - } + argument_null_pointer(const argument_null_pointer&) = default; + virtual ~argument_null_pointer() noexcept = default; }; } // namespace interface @@ -100,6 +95,12 @@ static std::string format_exception( const char * const function ) { err += "ERROR: "; const char * what = e.what(); err += what ? what : ""; + } catch ( const std::bad_alloc & e ) { + err += function; + err += ": "; + err += "OUT OF MEMORY: "; + const char * what = e.what(); + err += what ? what : ""; } catch ( const std::exception & e ) { err += function; err += ": "; diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp index a6ad8a7d3..e2eac37fc 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp @@ -152,10 +152,11 @@ int probe_file_header( std::uint64_t flags, std::istream & stream ) { #pragma warning(disable:4702) // unreachable code #endif // _MSC_VER -module::module( const module & ) { +module::module( const module & ) : impl(nullptr) { throw exception("openmpt::module is non-copyable"); } +// cppcheck-suppress operatorEqVarError void module::operator = ( const module & ) { throw exception("openmpt::module is non-copyable"); } @@ -417,9 +418,10 @@ module_ext::~module_ext() { #pragma warning(push) #pragma warning(disable:4702) // unreachable code #endif // _MSC_VER -module_ext::module_ext( const module_ext & other ) : module(other) { +module_ext::module_ext( const module_ext & other ) : module(other), ext_impl(nullptr) { throw std::runtime_error("openmpt::module_ext is non-copyable"); } +// cppcheck-suppress operatorEqVarError void module_ext::operator = ( const module_ext & ) { throw std::runtime_error("openmpt::module_ext is non-copyable"); } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp index 6051266c9..9301579c2 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp @@ -135,7 +135,7 @@ namespace openmpt { if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid tempo factor"); } - m_sndFile->m_nTempoFactor = Util::Round( 65536.0 / factor ); + m_sndFile->m_nTempoFactor = mpt::saturate_round( 65536.0 / factor ); m_sndFile->RecalculateSamplesPerTick(); } @@ -147,7 +147,7 @@ namespace openmpt { if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid pitch factor"); } - m_sndFile->m_nFreqFactor = Util::Round( 65536.0 * factor ); + m_sndFile->m_nFreqFactor = mpt::saturate_round( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); } @@ -159,7 +159,7 @@ namespace openmpt { if ( volume < 0.0 || volume > 1.0 ) { throw openmpt::exception("invalid global volume"); } - m_sndFile->m_PlayState.m_nGlobalVolume = Util::Round( volume * MAX_GLOBAL_VOLUME ); + m_sndFile->m_PlayState.m_nGlobalVolume = mpt::saturate_round( volume * MAX_GLOBAL_VOLUME ); } double module_ext_impl::get_global_volume( ) const { @@ -173,7 +173,7 @@ namespace openmpt { if ( volume < 0.0 || volume > 1.0 ) { throw openmpt::exception("invalid global volume"); } - m_sndFile->m_PlayState.Chn[channel].nGlobalVol = Util::Round(volume * 64.0); + m_sndFile->m_PlayState.Chn[channel].nGlobalVol = mpt::saturate_round(volume * 64.0); } double module_ext_impl::get_channel_volume( std::int32_t channel ) const { @@ -269,11 +269,11 @@ namespace openmpt { chn.nMasterChn = 0; // remove NNA association chn.nNewNote = chn.nLastNote = static_cast(note); chn.ResetEnvelopes(); - m_sndFile->InstrumentChange(&chn, instrument + 1); + m_sndFile->InstrumentChange(chn, instrument + 1); chn.nFadeOutVol = 0x10000; - m_sndFile->NoteChange(&chn, note, false, true, true); - chn.nPan = Util::Round( Clamp( panning * 128.0, -128.0, 128.0 ) + 128.0 ); - chn.nVolume = Util::Round( Clamp( volume * 256.0, 0.0, 256.0 ) ); + m_sndFile->NoteChange(chn, note, false, true, true); + chn.nPan = mpt::saturate_round( Clamp( panning * 128.0, -128.0, 128.0 ) + 128.0 ); + chn.nVolume = mpt::saturate_round( Clamp( volume * 256.0, 0.0, 256.0 ) ); // Remove channel from list of mixed channels to fix https://bugs.openmpt.org/view.php?id=209 // This is required because a previous note on the same channel might have just stopped playing, diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp index c407ddf91..68fad85cf 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp @@ -61,43 +61,43 @@ public: // pattern_vis - virtual effect_type get_pattern_row_channel_volume_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const; + effect_type get_pattern_row_channel_volume_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const override; - virtual effect_type get_pattern_row_channel_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const; + effect_type get_pattern_row_channel_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const override; // interactive - virtual void set_current_speed( std::int32_t speed ); + void set_current_speed( std::int32_t speed ) override; - virtual void set_current_tempo( std::int32_t tempo ); + void set_current_tempo( std::int32_t tempo ) override; - virtual void set_tempo_factor( double factor ); + void set_tempo_factor( double factor ) override; - virtual double get_tempo_factor( ) const; + double get_tempo_factor( ) const override; - virtual void set_pitch_factor( double factor ); + void set_pitch_factor( double factor ) override; - virtual double get_pitch_factor( ) const; + double get_pitch_factor( ) const override; - virtual void set_global_volume( double volume ); + void set_global_volume( double volume ) override; - virtual double get_global_volume( ) const; + double get_global_volume( ) const override; - virtual void set_channel_volume( std::int32_t channel, double volume ); + void set_channel_volume( std::int32_t channel, double volume ) override; - virtual double get_channel_volume( std::int32_t channel ) const; + double get_channel_volume( std::int32_t channel ) const override; - virtual void set_channel_mute_status( std::int32_t channel, bool mute ); + void set_channel_mute_status( std::int32_t channel, bool mute ) override; - virtual bool get_channel_mute_status( std::int32_t channel ) const; + bool get_channel_mute_status( std::int32_t channel ) const override; - virtual void set_instrument_mute_status( std::int32_t instrument, bool mute ); + void set_instrument_mute_status( std::int32_t instrument, bool mute ) override; - virtual bool get_instrument_mute_status( std::int32_t instrument ) const; + bool get_instrument_mute_status( std::int32_t instrument ) const override; - virtual std::int32_t play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ); + std::int32_t play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ) override; - virtual void stop_note( std::int32_t channel ); + void stop_note( std::int32_t channel ) override; /* add stuff here */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp index 1e953f77c..6ade376e2 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp @@ -36,12 +36,14 @@ OPENMPT_NAMESPACE_BEGIN +#if !defined(MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS) + #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if defined(_WIN32_WINNT) #if (_WIN32_WINNT < 0x0602) #if MPT_COMPILER_MSVC #pragma message("Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602.") -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG #warning "Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602." #else // There is no portable way to display a warning. @@ -58,13 +60,6 @@ static int Warning_libopenmpt_for_WinRT_is_built_with_reduced_functionality_Plea #else #pragma comment(lib, "rpcrt4.lib") #endif -#if defined(MPT_WITH_MEDIAFOUNDATION) -#pragma comment(lib, "mf.lib") -#pragma comment(lib, "mfplat.lib") -#pragma comment(lib, "mfreadwrite.lib") -#pragma comment(lib, "mfuuid.lib") // static lib -#pragma comment(lib, "propsys.lib") -#endif // MPT_WITH_MEDIAFOUNDATION #ifndef NO_DMO #pragma comment(lib, "dmoguids.lib") #pragma comment(lib, "strmiids.lib") @@ -74,7 +69,7 @@ static int Warning_libopenmpt_for_WinRT_is_built_with_reduced_functionality_Plea #if MPT_PLATFORM_MULTITHREADED && MPT_MUTEX_NONE #if MPT_COMPILER_MSVC #pragma message("Warning: libopenmpt built in non thread-safe mode because mutexes are not supported by the C++ standard library available.") -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG #warning "Warning: libopenmpt built in non thread-safe mode because mutexes are not supported by the C++ standard library available." #else // There is no portable way to display a warning. @@ -83,33 +78,33 @@ static int Warning_libopenmpt_built_in_non_thread_safe_mode_because_mutexes_are_ #endif #endif // MPT_MUTEX_NONE -#if 0 -#if (!MPT_COMPILER_HAS_CONSTEXPR11) +#if (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(MPT_WITH_MINGWSTDTHREADS) #if MPT_COMPILER_MSVC -#pragma message("Warning: libopenmpt is built with a compiler not supporting constexpr. Referring to libopenmpt from global initializers will result in undefined behaviour.") -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2 -#warning "Warning: libopenmpt is built with a compiler not supporting constexpr. Referring to libopenmpt from global initializers will result in undefined behaviour.") +#pragma message("Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads.") +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#warning "Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads." #else // There is no portable way to display a warning. // Try to provoke a warning with an unused variable. -static int Warning_libopenmpt_is_built_with_a_compiler_not_supporting_constexpr_Referring_to_libopenmpt_from_global_initializers_will_result_in_undefined_behaviour; +static int Warning_Building_libopenmpt_with_MinGW_w64_without_std_thread_support_is_not_recommended_ans_is_deprecated_Please_use_MinGW_w64_with_posix_threading_model_as_opposed_to_win32_threading_model_or_build_with_mingw_std_threads; #endif -#endif // "MPT_COMPILER_HAS_CONSTEXPR11 -#endif +#endif // MINGW + +#endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS #if defined(MPT_ASSERT_HANDLER_NEEDED) && !defined(ENABLE_TESTS) -MPT_NOINLINE void AssertHandler(const char *file, int line, const char *function, const char *expr, const char *msg) +MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg) { if(msg) { - mpt::log::Logger().SendLogMessage(mpt::log::Context(file, line, function), LogError, "ASSERT", - MPT_USTRING("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + MPT_USTRING(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + MPT_USTRING(")") + mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_(")") ); } else { - mpt::log::Logger().SendLogMessage(mpt::log::Context(file, line, function), LogError, "ASSERT", - MPT_USTRING("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) + mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) @@ -132,12 +127,12 @@ std::uint32_t get_library_version() { } std::uint32_t get_core_version() { - return MptVersion::num; + return Version::Current().GetRawVersion(); } static std::string get_library_version_string() { std::string str; - const MptVersion::SourceInfo sourceInfo = MptVersion::GetSourceInfo(); + const SourceInfo sourceInfo = SourceInfo::Current(); str += mpt::fmt::val(OPENMPT_API_VERSION_MAJOR); str += "."; str += mpt::fmt::val(OPENMPT_API_VERSION_MINOR); @@ -147,15 +142,15 @@ static std::string get_library_version_string() { str += OPENMPT_API_VERSION_PREREL; } std::vector fields; - if ( sourceInfo.Revision ) { - fields.push_back( "r" + mpt::fmt::val( sourceInfo.Revision ) ); + if ( sourceInfo.Revision() ) { + fields.push_back( "r" + mpt::fmt::val( sourceInfo.Revision() ) ); } - if ( sourceInfo.IsDirty ) { + if ( sourceInfo.IsDirty() ) { fields.push_back( "modified" ); - } else if ( sourceInfo.HasMixedRevisions ) { + } else if ( sourceInfo.HasMixedRevisions() ) { fields.push_back( "mixed" ); } - if ( sourceInfo.IsPackage ) { + if ( sourceInfo.IsPackage() ) { fields.push_back( "pkg" ); } if ( !fields.empty() ) { @@ -166,56 +161,56 @@ static std::string get_library_version_string() { } static std::string get_library_features_string() { - return mpt::String::Trim(MptVersion::GetBuildFeaturesString()); + return mpt::ToCharset(mpt::CharsetUTF8, mpt::String::Trim(Build::GetBuildFeaturesString())); } static std::string get_core_version_string() { - return MptVersion::GetVersionStringExtended(); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetVersionStringExtended()); } static std::string get_source_url_string() { - return MptVersion::GetSourceInfo().GetUrlWithRevision(); + return mpt::ToCharset(mpt::CharsetUTF8, SourceInfo::Current().GetUrlWithRevision()); } static std::string get_source_date_string() { - return MptVersion::GetSourceInfo().Date; + return mpt::ToCharset(mpt::CharsetUTF8, SourceInfo::Current().Date()); } static std::string get_source_revision_string() { - const MptVersion::SourceInfo sourceInfo = MptVersion::GetSourceInfo(); - return sourceInfo.Revision ? mpt::fmt::val(sourceInfo.Revision) : std::string(); + const SourceInfo sourceInfo = SourceInfo::Current(); + return sourceInfo.Revision() ? mpt::fmt::val(sourceInfo.Revision()) : std::string(); } static std::string get_build_string() { - return MptVersion::GetBuildDateString(); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetBuildDateString()); } static std::string get_build_compiler_string() { - return MptVersion::GetBuildCompilerString(); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetBuildCompilerString()); } static std::string get_credits_string() { - return mpt::ToCharset(mpt::CharsetUTF8, MptVersion::GetFullCreditsString()); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetFullCreditsString()); } static std::string get_contact_string() { - return mpt::ToCharset(mpt::CharsetUTF8, MPT_USTRING("Forum: ") + MptVersion::GetURL("forum")); + return mpt::ToCharset(mpt::CharsetUTF8, U_("Forum: ") + Build::GetURL(Build::Url::Forum)); } static std::string get_license_string() { - return mpt::ToCharset(mpt::CharsetUTF8, MptVersion::GetLicenseString()); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetLicenseString()); } static std::string get_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, MptVersion::GetURL("website")); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Website)); } static std::string get_support_forum_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, MptVersion::GetURL("forum")); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Forum)); } static std::string get_bugtracker_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, MptVersion::GetURL("bugtracker")); + return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Bugtracker)); } std::string get_string( const std::string & key ) { @@ -244,11 +239,11 @@ std::string get_string( const std::string & key ) { } else if ( key == "source_revision" ) { return get_source_revision_string(); } else if ( key == "source_is_modified" ) { - return MptVersion::GetSourceInfo().IsDirty ? "1" : "0"; + return SourceInfo::Current().IsDirty() ? "1" : "0"; } else if ( key == "source_has_mixed_revision" ) { - return MptVersion::GetSourceInfo().HasMixedRevisions ? "1" : "0"; + return SourceInfo::Current().HasMixedRevisions() ? "1" : "0"; } else if ( key == "source_is_package" ) { - return MptVersion::GetSourceInfo().IsPackage ? "1" : "0"; + return SourceInfo::Current().IsPackage() ? "1" : "0"; } else if ( key == "build" ) { return get_build_string(); } else if ( key == "build_compiler" ) { @@ -298,12 +293,9 @@ public: log_forwarder( log_interface & dest ) : destination(dest) { return; } - virtual ~log_forwarder() { - return; - } private: - void AddToLog( LogLevel level, const mpt::ustring & text ) const { - destination.log( mpt::ToCharset( mpt::CharsetUTF8, LogLevelToString( level ) + MPT_USTRING(": ") + text ) ); + void AddToLog( LogLevel level, const mpt::ustring & text ) const override { + destination.log( mpt::ToCharset( mpt::CharsetUTF8, LogLevelToString( level ) + U_(": ") + text ) ); } }; // class log_forwarder @@ -313,7 +305,7 @@ private: public: std::vector > GetMessages() const; private: - void AddToLog( LogLevel level, const mpt::ustring & text ) const; + void AddToLog( LogLevel level, const mpt::ustring & text ) const override; }; // class loader_log std::vector > loader_log::GetMessages() const { @@ -340,13 +332,13 @@ module_impl::subsong_data::subsong_data( double duration, std::int32_t start_row } static ResamplingMode filterlength_to_resamplingmode(std::int32_t length) { - ResamplingMode result = SRCMODE_POLYPHASE; + ResamplingMode result = SRCMODE_SINC8LP; if ( length == 0 ) { - result = SRCMODE_POLYPHASE; + result = SRCMODE_SINC8LP; } else if ( length >= 8 ) { - result = SRCMODE_POLYPHASE; + result = SRCMODE_SINC8LP; } else if ( length >= 3 ) { - result = SRCMODE_SPLINE; + result = SRCMODE_CUBIC; } else if ( length >= 2 ) { result = SRCMODE_LINEAR; } else if ( length >= 1 ) { @@ -364,11 +356,11 @@ static std::int32_t resamplingmode_to_filterlength(ResamplingMode mode) { case SRCMODE_LINEAR: return 2; break; - case SRCMODE_SPLINE: + case SRCMODE_CUBIC: return 4; break; - case SRCMODE_POLYPHASE: - case SRCMODE_FIRFILTER: + case SRCMODE_SINC8: + case SRCMODE_SINC8LP: case SRCMODE_DEFAULT: return 8; default: @@ -435,8 +427,8 @@ module_impl::subsongs_type module_impl::get_subsongs() const { } for ( SEQUENCEINDEX seq = 0; seq < m_sndFile->Order.GetNumSequences(); ++seq ) { const std::vector lengths = m_sndFile->GetLength( eNoAdjust, GetLengthTarget( true ).StartPos( seq, 0, 0 ) ); - for ( std::vector::const_iterator l = lengths.begin(); l != lengths.end(); ++l ) { - subsongs.push_back( subsong_data( l->duration, l->startRow, l->startOrder, seq ) ); + for ( const auto & l : lengths ) { + subsongs.push_back( subsong_data( l.duration, l.startRow, l.startOrder, seq ) ); } } return subsongs; @@ -451,20 +443,21 @@ void module_impl::ctor( const std::map< std::string, std::string > & ctls ) { m_sndFile = mpt::make_unique(); m_loaded = false; m_mixer_initialized = false; - m_Dither = mpt::make_unique(mpt::global_prng()); + m_Dither = mpt::make_unique( mpt::global_prng() ); m_LogForwarder = mpt::make_unique( *m_Log ); m_sndFile->SetCustomLog( m_LogForwarder.get() ); m_current_subsong = 0; m_currentPositionSeconds = 0.0; m_Gain = 1.0f; + m_ctl_play_at_end = song_end_action::fadeout_song; m_ctl_load_skip_samples = false; m_ctl_load_skip_patterns = false; m_ctl_load_skip_plugins = false; m_ctl_load_skip_subsongs_init = false; m_ctl_seek_sync_samples = false; // init member variables that correspond to ctls - for ( std::map< std::string, std::string >::const_iterator i = ctls.begin(); i != ctls.end(); ++i ) { - ctl_set( i->first, i->second, false ); + for ( const auto & ctl : ctls ) { + ctl_set( ctl.first, ctl.second, false ); } } void module_impl::load( const FileReader & file, const std::map< std::string, std::string > & ctls ) { @@ -491,13 +484,13 @@ void module_impl::load( const FileReader & file, const std::map< std::string, st } m_sndFile->SetCustomLog( m_LogForwarder.get() ); std::vector > loaderMessages = loaderlog.GetMessages(); - for ( std::vector >::iterator i = loaderMessages.begin(); i != loaderMessages.end(); ++i ) { - PushToCSoundFileLog( i->first, i->second ); - m_loaderMessages.push_back( mpt::ToCharset( mpt::CharsetUTF8, LogLevelToString( i->first ) ) + std::string(": ") + i->second ); + for ( const auto & msg : loaderMessages ) { + PushToCSoundFileLog( msg.first, msg.second ); + m_loaderMessages.push_back( mpt::ToCharset( mpt::CharsetUTF8, LogLevelToString( msg.first ) ) + std::string(": ") + msg.second ); } // init CSoundFile state that corresponds to ctls - for ( std::map< std::string, std::string >::const_iterator i = ctls.begin(); i != ctls.end(); ++i ) { - ctl_set( i->first, i->second, false ); + for ( const auto & ctl : ctls ) { + ctl_set( ctl.first, ctl.second, false ); } } bool module_impl::is_loaded() const { @@ -505,6 +498,7 @@ bool module_impl::is_loaded() const { } std::size_t module_impl::read_wrapper( std::size_t count, std::int16_t * left, std::int16_t * right, std::int16_t * rear_left, std::int16_t * rear_right ) { m_sndFile->ResetMixStat(); + m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; while ( count > 0 ) { std::int16_t * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; @@ -519,10 +513,15 @@ std::size_t module_impl::read_wrapper( std::size_t count, std::int16_t * left, s count -= count_chunk; count_read += count_chunk; } + if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { + // This is the song end, but allow the song or loop to restart on the next call + m_sndFile->m_SongFlags.reset(SONG_ENDREACHED); + } return count_read; } std::size_t module_impl::read_wrapper( std::size_t count, float * left, float * right, float * rear_left, float * rear_right ) { m_sndFile->ResetMixStat(); + m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; while ( count > 0 ) { float * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; @@ -537,10 +536,15 @@ std::size_t module_impl::read_wrapper( std::size_t count, float * left, float * count -= count_chunk; count_read += count_chunk; } + if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { + // This is the song end, but allow the song or loop to restart on the next call + m_sndFile->m_SongFlags.reset(SONG_ENDREACHED); + } return count_read; } std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_t channels, std::int16_t * interleaved ) { m_sndFile->ResetMixStat(); + m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; while ( count > 0 ) { AudioReadTargetGainBuffer target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); @@ -554,10 +558,15 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_ count -= count_chunk; count_read += count_chunk; } + if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { + // This is the song end, but allow the song or loop to restart on the next call + m_sndFile->m_SongFlags.reset(SONG_ENDREACHED); + } return count_read; } std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_t channels, float * interleaved ) { m_sndFile->ResetMixStat(); + m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; while ( count > 0 ) { AudioReadTargetGainBuffer target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); @@ -571,6 +580,10 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_ count -= count_chunk; count_read += count_chunk; } + if ( count_read == 0 && m_ctl_play_at_end == song_end_action::continue_song ) { + // This is the song end, but allow the song or loop to restart on the next call + m_sndFile->m_SongFlags.reset(SONG_ENDREACHED); + } return count_read; } @@ -1004,8 +1017,8 @@ double module_impl::get_duration_seconds() const { if ( m_current_subsong == all_subsongs ) { // Play all subsongs consecutively. double total_duration = 0.0; - for ( std::size_t i = 0; i < subsongs.size(); ++i ) { - total_duration += subsongs[i].duration; + for ( const auto & subsong : subsongs ) { + total_duration += subsong.duration; } return total_duration; } @@ -1083,31 +1096,38 @@ double module_impl::set_position_order_row( std::int32_t order, std::int32_t row return m_currentPositionSeconds; } std::vector module_impl::get_metadata_keys() const { - std::vector retval; - retval.push_back("type"); - retval.push_back("type_long"); - retval.push_back("container"); - retval.push_back("container_long"); - retval.push_back("tracker"); - retval.push_back("artist"); - retval.push_back("title"); - retval.push_back("date"); - retval.push_back("message"); - retval.push_back("message_raw"); - retval.push_back("warnings"); - return retval; + return + { + "type", + "type_long", + "originaltype", + "originaltype_long", + "container", + "container_long", + "tracker", + "artist", + "title", + "date", + "message", + "message_raw", + "warnings", + }; } std::string module_impl::get_metadata( const std::string & key ) const { if ( key == std::string("type") ) { - return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModTypeToString( m_sndFile->GetType() ) ); + return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.type ); } else if ( key == std::string("type_long") ) { - return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModTypeToTracker( m_sndFile->GetType() ) ); + return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.formatName ); + } else if ( key == std::string("originaltype") ) { + return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.originalType ); + } else if ( key == std::string("originaltype_long") ) { + return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.originalFormatName ); } else if ( key == std::string("container") ) { return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModContainerTypeToString( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("container_long") ) { return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModContainerTypeToTracker( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("tracker") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_madeWithTracker ); + return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.madeWithTracker ); } else if ( key == std::string("artist") ) { return mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->m_songArtist ); } else if ( key == std::string("title") ) { @@ -1116,7 +1136,7 @@ std::string module_impl::get_metadata( const std::string & key ) const { if ( m_sndFile->GetFileHistory().empty() ) { return std::string(); } - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->GetFileHistory()[m_sndFile->GetFileHistory().size() - 1].AsISO8601() ); + return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->GetFileHistory().back().AsISO8601() ); } else if ( key == std::string("message") ) { std::string retval = m_sndFile->m_songMessage.GetFormatted( SongMessage::leLF ); if ( retval.empty() ) { @@ -1156,13 +1176,13 @@ std::string module_impl::get_metadata( const std::string & key ) const { } else if ( key == std::string("warnings") ) { std::string retval; bool first = true; - for ( std::vector::const_iterator i = m_loaderMessages.begin(); i != m_loaderMessages.end(); ++i ) { + for ( const auto & msg : m_loaderMessages ) { if ( !first ) { retval += "\n"; } else { first = false; } - retval += *i; + retval += msg; } return retval; } @@ -1254,8 +1274,8 @@ std::vector module_impl::get_subsong_names() const { std::vector retval; std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; - for ( std::size_t i = 0; i < subsongs.size(); ++i ) { - retval.push_back( mod_string_to_utf8( m_sndFile->Order( static_cast( subsongs[i].sequence ) ).GetName() ) ); + for ( const auto & subsong : subsongs ) { + retval.push_back( mod_string_to_utf8( m_sndFile->Order( static_cast( subsong.sequence ) ).GetName() ) ); } return retval; } @@ -1268,7 +1288,9 @@ std::vector module_impl::get_channel_names() const { } std::vector module_impl::get_order_names() const { std::vector retval; - for ( ORDERINDEX i = 0; i < m_sndFile->Order().GetLengthTailTrimmed(); ++i ) { + ORDERINDEX num_orders = m_sndFile->Order().GetLengthTailTrimmed(); + retval.reserve( num_orders ); + for ( ORDERINDEX i = 0; i < num_orders; ++i ) { PATTERNINDEX pat = m_sndFile->Order()[i]; if ( m_sndFile->Patterns.IsValidIndex( pat ) ) { retval.push_back( mod_string_to_utf8( m_sndFile->Patterns[ m_sndFile->Order()[i] ].GetName() ) ); @@ -1286,6 +1308,7 @@ std::vector module_impl::get_order_names() const { } std::vector module_impl::get_pattern_names() const { std::vector retval; + retval.reserve( m_sndFile->Patterns.GetNumPatterns() ); for ( PATTERNINDEX i = 0; i < m_sndFile->Patterns.GetNumPatterns(); ++i ) { retval.push_back( mod_string_to_utf8( m_sndFile->Patterns[i].GetName() ) ); } @@ -1293,6 +1316,7 @@ std::vector module_impl::get_pattern_names() const { } std::vector module_impl::get_instrument_names() const { std::vector retval; + retval.reserve( m_sndFile->GetNumInstruments() ); for ( INSTRUMENTINDEX i = 1; i <= m_sndFile->GetNumInstruments(); ++i ) { retval.push_back( mod_string_to_utf8( m_sndFile->GetInstrumentName( i ) ) ); } @@ -1300,6 +1324,7 @@ std::vector module_impl::get_instrument_names() const { } std::vector module_impl::get_sample_names() const { std::vector retval; + retval.reserve( m_sndFile->GetNumSamples() ); for ( SAMPLEINDEX i = 1; i <= m_sndFile->GetNumSamples(); ++i ) { retval.push_back( mod_string_to_utf8( m_sndFile->GetSampleName( i ) ) ); } @@ -1379,7 +1404,7 @@ std::pair< std::string, std::string > module_impl::format_and_highlight_pattern_ switch ( cmd ) { case module::command_note: return std::make_pair( - ( cell.IsNote() || cell.IsSpecialNote() ) ? m_sndFile->GetNoteName( cell.note, cell.instr ) : std::string("...") + ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("...") , ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("...") ); @@ -1448,7 +1473,7 @@ std::pair< std::string, std::string > module_impl::format_and_highlight_pattern_ const ModCommand & cell = *pattern.GetpModCommand( static_cast( r ), static_cast( c ) ); text.clear(); high.clear(); - text += ( cell.IsNote() || cell.IsSpecialNote() ) ? m_sndFile->GetNoteName( cell.note, cell.instr ) : std::string("..."); + text += ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("..."); high += ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("..."); if ( ( width == 0 ) || ( width >= 6 ) ) { text += std::string(" "); @@ -1486,18 +1511,21 @@ std::string module_impl::highlight_pattern_row_channel( std::int32_t p, std::int } std::vector module_impl::get_ctls() const { - std::vector retval; - retval.push_back( "load.skip_samples" ); - retval.push_back( "load.skip_patterns" ); - retval.push_back( "load.skip_plugins" ); - retval.push_back( "load.skip_subsongs_init" ); - retval.push_back( "seek.sync_samples" ); - retval.push_back( "subsong" ); - retval.push_back( "play.tempo_factor" ); - retval.push_back( "play.pitch_factor" ); - retval.push_back( "render.resampler.emulate_amiga" ); - retval.push_back( "dither" ); - return retval; + return + { + "load.skip_samples", + "load.skip_patterns", + "load.skip_plugins", + "load.skip_subsongs_init", + "seek.sync_samples", + "subsong", + "play.tempo_factor", + "play.pitch_factor", + "play.at_end", + "render.resampler.emulate_amiga", + "render.opl.volume_factor", + "dither", + }; } std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { @@ -1525,6 +1553,18 @@ std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const return mpt::fmt::val( m_ctl_seek_sync_samples ); } else if ( ctl == "subsong" ) { return mpt::fmt::val( get_selected_subsong() ); + } else if ( ctl == "play.at_end" ) { + switch ( m_ctl_play_at_end ) + { + case song_end_action::fadeout_song: + return "fadeout"; + case song_end_action::continue_song: + return "continue"; + case song_end_action::stop_song: + return "stop"; + default: + return std::string(); + } } else if ( ctl == "play.tempo_factor" ) { if ( !is_loaded() ) { return "1.0"; @@ -1537,6 +1577,8 @@ std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const return mpt::fmt::val( m_sndFile->m_nFreqFactor / 65536.0 ); } else if ( ctl == "render.resampler.emulate_amiga" ) { return mpt::fmt::val( m_sndFile->m_Resampler.m_Settings.emulateAmiga ); + } else if ( ctl == "render.opl.volume_factor" ) { + return mpt::fmt::val( static_cast( m_sndFile->m_OPLVolumeFactor ) / static_cast( m_sndFile->m_OPLVolumeFactorScale ) ); } else if ( ctl == "dither" ) { return mpt::fmt::val( static_cast( m_Dither->GetMode() ) ); } else { @@ -1573,6 +1615,16 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro m_ctl_seek_sync_samples = ConvertStrTo( value ); } else if ( ctl == "subsong" ) { select_subsong( ConvertStrTo( value ) ); + } else if ( ctl == "play.at_end" ) { + if ( value == "fadeout" ) { + m_ctl_play_at_end = song_end_action::fadeout_song; + } else if(value == "continue") { + m_ctl_play_at_end = song_end_action::continue_song; + } else if(value == "stop") { + m_ctl_play_at_end = song_end_action::stop_song; + } else { + throw openmpt::exception("unknown song end action:" + value); + } } else if ( ctl == "play.tempo_factor" ) { if ( !is_loaded() ) { return; @@ -1581,7 +1633,7 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid tempo factor"); } - m_sndFile->m_nTempoFactor = Util::Round( 65536.0 / factor ); + m_sndFile->m_nTempoFactor = mpt::saturate_round( 65536.0 / factor ); m_sndFile->RecalculateSamplesPerTick(); } else if ( ctl == "play.pitch_factor" ) { if ( !is_loaded() ) { @@ -1591,7 +1643,7 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid pitch factor"); } - m_sndFile->m_nFreqFactor = Util::Round( 65536.0 * factor ); + m_sndFile->m_nFreqFactor = mpt::saturate_round( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); } else if ( ctl == "render.resampler.emulate_amiga" ) { CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; @@ -1599,6 +1651,8 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { m_sndFile->SetResamplerSettings( newsettings ); } + } else if ( ctl == "render.opl.volume_factor" ) { + m_sndFile->m_OPLVolumeFactor = mpt::saturate_round( ConvertStrTo( value ) * static_cast( m_sndFile->m_OPLVolumeFactorScale ) ); } else if ( ctl == "dither" ) { int dither = ConvertStrTo( value ); if ( dither < 0 || dither >= NumDitherModes ) { diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp index bec492ba0..299417be3 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp @@ -58,7 +58,7 @@ private: public: std_ostream_log( std::ostream & dst ); virtual ~std_ostream_log(); - virtual void log( const std::string & message ) const; + void log( const std::string & message ) const override; }; // class CSoundFileLog_std_ostream class log_forwarder; @@ -79,8 +79,17 @@ protected: std::int32_t sequence; subsong_data( double duration, std::int32_t start_row, std::int32_t start_order, std::int32_t sequence ); }; // struct subsong_data + typedef std::vector subsongs_type; + + enum class song_end_action { + fadeout_song, + continue_song, + stop_song, + }; + static const std::int32_t all_subsongs = -1; + std::unique_ptr m_Log; std::unique_ptr m_LogForwarder; std::int32_t m_current_subsong; @@ -91,6 +100,7 @@ protected: std::unique_ptr m_Dither; subsongs_type m_subsongs; float m_Gain; + song_end_action m_ctl_play_at_end; bool m_ctl_load_skip_samples; bool m_ctl_load_skip_patterns; bool m_ctl_load_skip_plugins; diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c index 0255ee15a..d5b469552 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c @@ -28,20 +28,21 @@ #include #include -/* define to emulate 0.8.7 API/ABI instead of 0.8.8 API/ABI */ -/* #define LIBOPENMPT_MODPLUG_0_8_7 */ - +#define MODPLUG_BUILD +#ifdef _MSC_VER +#ifdef MPT_BUILD_MSVC_SHARED +#define DLL_EXPORT +#endif /* MPT_BUILD_MSVC_SHARED */ +#ifdef MPT_BUILD_MSVC_STATIC +#define MODPLUG_STATIC +#endif /* MPT_BUILD_MSVC_STATIC */ +#endif /* _MSC_VER */ #ifdef _MSC_VER -/* msvc errors when seeing dllexport declarations after prototypes have been declared in modplug.h */ #define LIBOPENMPT_MODPLUG_API #else /* !_MSC_VER */ #define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT #endif /* _MSC_VER */ -#ifdef LIBOPENMPT_MODPLUG_0_8_7 -#include "libmodplug/modplug_0.8.7.h" -#else #include "libmodplug/modplug.h" -#endif /* from libmodplug/sndfile.h */ /* header is not c clean */ @@ -94,10 +95,8 @@ static ModPlug_Settings globalsettings = { 16, 44100, MODPLUG_RESAMPLE_LINEAR, -#ifndef LIBOPENMPT_MODPLUG_0_8_7 128, 256, -#endif 0, 0, 0, @@ -169,9 +168,7 @@ LIBOPENMPT_MODPLUG_API ModPlugFile* ModPlug_Load(const void* data, int size) strcpy(file->message,""); } } -#ifndef LIBOPENMPT_MODPLUG_0_8_7 openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,file->settings.mStereoSeparation*100/128); -#endif openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,modplugresamplingmode_to_filterlength(file->settings.mResamplingMode)); return file; } @@ -622,74 +619,4 @@ LIBOPENMPT_MODPLUG_API char ModPlug_ExportIT(ModPlugFile* file, const char* file return 0; } -#ifdef _MSC_VER -#ifdef _M_IX86 -#pragma comment(linker, "/EXPORT:ModPlug_Load=_ModPlug_Load") -#pragma comment(linker, "/EXPORT:ModPlug_Unload=_ModPlug_Unload") -#pragma comment(linker, "/EXPORT:ModPlug_Read=_ModPlug_Read") -#pragma comment(linker, "/EXPORT:ModPlug_GetName=_ModPlug_GetName") -#pragma comment(linker, "/EXPORT:ModPlug_GetLength=_ModPlug_GetLength") -#pragma comment(linker, "/EXPORT:ModPlug_Seek=_ModPlug_Seek") -#pragma comment(linker, "/EXPORT:ModPlug_GetSettings=_ModPlug_GetSettings") -#pragma comment(linker, "/EXPORT:ModPlug_SetSettings=_ModPlug_SetSettings") -#pragma comment(linker, "/EXPORT:ModPlug_GetMasterVolume=_ModPlug_GetMasterVolume") -#pragma comment(linker, "/EXPORT:ModPlug_SetMasterVolume=_ModPlug_SetMasterVolume") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentSpeed=_ModPlug_GetCurrentSpeed") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentTempo=_ModPlug_GetCurrentTempo") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentOrder=_ModPlug_GetCurrentOrder") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentPattern=_ModPlug_GetCurrentPattern") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentRow=_ModPlug_GetCurrentRow") -#pragma comment(linker, "/EXPORT:ModPlug_GetPlayingChannels=_ModPlug_GetPlayingChannels") -#pragma comment(linker, "/EXPORT:ModPlug_SeekOrder=_ModPlug_SeekOrder") -#pragma comment(linker, "/EXPORT:ModPlug_GetModuleType=_ModPlug_GetModuleType") -#pragma comment(linker, "/EXPORT:ModPlug_GetMessage=_ModPlug_GetMessage") -#pragma comment(linker, "/EXPORT:ModPlug_NumInstruments=_ModPlug_NumInstruments") -#pragma comment(linker, "/EXPORT:ModPlug_NumSamples=_ModPlug_NumSamples") -#pragma comment(linker, "/EXPORT:ModPlug_NumPatterns=_ModPlug_NumPatterns") -#pragma comment(linker, "/EXPORT:ModPlug_NumChannels=_ModPlug_NumChannels") -#pragma comment(linker, "/EXPORT:ModPlug_SampleName=_ModPlug_SampleName") -#pragma comment(linker, "/EXPORT:ModPlug_InstrumentName=_ModPlug_InstrumentName") -#pragma comment(linker, "/EXPORT:ModPlug_GetPattern=_ModPlug_GetPattern") -#pragma comment(linker, "/EXPORT:ModPlug_InitMixerCallback=_ModPlug_InitMixerCallback") -#pragma comment(linker, "/EXPORT:ModPlug_UnloadMixerCallback=_ModPlug_UnloadMixerCallback") -#pragma comment(linker, "/EXPORT:ModPlug_ExportS3M=_ModPlug_ExportS3M") -#pragma comment(linker, "/EXPORT:ModPlug_ExportXM=_ModPlug_ExportXM") -#pragma comment(linker, "/EXPORT:ModPlug_ExportMOD=_ModPlug_ExportMOD") -#pragma comment(linker, "/EXPORT:ModPlug_ExportIT=_ModPlug_ExportIT") -#else /* !_M_IX86 */ -#pragma comment(linker, "/EXPORT:ModPlug_Load") -#pragma comment(linker, "/EXPORT:ModPlug_Unload") -#pragma comment(linker, "/EXPORT:ModPlug_Read") -#pragma comment(linker, "/EXPORT:ModPlug_GetName") -#pragma comment(linker, "/EXPORT:ModPlug_GetLength") -#pragma comment(linker, "/EXPORT:ModPlug_Seek") -#pragma comment(linker, "/EXPORT:ModPlug_GetSettings") -#pragma comment(linker, "/EXPORT:ModPlug_SetSettings") -#pragma comment(linker, "/EXPORT:ModPlug_GetMasterVolume") -#pragma comment(linker, "/EXPORT:ModPlug_SetMasterVolume") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentSpeed") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentTempo") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentOrder") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentPattern") -#pragma comment(linker, "/EXPORT:ModPlug_GetCurrentRow") -#pragma comment(linker, "/EXPORT:ModPlug_GetPlayingChannels") -#pragma comment(linker, "/EXPORT:ModPlug_SeekOrder") -#pragma comment(linker, "/EXPORT:ModPlug_GetModuleType") -#pragma comment(linker, "/EXPORT:ModPlug_GetMessage") -#pragma comment(linker, "/EXPORT:ModPlug_NumInstruments") -#pragma comment(linker, "/EXPORT:ModPlug_NumSamples") -#pragma comment(linker, "/EXPORT:ModPlug_NumPatterns") -#pragma comment(linker, "/EXPORT:ModPlug_NumChannels") -#pragma comment(linker, "/EXPORT:ModPlug_SampleName") -#pragma comment(linker, "/EXPORT:ModPlug_InstrumentName") -#pragma comment(linker, "/EXPORT:ModPlug_GetPattern") -#pragma comment(linker, "/EXPORT:ModPlug_InitMixerCallback") -#pragma comment(linker, "/EXPORT:ModPlug_UnloadMixerCallback") -#pragma comment(linker, "/EXPORT:ModPlug_ExportS3M") -#pragma comment(linker, "/EXPORT:ModPlug_ExportXM") -#pragma comment(linker, "/EXPORT:ModPlug_ExportMOD") -#pragma comment(linker, "/EXPORT:ModPlug_ExportIT") -#endif /* _M_IX86 */ -#endif /* _MSC_VER */ - #endif /* NO_LIBMODPLUG */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp index f703e6174..a1d36e5a5 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp @@ -41,34 +41,33 @@ Metadata and other state is not provided or updated. #include #include -#include #include #include #include #include +#define MODPLUG_BUILD +#ifdef _MSC_VER +/* libmodplug C++ header is broken for MSVC DLL builds */ +#define MODPLUG_STATIC +#endif /* _MSC_VER */ #ifdef _MSC_VER -/* msvc errors when seeing dllexport declarations after prototypes have been declared in modplug.h */ #define LIBOPENMPT_MODPLUG_API #else /* !_MSC_VER */ #define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT #endif /* _MSC_VER */ +class LIBOPENMPT_MODPLUG_API CSoundFile; +#include "libmodplug/stdafx.h" +#include "libmodplug/sndfile.h" namespace { - template void Clear( T & x ) { std::memset( &x, 0, sizeof(T) ); } - } -class LIBOPENMPT_MODPLUG_API CSoundFile; - -#include "libmodplug/stdafx.h" -#include "libmodplug/sndfile.h" - //#define mpcpplog() fprintf(stderr, "%s %i\n", __func__, __LINE__) #define mpcpplog() do{}while(0) diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp index 984f1333f..780d494ef 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp @@ -32,13 +32,7 @@ class CSettingsApp : public CWinApp { public: - CSettingsApp() { - return; - } - -public: - - virtual BOOL InitInstance() { + BOOL InitInstance() override { if ( !CWinApp::InitInstance() ) { return FALSE; @@ -47,18 +41,13 @@ public: return TRUE; } - virtual int ExitInstance() { + int ExitInstance() override { DllMainDetach(); return CWinApp::ExitInstance(); } - DECLARE_MESSAGE_MAP() - }; -BEGIN_MESSAGE_MAP(CSettingsApp, CWinApp) -END_MESSAGE_MAP() - CSettingsApp theApp; @@ -94,7 +83,7 @@ public: protected: - virtual void DoDataExchange( CDataExchange * pDX ) + void DoDataExchange( CDataExchange * pDX ) override { CDialog::DoDataExchange( pDX ); DDX_Control( pDX, IDC_COMBO_SAMPLERATE, m_ComboBoxSamplerate ); @@ -107,19 +96,20 @@ protected: DDX_Control( pDX, IDC_COMBO_RAMPING, m_ComboBoxRamping ); } - afx_msg BOOL OnInitDialog() { + afx_msg BOOL OnInitDialog() override { if ( !CDialog::OnInitDialog() ) { return false; } SetWindowText( m_Title ); + EnableToolTips(); bool selected = false; selected = false; if ( !s->no_default_format ) { - m_ComboBoxSamplerate.SetItemData( m_ComboBoxSamplerate.AddString( L"default" ), 0 ); + m_ComboBoxSamplerate.SetItemData( m_ComboBoxSamplerate.AddString( L"Default" ), 0 ); } m_ComboBoxSamplerate.SetItemData( m_ComboBoxSamplerate.AddString( L"6000" ), 6000 ); m_ComboBoxSamplerate.SetItemData( m_ComboBoxSamplerate.AddString( L"8000" ), 8000 ); @@ -132,7 +122,7 @@ protected: m_ComboBoxSamplerate.SetItemData( m_ComboBoxSamplerate.AddString( L"88200" ), 88200 ); m_ComboBoxSamplerate.SetItemData( m_ComboBoxSamplerate.AddString( L"96000" ), 96000 ); if ( !s->no_default_format && s->samplerate == 0 ) { - m_ComboBoxSamplerate.SelectString( 0, L"default" ); + m_ComboBoxSamplerate.SelectString( 0, L"Default" ); } for ( int index = 0; index < m_ComboBoxSamplerate.GetCount(); ++index ) { if ( m_ComboBoxSamplerate.GetItemData( index ) == s->samplerate ) { @@ -146,13 +136,13 @@ protected: selected = false; if ( !s->no_default_format ) { - m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"default" ), 0 ); + m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"Default" ), 0 ); } - m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"mono" ), 1 ); - m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"stereo" ), 2 ); - m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"quad" ), 4 ); + m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"Mono" ), 1 ); + m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"Stereo" ), 2 ); + m_ComboBoxChannels.SetItemData( m_ComboBoxChannels.AddString( L"Quad" ), 4 ); if ( !s->no_default_format && s->channels == 0 ) { - m_ComboBoxChannels.SelectString( 0, L"default" ); + m_ComboBoxChannels.SelectString( 0, L"Default" ); } for ( int index = 0; index < m_ComboBoxChannels.GetCount(); ++index ) { if ( m_ComboBoxChannels.GetItemData( index ) == s->channels ) { @@ -161,7 +151,7 @@ protected: } } if ( !selected ) { - m_ComboBoxChannels.SelectString( 0, L"stereo" ); + m_ComboBoxChannels.SelectString( 0, L"Stereo" ); } m_SliderCtrlGain.SetRange( -1200, 1200 ); @@ -171,10 +161,10 @@ protected: m_SliderCtrlGain.SetPos( s->mastergain_millibel ); selected = false; - m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"off / 1 tap (nearest)" ), 1 ); - m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"2 tap (linear)" ), 2 ); - m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"4 tap (cubic)" ), 4 ); - m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"8 tap (polyphase fir)" ), 8 ); + m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"Off / 1 Tap (Nearest)" ), 1 ); + m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"2 Tap (Linear)" ), 2 ); + m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"4 Tap (Cubic)" ), 4 ); + m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"8 Tap (Polyphase FIR)" ), 8 ); for ( int index = 0; index < m_ComboBoxInterpolation.GetCount(); ++index ) { if ( m_ComboBoxInterpolation.GetItemData( index ) == s->interpolationfilterlength ) { m_ComboBoxInterpolation.SetCurSel( index ); @@ -182,15 +172,15 @@ protected: } } if ( !selected ) { - m_ComboBoxInterpolation.SelectString( 0, L"8 tap (polyphase fir)" ); + m_ComboBoxInterpolation.SelectString( 0, L"8 Tap (Polyphase FIR)" ); } m_CheckBoxAmigaResampler.SetCheck( s->use_amiga_resampler ? BST_CHECKED : BST_UNCHECKED ); selected = false; - m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"forever" ), -1 ); - m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"never" ), 0 ); - m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"once" ), 1 ); + m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Forever" ), -1 ); + m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Never" ), 0 ); + m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Once" ), 1 ); for ( int index = 0; index < m_ComboBoxRepeat.GetCount(); ++index ) { if ( m_ComboBoxRepeat.GetItemData( index ) == s->repeatcount ) { m_ComboBoxRepeat.SetCurSel( index ); @@ -198,7 +188,7 @@ protected: } } if ( !selected ) { - m_ComboBoxRepeat.SelectString( 0, L"never" ); + m_ComboBoxRepeat.SelectString( 0, L"Never" ); } m_SliderCtrlStereoSeparation.SetRange( 0, 200 ); @@ -208,8 +198,8 @@ protected: m_SliderCtrlStereoSeparation.SetPos( s->stereoseparation ); selected = false; - m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"default" ), -1 ); - m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"off" ), 0 ); + m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"Default" ), -1 ); + m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"Off" ), 0 ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"1 ms" ), 1 ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"2 ms" ), 2 ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"3 ms" ), 3 ); @@ -222,14 +212,14 @@ protected: } } if ( !selected ) { - m_ComboBoxRamping.SelectString( 0, L"default" ); + m_ComboBoxRamping.SelectString( 0, L"Default" ); } return TRUE; } - virtual void OnOK() { + void OnOK() override { s->samplerate = m_ComboBoxSamplerate.GetItemData( m_ComboBoxSamplerate.GetCurSel() ); @@ -253,9 +243,37 @@ protected: } + BOOL OnToolTipText( UINT, NMHDR * pNMHDR, LRESULT * pResult ) { + TOOLTIPTEXT * pTTT = reinterpret_cast( pNMHDR ); + + UINT_PTR nID = pNMHDR->idFrom; + if( pTTT->uFlags & TTF_IDISHWND ) + { + // idFrom is actually the HWND of the tool + nID = (UINT_PTR)::GetDlgCtrlID((HWND)nID); + } + + switch ( nID ) { + case IDC_SLIDER_GAIN: + swprintf( pTTT->szText, _countof(pTTT->szText), L"%.02f dB", m_SliderCtrlGain.GetPos() * 0.01f ); + break; + + case IDC_SLIDER_STEREOSEPARATION: + swprintf( pTTT->szText, _countof(pTTT->szText), L"%d %%", m_SliderCtrlStereoSeparation.GetPos()); + break; + + default: + return FALSE; + } + + *pResult = 0; + return TRUE; + } + }; BEGIN_MESSAGE_MAP(CSettingsDialog, CDialog) + ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CSettingsDialog::OnToolTipText) END_MESSAGE_MAP() @@ -264,8 +282,6 @@ class CInfoDialog : public CDialog { protected: - DECLARE_MESSAGE_MAP() - CString m_Title; CString m_FileInfo; CEdit m_EditFileInfo; @@ -282,13 +298,13 @@ public: protected: - virtual void DoDataExchange( CDataExchange * pDX ) + void DoDataExchange( CDataExchange * pDX ) override { CDialog::DoDataExchange( pDX ); DDX_Control( pDX, IDC_FILEINFO, m_EditFileInfo ); } - afx_msg BOOL OnInitDialog() { + afx_msg BOOL OnInitDialog() override { if ( !CDialog::OnInitDialog() ) { return false; @@ -302,28 +318,19 @@ protected: } - virtual void OnOK() { - - CDialog::OnOK(); - - } - }; -BEGIN_MESSAGE_MAP(CInfoDialog, CDialog) -END_MESSAGE_MAP() - void gui_edit_settings( libopenmpt_settings * s, HWND parent, std::wstring title ) { AFX_MANAGE_STATE( AfxGetStaticModuleState() ); - CSettingsDialog dlg( s, title.c_str(), parent ? CWnd::FromHandle( parent ) : NULL ); + CSettingsDialog dlg( s, title.c_str(), parent ? CWnd::FromHandle( parent ) : nullptr ); dlg.DoModal(); } void gui_show_file_info( HWND parent, std::wstring title, std::wstring info ) { AFX_MANAGE_STATE( AfxGetStaticModuleState() ); - CInfoDialog dlg( title.c_str(), info.c_str(), parent ? CWnd::FromHandle( parent ) : NULL ); + CInfoDialog dlg( title.c_str(), info.c_str(), parent ? CWnd::FromHandle( parent ) : nullptr); dlg.DoModal(); } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc index 64e300b43..ebd7bbe4b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc @@ -50,33 +50,33 @@ END // Dialog // -IDD_SETTINGS DIALOGEX 0, 0, 161, 183 +IDD_SETTINGS DIALOGEX 0, 0, 201, 183 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "&OK",IDOK,42,162,54,14 - PUSHBUTTON "&Cancel",IDCANCEL,102,162,54,14 + DEFPUSHBUTTON "&OK",IDOK,78,162,54,14 + PUSHBUTTON "&Cancel",IDCANCEL,138,162,54,14 LTEXT "&Samplerate",IDC_STATIC,6,6,60,12,SS_CENTERIMAGE - COMBOBOX IDC_COMBO_SAMPLERATE,72,6,84,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_SAMPLERATE,72,6,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "C&hannels",IDC_STATIC,6,24,60,12,SS_CENTERIMAGE - COMBOBOX IDC_COMBO_CHANNELS,72,24,84,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_CHANNELS,72,24,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "&Gain",IDC_STATIC,6,42,60,12,SS_CENTERIMAGE - CONTROL "",IDC_SLIDER_GAIN,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,72,42,84,15 + CONTROL "",IDC_SLIDER_GAIN,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,72,42,120,15 LTEXT "&Interpolation",IDC_STATIC,6,60,60,12,SS_CENTERIMAGE - COMBOBOX IDC_COMBO_INTERPOLATION,72,60,84,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_INTERPOLATION,72,60,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "Use &Amiga resampler for Amiga modules",IDC_CHECK_AMIGA_RESAMPLER, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,78,150,12 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,78,186,12 LTEXT "&Repeat",IDC_STATIC,6,96,60,12,SS_CENTERIMAGE - COMBOBOX IDC_COMBO_REPEAT,72,96,84,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_REPEAT,72,96,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "S&tereo Separation",IDC_STATIC,6,114,60,12,SS_CENTERIMAGE - CONTROL "",IDC_SLIDER_STEREOSEPARATION,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,72,114,84,15 + CONTROL "",IDC_SLIDER_STEREOSEPARATION,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,72,114,126,15 LTEXT "&Volume Ramping",IDC_STATIC,6,132,60,12,SS_CENTERIMAGE - COMBOBOX IDC_COMBO_RAMPING,72,132,84,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,6,156,144,1 + COMBOBOX IDC_COMBO_RAMPING,72,132,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,6,156,186,1 END IDD_FILEINFO DIALOGEX 0, 0, 310, 174 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,258,156,48,12 @@ -95,7 +95,7 @@ BEGIN IDD_SETTINGS, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 154 + RIGHTMARGIN, 194 TOPMARGIN, 7 BOTTOMMARGIN, 176 END @@ -121,6 +121,13 @@ BEGIN 0 END +IDD_FILEINFO AFX_DIALOG_LAYOUT +BEGIN + 0, + 100, 100, 0, 0, + 0, 0, 100, 100 +END + #endif // German (Germany) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_buffer.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_buffer.h index 8111cfde7..575049e4d 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_buffer.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_buffer.h @@ -27,6 +27,10 @@ #include #include +/*! \addtogroup libopenmpt_c + * @{ + */ + #ifdef __cplusplus extern "C" { #endif @@ -162,6 +166,17 @@ static void openmpt_stream_buffer_init( openmpt_stream_buffer * buffer, const vo #define openmpt_stream_buffer_overflowed( buffer_ ) ( (buffer_)->overflow ) +/*! \brief Provide openmpt_stream_callbacks for in-memoy buffers + * + * Fills openmpt_stream_callbacks suitable for passing an in-memory buffer as a stream parameter to functions doing file input/output. + * + * \remarks The stream argument must be passed as `(void*)(openmpt_stream_buffer*)stream_buffer`. + * \sa \ref libopenmpt_c_fileio + * \sa openmpt_stream_callbacks + * \sa openmpt_could_open_probability2 + * \sa openmpt_probe_file_header_from_stream + * \sa openmpt_module_create2 + */ static openmpt_stream_callbacks openmpt_stream_get_buffer_callbacks(void) { openmpt_stream_callbacks retval; memset( &retval, 0, sizeof( openmpt_stream_callbacks ) ); @@ -175,5 +190,9 @@ static openmpt_stream_callbacks openmpt_stream_get_buffer_callbacks(void) { } #endif +/*! + * @} + */ + #endif /* LIBOPENMPT_STREAM_CALLBACKS_BUFFER_H */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_fd.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_fd.h index 4c814cf3f..46b39f129 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_fd.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_fd.h @@ -23,6 +23,10 @@ #include #endif +/*! \addtogroup libopenmpt_c + * @{ + */ + #ifdef __cplusplus extern "C" { #endif @@ -67,6 +71,17 @@ static size_t openmpt_stream_fd_read_func( void * stream, void * dst, size_t byt return retval; } +/*! \brief Provide openmpt_stream_callbacks for standard POSIX file descriptors + * + * Fills openmpt_stream_callbacks suitable for passing a POSIX filer descriptor as a stream parameter to functions doing file input/output. + * + * \remarks The stream argument must be passed as `(void*)(uintptr_t)(int)fd`. + * \sa \ref libopenmpt_c_fileio + * \sa openmpt_stream_callbacks + * \sa openmpt_could_open_probability2 + * \sa openmpt_probe_file_header_from_stream + * \sa openmpt_module_create2 + */ static openmpt_stream_callbacks openmpt_stream_get_fd_callbacks(void) { openmpt_stream_callbacks retval; memset( &retval, 0, sizeof( openmpt_stream_callbacks ) ); @@ -78,5 +93,9 @@ static openmpt_stream_callbacks openmpt_stream_get_fd_callbacks(void) { } #endif +/*! + * @} + */ + #endif /* LIBOPENMPT_STREAM_CALLBACKS_FD_H */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_file.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_file.h index 4fa529fd9..643049153 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_file.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_stream_callbacks_file.h @@ -20,6 +20,10 @@ #include /* off_t */ #endif +/*! \addtogroup libopenmpt_c + * @{ + */ + #ifdef __cplusplus extern "C" { #endif @@ -96,6 +100,17 @@ static int64_t openmpt_stream_file_tell_func( void * stream ) { return retval; } +/*! \brief Provide openmpt_stream_callbacks for standard C FILE objects + * + * Fills openmpt_stream_callbacks suitable for passing a standard C FILE object as a stream parameter to functions doing file input/output. + * + * \remarks The stream argument must be passed as `(void*)(FILE*)file`. + * \sa \ref libopenmpt_c_fileio + * \sa openmpt_stream_callbacks + * \sa openmpt_could_open_probability2 + * \sa openmpt_probe_file_header_from_stream + * \sa openmpt_module_create2 + */ static openmpt_stream_callbacks openmpt_stream_get_file_callbacks(void) { openmpt_stream_callbacks retval; memset( &retval, 0, sizeof( openmpt_stream_callbacks ) ); @@ -109,5 +124,9 @@ static openmpt_stream_callbacks openmpt_stream_get_file_callbacks(void) { } #endif +/*! + * @} + */ + #endif /* LIBOPENMPT_STREAM_CALLBACKS_FILE_H */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_test.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_test.cpp index 99607f0bf..a7c980b32 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_test.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_test.cpp @@ -45,7 +45,8 @@ int main( int /*argc*/ , char * /*argv*/ [] ) { // try to set the C and C++ locales to the user locale try { - std::locale::global( std::locale( "" ) ); + std::locale old = std::locale::global( std::locale( "" ) ); + (void)old; } catch ( ... ) { // Setting c++ global locale does not work. // This is no problem for libopenmpt, just continue. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index 9d697e0ca..ecf1f3a8a 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -17,9 +17,9 @@ /*! \brief libopenmpt major version number */ #define OPENMPT_API_VERSION_MAJOR 0 /*! \brief libopenmpt minor version number */ -#define OPENMPT_API_VERSION_MINOR 3 +#define OPENMPT_API_VERSION_MINOR 4 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 12 +#define OPENMPT_API_VERSION_PATCH 2 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk index 5ffb8a15f..bcf578f7d 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 -LIBOPENMPT_VERSION_MINOR=3 -LIBOPENMPT_VERSION_PATCH=12 +LIBOPENMPT_VERSION_MINOR=4 +LIBOPENMPT_VERSION_PATCH=2 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=1 -LIBOPENMPT_LTVER_REVISION=12 +LIBOPENMPT_LTVER_REVISION=2 LIBOPENMPT_LTVER_AGE=1 diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc index 87297802f..52fca64be 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc @@ -192,7 +192,7 @@ BEGIN VALUE "FileDescription", VER_FILEDESC_STR VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", VER_FILENAME_STR - VALUE "LegalCopyright", "Copyright © 2004-2018 OpenMPT contributors, Copyright © 1997-2003 Olivier Lapicque" + VALUE "LegalCopyright", "Copyright © 2004-2019 OpenMPT contributors, Copyright © 1997-2003 Olivier Lapicque" VALUE "OriginalFilename", VER_FILENAME_STR VALUE "ProductName", "libopenmpt" VALUE "ProductVersion", VER_FILEVERSION_STR diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h index a99fe7534..c4d4a2121 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by libopenmpt_settings.rc +// Used by libopenmpt_plugin_gui.rc // #define IDD_SETTINGS 101 #define IDD_FILEINFO 102 diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp index 30a8e1eb4..c210d7f7f 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp @@ -9,7 +9,6 @@ #ifndef NO_XMPLAY -#if defined(_MFC_VER) || 1 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif @@ -17,11 +16,13 @@ #define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP #endif #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // Avoid binary bloat from linking unused MFC controls +#ifndef NOMINMAX #define NOMINMAX +#endif #include #include #include -#endif // _MFC_VER +#include #ifdef LIBOPENMPT_BUILD_DLL #undef LIBOPENMPT_BUILD_DLL @@ -36,27 +37,10 @@ #endif #endif // _MSC_VER -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#include +#include #include "libopenmpt.hpp" #include "libopenmpt_ext.hpp" -#ifdef LIBOPENMPT_QUIRK_NO_CSTDINT -#include -namespace std { -typedef ::int8_t int8_t; -typedef ::int16_t int16_t; -typedef ::int32_t int32_t; -typedef ::int64_t int64_t; -typedef ::uint8_t uint8_t; -typedef ::uint16_t uint16_t; -typedef ::uint32_t uint32_t; -typedef ::uint64_t uint64_t; -} -#endif #include "libopenmpt_plugin_gui.hpp" @@ -80,6 +64,7 @@ enum { openmpt_shortcut_tempo_increase, openmpt_shortcut_pitch_decrease, openmpt_shortcut_pitch_increase, + openmpt_shortcut_switch_interpolation, openmpt_shortcut_last = 0x21fff, openmpt_shortcut_ex = 0x80000000, // Use extended version of the shortcut callback @@ -113,12 +98,12 @@ public: } }; -static XMPFUNC_IN * xmpfin = NULL; -static XMPFUNC_MISC * xmpfmisc = NULL; -static XMPFUNC_REGISTRY * xmpfregistry = NULL; -static XMPFUNC_FILE * xmpffile = NULL; -static XMPFUNC_TEXT * xmpftext = NULL; -static XMPFUNC_STATUS * xmpfstatus = NULL; +static XMPFUNC_IN * xmpfin = nullptr; +static XMPFUNC_MISC * xmpfmisc = nullptr; +static XMPFUNC_REGISTRY * xmpfregistry = nullptr; +static XMPFUNC_FILE * xmpffile = nullptr; +static XMPFUNC_TEXT * xmpftext = nullptr; +static XMPFUNC_STATUS * xmpfstatus = nullptr; struct self_xmplay_t; @@ -132,14 +117,14 @@ class xmp_openmpt_settings : public libopenmpt::plugin::settings { protected: - virtual void read_setting( const std::string & key, const std::wstring & keyW, int & val ) { + void read_setting( const std::string & key, const std::wstring & keyW, int & val ) override { libopenmpt::plugin::settings::read_setting( key, keyW, val ); int storedVal = 0; if ( xmpfregistry->GetInt( "OpenMPT", key.c_str(), &storedVal ) ) { val = storedVal; } } - virtual void write_setting( const std::string & key, const std::wstring & /* keyW */ , int val ) { + void write_setting( const std::string & key, const std::wstring & /* keyW */ , int val ) override { if ( !xmpfregistry->SetInt( "OpenMPT", key.c_str(), &val ) ) { // error } @@ -159,25 +144,15 @@ public: struct self_xmplay_t { std::vector subsong_lengths; - std::size_t samplerate; - std::size_t num_channels; + std::size_t samplerate = 48000; + std::size_t num_channels = 2; xmp_openmpt_settings settings; - openmpt::module_ext * mod; - bool set_format_called; - openmpt::ext::pattern_vis * pattern_vis; - std::int32_t tempo_factor, pitch_factor; - bool single_subsong_mode; - self_xmplay_t() - : samplerate(48000) - , num_channels(2) - , settings() - , mod(0) - , set_format_called(false) - , pattern_vis(0) - , tempo_factor(0) - , pitch_factor(0) - , single_subsong_mode(false) - { + openmpt::module_ext * mod = nullptr; + bool set_format_called = false; + openmpt::ext::pattern_vis * pattern_vis = nullptr; + std::int32_t tempo_factor = 0, pitch_factor = 0; + bool single_subsong_mode = false; + self_xmplay_t() { settings.changed = apply_and_save_options; } void on_new_mod() { @@ -209,19 +184,19 @@ static std::string convert_to_native( const std::string & str ) { static std::string StringEncode( const std::wstring &src, UINT codepage ) { - int required_size = WideCharToMultiByte( codepage, 0, src.c_str(), -1, NULL, 0, NULL, NULL ); + int required_size = WideCharToMultiByte( codepage, 0, src.c_str(), -1, nullptr, 0, nullptr, nullptr); if(required_size <= 0) { return std::string(); } std::vector encoded_string( required_size ); - WideCharToMultiByte( codepage, 0, src.c_str(), -1, &encoded_string[0], encoded_string.size(), NULL, NULL ); + WideCharToMultiByte( codepage, 0, src.c_str(), -1, &encoded_string[0], encoded_string.size(), nullptr, nullptr); return &encoded_string[0]; } static std::wstring StringDecode( const std::string & src, UINT codepage ) { - int required_size = MultiByteToWideChar( codepage, 0, src.c_str(), -1, NULL, 0 ); + int required_size = MultiByteToWideChar( codepage, 0, src.c_str(), -1, nullptr, 0 ); if(required_size <= 0) { return std::wstring(); @@ -243,6 +218,11 @@ static inline Tstring StringReplace( Tstring str, const Tstring2 & oldStr_, cons return str; } +static std::string StringUpperCase( std::string str ) { + std::transform( str.begin(), str.end(), str.begin(), std::toupper ); + return str; +} + static std::string seconds_to_string( float time ) { std::int64_t time_ms = static_cast( time * 1000 ); std::int64_t seconds = ( time_ms / 1000 ) % 60; @@ -271,7 +251,7 @@ static void save_settings_to_map( std::map & result, const libo } static inline void load_map_setting( const std::map & map, const std::string & key, int & val ) { - std::map::const_iterator it = map.find( key ); + auto it = map.find( key ); if ( it != map.end() ) { val = it->second; } @@ -293,8 +273,8 @@ static void load_settings_from_xml( libopenmpt::plugin::settings & s, const std: doc.load_string( xml.c_str() ); pugi::xml_node settings_node = doc.child( "settings" ); std::map map; - for ( pugi::xml_attribute_iterator it = settings_node.attributes_begin(); it != settings_node.attributes_end(); ++it ) { - map[ it->name() ] = it->as_int(); + for ( const auto & attr : settings_node.attributes() ) { + map[ attr.name() ] = attr.as_int(); } load_settings_from_map( s, map ); } @@ -304,7 +284,7 @@ static void save_settings_to_xml( std::string & xml, const libopenmpt::plugin::s save_settings_to_map( map, s ); pugi::xml_document doc; pugi::xml_node settings_node = doc.append_child( "settings" ); - for ( const auto &setting : map ) { + for ( const auto & setting : map ) { settings_node.append_attribute( setting.first.c_str() ).set_value( setting.second ); } std::ostringstream buf; @@ -374,6 +354,24 @@ static void WINAPI ShortcutHandler( DWORD id ) { case openmpt_shortcut_tempo_increase: self->tempo_factor++; tempo_changed = true; break; case openmpt_shortcut_pitch_decrease: self->pitch_factor--; pitch_changed = true; break; case openmpt_shortcut_pitch_increase: self->pitch_factor++; pitch_changed = true; break; + case openmpt_shortcut_switch_interpolation: + self->settings.interpolationfilterlength *= 2; + if ( self->settings.interpolationfilterlength > 8 ) { + self->settings.interpolationfilterlength = 1; + } + apply_and_save_options(); + const char *s = nullptr; + switch ( self->settings.interpolationfilterlength ) + { + case 1: s = "Interpolation: Off"; break; + case 2: s = "Interpolation: Linear"; break; + case 4: s = "Interpolation: Cubic"; break; + case 8: s = "Interpolation: Polyphase"; break; + } + if ( s ) { + xmpfmisc->ShowBubble( s, 0 ); + } + break; } self->tempo_factor = std::min ( 48, std::max( -48, self->tempo_factor ) ); @@ -451,7 +449,7 @@ static void clear_current_timeinfo() { static void WINAPI openmpt_About( HWND win ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2018 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2019 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; @@ -465,7 +463,7 @@ static void WINAPI openmpt_About( HWND win ) { credits << "Additional thanks to:" << std::endl; credits << std::endl; credits << "Arseny Kapoulkine for pugixml" << std::endl; - credits << "http://pugixml.org/" << std::endl; + credits << "https://pugixml.org/" << std::endl; libopenmpt::plugin::gui_show_file_info( win, TEXT(SHORT_TITLE), StringReplace( StringDecode( credits.str(), CP_UTF8 ), L"\n", L"\r\n" ) ); } @@ -595,11 +593,11 @@ static std::string extract_date( const openmpt::module & mod ) { // be considered. std::string s = " " + mod.get_metadata("message"); auto names = mod.get_sample_names(); - for ( auto &name : names ) { + for ( const auto & name : names ) { s += " " + name; } names = mod.get_instrument_names(); - for ( auto &name : names ) { + for ( const auto & name : names ) { s += " " + name; } s += " "; @@ -658,7 +656,7 @@ static void append_xmplay_tag( std::string & tags, const std::string & tag, cons static char * build_xmplay_tags( const openmpt::module & mod ) { std::string tags; - append_xmplay_tag( tags, "filetype", convert_to_native( 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( mod.get_metadata("title") ) ); 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 @@ -669,7 +667,7 @@ static char * build_xmplay_tags( const openmpt::module & mod ) { tags.append( 1, '\0' ); char * result = static_cast( xmpfmisc->Alloc( tags.size() ) ); if ( !result ) { - return NULL; + return nullptr; } std::copy( tags.data(), tags.data() + tags.size(), result ); return result; @@ -678,7 +676,7 @@ static char * build_xmplay_tags( const openmpt::module & mod ) { static float * build_xmplay_length( const openmpt::module & mod ) { float * result = static_cast( xmpfmisc->Alloc( sizeof( float ) * self->subsong_lengths.size() ) ); if ( !result ) { - return NULL; + return nullptr; } for ( std::size_t i = 0; i < self->subsong_lengths.size(); ++i ) { result[i] = self->subsong_lengths[i]; @@ -794,9 +792,11 @@ static BOOL WINAPI openmpt_CheckFile( const char * filename, XMPFILE file ) { static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, float * * length, char * * tags ) { try { - std::map< std::string, std::string > ctls; - ctls[ "load.skip_plugins" ] = "1"; - ctls[ "load.skip_samples" ] = "1"; + std::map< std::string, std::string > ctls + { + { "load.skip_plugins", "1" }, + { "load.skip_samples", "1" }, + }; #ifdef USE_XMPLAY_FILE_IO #ifdef USE_XMPLAY_ISTREAM switch ( xmpffile->GetType( file ) ) { @@ -857,8 +857,8 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl } #endif } catch ( ... ) { - if ( length ) *length = NULL; - if ( tags ) *tags = NULL; + if ( length ) *length = nullptr; + if ( tags ) *tags = nullptr; return 0; } return self->subsong_lengths.size() + XMPIN_INFO_NOSUBTAGS; @@ -870,8 +870,11 @@ static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) { xmpopenmpt_lock guard; reset_options(); try { - std::map< std::string, std::string > ctls; - ctls["seek.sync_samples"] = "1"; + std::map< std::string, std::string > ctls + { + { "seek.sync_samples", "1" }, + { "play.at_end", "continue" }, + }; self->delete_mod(); #ifdef USE_XMPLAY_FILE_IO #ifdef USE_XMPLAY_ISTREAM @@ -991,7 +994,7 @@ static void WINAPI openmpt_GetInfoText( char * format, char * length ) { if ( format ) { std::ostringstream str; str - << self->mod->get_metadata("type") + << StringUpperCase( self->mod->get_metadata("type") ) << " - " << self->mod->get_num_channels() << " ch" << " - " @@ -1033,6 +1036,9 @@ static void WINAPI openmpt_GetGeneralInfo( char * buf ) { str << "\r"; } str << "Format" << "\t" << sanitize_xmplay_info_string( self->mod->get_metadata("type") ) << " (" << sanitize_xmplay_info_string( self->mod->get_metadata("type_long") ) << ")" << "\r"; + if ( !self->mod->get_metadata("originaltype").empty() ) { + str << "Original Type" << "\t" << sanitize_xmplay_info_string( self->mod->get_metadata("originaltype") ) << " (" << sanitize_xmplay_info_string( self->mod->get_metadata("originaltype_long") ) << ")" << "\r"; + } if ( !self->mod->get_metadata("container").empty() ) { str << "Container" << "\t" << sanitize_xmplay_info_string( self->mod->get_metadata("container") ) << " (" << sanitize_xmplay_info_string( self->mod->get_metadata("container_long") ) << ")" << "\r"; } @@ -1082,6 +1088,16 @@ static double WINAPI openmpt_SetPosition( DWORD pos ) { if ( !self->mod ) { return -1.0; } + if ( pos == XMPIN_POS_LOOP ) { + // If the time of the loop start position is known, that should be returned, otherwise -2 can be returned to let the time run on. + // There is currently no way to easily figure out at which time the loop restarts. + return -2; + } else if ( pos == XMPIN_POS_AUTOLOOP ) { + // In the auto-looping case, the function should only loop when a loop has been detected, and otherwise return -1 + // If the time of the loop start position is known, that should be returned, otherwise -2 can be returned to let the time run on. + // There is currently no way to easily figure out at which time the loop restarts. + return -2; + } if ( pos & XMPIN_POS_SUBSONG ) { self->single_subsong_mode = ( pos & XMPIN_POS_SUBSONG1 ) != 0; try { @@ -1118,7 +1134,7 @@ static DWORD WINAPI openmpt_Process( float * dstbuf, DWORD count ) { std::size_t frames_to_render = frames; std::size_t frames_rendered = 0; while ( frames_to_render > 0 ) { - std::size_t frames_chunk = std::min( frames_to_render, self->samplerate / 100 ); // 100 Hz timing info update interval + std::size_t frames_chunk = std::min( frames_to_render, ( self->samplerate + 99 ) / 100 ); // 100 Hz timing info update interval switch ( self->num_channels ) { case 1: { @@ -1184,9 +1200,9 @@ static void WINAPI openmpt_GetSamples( char * buf ) { } static DWORD WINAPI openmpt_GetSubSongs( float * length ) { - *length = 0.0; - for ( std::size_t i = 0; i < self->subsong_lengths.size(); ++i ) { - *length += self->subsong_lengths[i]; + *length = 0.0f; + for ( auto sub_length : self->subsong_lengths ) { + *length += sub_length; } return static_cast( self->subsong_lengths.size() ); @@ -1583,16 +1599,16 @@ static XMPIN xmpin = { #else XMPIN_FLAG_NOXMPFILE | #endif - XMPIN_FLAG_CONFIG,// 0, // XMPIN_FLAG_LOOP, the xmplay looping interface is not really compatible with libopenmpt looping interface, so dont support that for now + XMPIN_FLAG_CONFIG | XMPIN_FLAG_LOOP, xmp_openmpt_string, - NULL, // "libopenmpt\0mptm/mptmz", + nullptr, // "libopenmpt\0mptm/mptmz", openmpt_About, openmpt_Config, openmpt_CheckFile, openmpt_GetFileInfo, openmpt_Open, openmpt_Close, - NULL, // reserved + nullptr, // reserved openmpt_SetFormat, openmpt_GetTags, openmpt_GetInfoText, @@ -1600,25 +1616,25 @@ static XMPIN xmpin = { openmpt_GetMessage, openmpt_SetPosition, openmpt_GetGranularity, - NULL, // GetBuffering + nullptr, // GetBuffering openmpt_Process, - NULL, // WriteFile + nullptr, // WriteFile openmpt_GetSamples, openmpt_GetSubSongs, // GetSubSongs - NULL, // GetCues - NULL, // GetDownloaded + nullptr, // GetCues + nullptr, // GetDownloaded "OpenMPT Pattern Display", VisOpen, VisClose, VisSize, - /*VisRender,*/NULL, + /*VisRender,*/nullptr, VisRenderDC, - /*VisButton,*/NULL, + /*VisButton,*/nullptr, - NULL, // reserved2 - openmpt_GetConfig,// NULL, // GetConfig - openmpt_SetConfig// NULL // SetConfig + nullptr, // reserved2 + openmpt_GetConfig, + openmpt_SetConfig }; static const char * xmp_openmpt_default_exts = "OpenMPT\0mptm/mptmz"; @@ -1628,20 +1644,17 @@ static char * file_formats; static void xmp_openmpt_on_dll_load() { ZeroMemory( &xmpopenmpt_mutex, sizeof( xmpopenmpt_mutex ) ); InitializeCriticalSection( &xmpopenmpt_mutex ); - std::vector filetypes_string; - filetypes_string.clear(); std::vector extensions = openmpt::get_supported_extensions(); - const char * openmpt_str = "OpenMPT"; - std::copy( openmpt_str, openmpt_str + std::strlen(openmpt_str), std::back_inserter( filetypes_string ) ); + std::string filetypes_string = "OpenMPT"; filetypes_string.push_back('\0'); bool first = true; - for ( std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ++ext ) { + for ( const auto & ext : extensions ) { if ( first ) { first = false; } else { filetypes_string.push_back('/'); } - std::copy( (*ext).begin(), (*ext).end(), std::back_inserter( filetypes_string ) ); + filetypes_string += ext; } filetypes_string.push_back('\0'); file_formats = (char*)HeapAlloc( GetProcessHeap(), 0, filetypes_string.size() ); @@ -1656,21 +1669,16 @@ static void xmp_openmpt_on_dll_load() { static void xmp_openmpt_on_dll_unload() { delete self; - self = 0; - if ( !xmpin.exts ) { - return; + self = nullptr; + if ( xmpin.exts != xmp_openmpt_default_exts ) { + HeapFree(GetProcessHeap(), 0, (LPVOID)xmpin.exts); } - if ( xmpin.exts == xmp_openmpt_default_exts ) { - xmpin.exts = NULL; - return; - } - HeapFree( GetProcessHeap(), 0, (LPVOID)xmpin.exts ); - xmpin.exts = NULL; + xmpin.exts = nullptr; DeleteCriticalSection( &xmpopenmpt_mutex ); } static XMPIN * XMPIN_GetInterface_cxx( DWORD face, InterfaceProc faceproc ) { - if (face!=XMPIN_FACE) return NULL; + if ( face != XMPIN_FACE ) return nullptr; xmpfin=(XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE); xmpfmisc=(XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE); xmpfregistry=(XMPFUNC_REGISTRY*)faceproc(XMPFUNC_REGISTRY_FACE); @@ -1679,20 +1687,20 @@ static XMPIN * XMPIN_GetInterface_cxx( DWORD face, InterfaceProc faceproc ) { xmpfstatus=(XMPFUNC_STATUS*)faceproc(XMPFUNC_STATUS_FACE); // Register keyboard shortcuts + static constexpr std::pair shortcuts[] = { + { openmpt_shortcut_ex | openmpt_shortcut_tempo_decrease, "OpenMPT - Decrease Tempo" }, + { openmpt_shortcut_ex | openmpt_shortcut_tempo_increase, "OpenMPT - Increase Tempo" }, + { openmpt_shortcut_ex | openmpt_shortcut_pitch_decrease, "OpenMPT - Decrease Pitch" }, + { openmpt_shortcut_ex | openmpt_shortcut_pitch_increase, "OpenMPT - Increase Pitch" }, + { openmpt_shortcut_ex | openmpt_shortcut_switch_interpolation, "OpenMPT - Switch Interpolation" }, + }; XMPSHORTCUT cut; cut.procex = &ShortcutHandler; - cut.id = openmpt_shortcut_ex | openmpt_shortcut_tempo_decrease; - cut.text = "OpenMPT - Decrease Tempo"; - xmpfmisc->RegisterShortcut( &cut ); - cut.id = openmpt_shortcut_ex | openmpt_shortcut_tempo_increase; - cut.text = "OpenMPT - Increase Tempo"; - xmpfmisc->RegisterShortcut( &cut ); - cut.id = openmpt_shortcut_ex | openmpt_shortcut_pitch_decrease; - cut.text = "OpenMPT - Decrease Pitch"; - xmpfmisc->RegisterShortcut( &cut ); - cut.id = openmpt_shortcut_ex | openmpt_shortcut_pitch_increase; - cut.text = "OpenMPT - Increase Pitch"; - xmpfmisc->RegisterShortcut( &cut ); + for ( const auto & shortcut : shortcuts ) { + cut.id = shortcut.first; + cut.text = shortcut.second; + xmpfmisc->RegisterShortcut( &cut ); + } self->settings.load(); diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index 4db2dae66..b5b4b516d 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -8,9 +8,7 @@ */ static const char * const license = -"The OpenMPT code is licensed under the BSD license." "\n" -"" "\n" -"Copyright (c) 2004-2018, OpenMPT contributors" "\n" +"Copyright (c) 2004-2019, OpenMPT contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" @@ -60,7 +58,15 @@ static const char * const license = #include #include -#if defined(WIN32) +#if defined(__DJGPP__) +#include +#include +#include +#include +#include +#include +#include +#elif defined(WIN32) #include #include #include @@ -90,6 +96,7 @@ static const char * const license = #include "openmpt123_sndfile.hpp" #include "openmpt123_raw.hpp" #include "openmpt123_stdout.hpp" +#include "openmpt123_allegro42.hpp" #include "openmpt123_portaudio.hpp" #include "openmpt123_pulseaudio.hpp" #include "openmpt123_sdl.hpp" @@ -99,35 +106,27 @@ static const char * const license = namespace openmpt123 { struct silent_exit_exception : public std::exception { - silent_exit_exception() { } }; struct show_license_exception : public std::exception { - show_license_exception() { } }; struct show_credits_exception : public std::exception { - show_credits_exception() { } }; struct show_man_version_exception : public std::exception { - show_man_version_exception() { } }; struct show_man_help_exception : public std::exception { - show_man_help_exception() { } }; struct show_short_version_number_exception : public std::exception { - show_short_version_number_exception() { } }; struct show_version_number_exception : public std::exception { - show_version_number_exception() { } }; struct show_long_version_number_exception : public std::exception { - show_long_version_number_exception() { } }; bool IsTerminal( int fd ) { @@ -205,27 +204,27 @@ public: impl = 0; } } - virtual void write_metadata( std::map metadata ) { + void write_metadata( std::map metadata ) override { impl->write_metadata( metadata ); } - virtual void write_updated_metadata( std::map metadata ) { + void write_updated_metadata( std::map metadata ) override { impl->write_updated_metadata( metadata ); } - virtual void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { impl->write( buffers, frames ); } - virtual void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { impl->write( buffers, frames ); } }; static std::string ctls_to_string( const std::map & ctls ) { std::string result; - for ( std::map::const_iterator it = ctls.begin(); it != ctls.end(); ++it ) { + for ( const auto & ctl : ctls ) { if ( !result.empty() ) { result += "; "; } - result += it->first + "=" + it->second; + result += ctl.first + "=" + ctl.second; } return result; } @@ -286,8 +285,8 @@ static std::ostream & operator << ( std::ostream & s, const commandlineflags & f s << "Ctls: " << ctls_to_string( flags.ctls ) << std::endl; s << std::endl; s << "Files: " << std::endl; - for ( std::vector::const_iterator filename = flags.filenames.begin(); filename != flags.filenames.end(); ++filename ) { - s << " " << *filename << std::endl; + for ( const auto & filename : flags.filenames ) { + s << " " << filename << std::endl; } s << std::endl; return s; @@ -442,7 +441,7 @@ static std::string seconds_to_string( double time ) { static void show_info( std::ostream & log, bool verbose ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << ", libopenmpt " << openmpt::string::get( "library_version" ) << " (" << "OpenMPT " << openmpt::string::get( "core_version" ) << ")" << std::endl; - log << "Copyright (c) 2013-2018 OpenMPT developers " << std::endl; + log << "Copyright (c) 2013-2019 OpenMPT developers " << std::endl; if ( !verbose ) { log << std::endl; return; @@ -469,13 +468,13 @@ static void show_info( std::ostream & log, bool verbose ) { fields.push_back( field ); } bool first = true; - for ( std::vector::const_iterator it = fields.begin(); it != fields.end(); ++it ) { + for ( const auto & field : fields ) { if ( first ) { first = false; } else { log << ", "; } - log << (*it); + log << field; } } log << std::endl; @@ -531,7 +530,7 @@ static void show_info( std::ostream & log, bool verbose ) { static void show_man_version( textout & log ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << std::endl; log << std::endl; - log << "Copyright (c) 2013-2018 OpenMPT developers " << std::endl; + log << "Copyright (c) 2013-2019 OpenMPT developers " << std::endl; } static void show_short_version( textout & log ) { @@ -666,13 +665,13 @@ static void show_help( textout & log, bool with_info = true, bool longhelp = fal log << " "; std::vector extensions = openmpt::get_supported_extensions(); bool first = true; - for ( std::vector::iterator i = extensions.begin(); i != extensions.end(); ++i ) { + for ( const auto & extension : extensions ) { if ( first ) { first = false; } else { log << ", "; } - log << *i; + log << extension; } log << std::endl; } @@ -741,15 +740,15 @@ void ctl_set( Tmod & mod, const std::string & ctl, const T & val ) { template < typename Tmod > static void apply_mod_settings( commandlineflags & flags, Tmod & mod ) { - flags.separation = std::max( flags.separation, 0 ); - flags.filtertaps = std::max( flags.filtertaps, 1 ); - flags.filtertaps = std::min( flags.filtertaps, 8 ); - flags.ramping = std::max( flags.ramping, -1 ); - flags.ramping = std::min( flags.ramping, 10 ); - flags.tempo = std::max( flags.tempo, -48 ); - flags.tempo = std::min( flags.tempo, 48 ); - flags.pitch = std::max( flags.pitch, -48 ); - flags.pitch = std::min( flags.pitch, 48 ); + flags.separation = std::max( flags.separation, std::int32_t( 0 ) ); + flags.filtertaps = std::max( flags.filtertaps, std::int32_t( 1 ) ); + flags.filtertaps = std::min( flags.filtertaps, std::int32_t( 8 ) ); + flags.ramping = std::max( flags.ramping, std::int32_t( -1 ) ); + flags.ramping = std::min( flags.ramping, std::int32_t( 10 ) ); + flags.tempo = std::max( flags.tempo, std::int32_t( -48 ) ); + flags.tempo = std::min( flags.tempo, std::int32_t( 48 ) ); + flags.pitch = std::max( flags.pitch, std::int32_t( -48 ) ); + flags.pitch = std::min( flags.pitch, std::int32_t( 48 ) ); mod.set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, flags.gain ); mod.set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, flags.separation ); mod.set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, flags.filtertaps ); @@ -1125,7 +1124,16 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto if ( flags.mode == ModeUI ) { -#if defined( WIN32 ) +#if defined( __DJGPP__ ) + + while ( kbhit() ) { + int c = getch(); + if ( !handle_keypress( c, flags, mod, audio_stream ) ) { + return; + } + } + +#elif defined( WIN32 ) while ( _kbhit() ) { int c = _getch(); @@ -1401,8 +1409,8 @@ template < typename Tmod > std::map get_metadata( const Tmod & mod ) { std::map result; const std::vector metadata_keys = mod.get_metadata_keys(); - for ( std::vector::const_iterator key = metadata_keys.begin(); key != metadata_keys.end(); ++key ) { - result[ *key ] = mod.get_metadata( *key ); + for ( const auto & key : metadata_keys ) { + result[ key ] = mod.get_metadata( key ); } return result; } @@ -1426,9 +1434,9 @@ public: static void show_fields( textout & log, const std::vector & fields ) { const std::size_t fw = 11; - for ( std::vector::const_iterator it = fields.begin(); it != fields.end(); ++it ) { - std::string key = it->key; - std::string val = it->val; + for ( const auto & field :fields ) { + std::string key = field.key; + std::string val = field.val; if ( key.length() < fw ) { key += std::string( fw - key.length(), '.' ); } @@ -1534,6 +1542,9 @@ void render_mod_file( commandlineflags & flags, const std::string & filename, st set_field( fields, "Container" ).ostream() << mod.get_metadata( "container" ) << " (" << mod.get_metadata( "container_long" ) << ")"; } set_field( fields, "Type" ).ostream() << mod.get_metadata( "type" ) << " (" << mod.get_metadata( "type_long" ) << ")"; + if ( !mod.get_metadata( "originaltype" ).empty() ) { + set_field( fields, "Orig. Type" ).ostream() << mod.get_metadata( "originaltype" ) << " (" << mod.get_metadata( "originaltype_long" ) << ")"; + } if ( ( mod.get_num_subsongs() > 1 ) && ( flags.subsong != -1 ) ) { set_field( fields, "Subsong" ).ostream() << flags.subsong; } @@ -1968,7 +1979,9 @@ static commandlineflags parse_openmpt123( const std::vector & args, commandlineflags flags; bool files_only = false; - for ( std::vector::const_iterator i = args.begin(); i != args.end(); ++i ) { + // cppcheck false-positive + // cppcheck-suppress StlMissingComparison + for ( auto i = args.begin(); i != args.end(); ++i ) { if ( i == args.begin() ) { // skip program name continue; @@ -2067,6 +2080,9 @@ static commandlineflags parse_openmpt123( const std::vector & args, #endif #if defined( WIN32 ) drivers << " " << "waveout" << std::endl; +#endif +#if defined( MPT_WITH_ALLEGRO42 ) + drivers << " " << "allegro42" << std::endl; #endif throw show_help_exception( drivers.str() ); } else if ( nextarg == "default" ) { @@ -2093,6 +2109,9 @@ static commandlineflags parse_openmpt123( const std::vector & args, #endif #if defined( WIN32 ) devices << show_waveout_devices( log ); +#endif +#if defined( MPT_WITH_ALLEGRO42 ) + devices << show_allegro42_devices( log ); #endif throw show_help_exception( devices.str() ); } else if ( nextarg == "default" ) { @@ -2354,8 +2373,8 @@ static int main( int argc, char * argv [] ) { try { bool stdin_can_ui = true; - for ( std::vector::iterator filename = flags.filenames.begin(); filename != flags.filenames.end(); ++filename ) { - if ( *filename == "-" ) { + for ( const auto & filename : flags.filenames ) { + if ( filename == "-" ) { stdin_can_ui = false; break; } @@ -2406,8 +2425,8 @@ static int main( int argc, char * argv [] ) { switch ( flags.mode ) { case ModeProbe: { - for ( std::vector::iterator filename = flags.filenames.begin(); filename != flags.filenames.end(); ++filename ) { - probe_file( flags, *filename, log ); + for ( const auto & filename : flags.filenames ) { + probe_file( flags, filename, log ); flags.playlist_index++; } } break; @@ -2449,6 +2468,11 @@ static int main( int argc, char * argv [] ) { } else if ( flags.driver == "waveout" || flags.driver.empty() ) { waveout_stream_raii waveout_stream( flags ); render_files( flags, log, waveout_stream, prng ); +#endif +#if defined( MPT_WITH_ALLEGRO42 ) + } else if ( flags.driver == "allegro42" || flags.driver.empty() ) { + allegro42_stream_raii allegro42_stream( flags, log ); + render_files( flags, log, allegro42_stream, prng ); #endif } else { if ( flags.driver.empty() ) { @@ -2459,10 +2483,10 @@ static int main( int argc, char * argv [] ) { } } break; case ModeRender: { - for ( std::vector::iterator filename = flags.filenames.begin(); filename != flags.filenames.end(); ++filename ) { + for ( const auto & filename : flags.filenames ) { flags.apply_default_buffer_sizes(); - file_audio_stream_raii file_audio_stream( flags, *filename + std::string(".") + flags.output_extension, log ); - render_file( flags, *filename, log, file_audio_stream ); + file_audio_stream_raii file_audio_stream( flags, filename + std::string(".") + flags.output_extension, log ); + render_file( flags, filename, log, file_audio_stream ); flags.playlist_index++; } } break; @@ -2473,6 +2497,12 @@ static int main( int argc, char * argv [] ) { } catch ( args_error_exception & ) { show_help( std_out ); return 1; +#ifdef MPT_WITH_ALLEGRO42 + } catch ( allegro42_exception & e ) { + std_err << "Allegro-4.2 error: " << e.what() << std::endl; + std_err.writeout(); + return 1; +#endif #ifdef MPT_WITH_PULSEAUDIO } catch ( pulseaudio_exception & e ) { std_err << "PulseAudio error: " << e.what() << std::endl; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp index 46656852f..89104c9e4 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp @@ -145,7 +145,7 @@ public: return; } public: - virtual void write( const std::string & /* text */ ) { + void write( const std::string & /* text */ ) override { return; } }; @@ -163,10 +163,10 @@ public: writeout(); } public: - virtual void write( const std::string & text ) { + void write( const std::string & text ) override { s << text; } - virtual void writeout() { + void writeout() override { textout::writeout(); s.flush(); } @@ -187,7 +187,7 @@ public: writeout(); } public: - virtual void write( const std::string & text ) { + void write( const std::string & text ) override { #if defined(UNICODE) std::wstring wtext = utf8_to_wstring( text ); WriteConsole( handle, wtext.data(), static_cast( wtext.size() ), NULL, NULL ); @@ -328,9 +328,15 @@ struct commandlineflags { device = ""; buffer = default_high; period = default_high; +#if defined(__DJGPP__) + samplerate = 44100; + channels = 2; + use_float = false; +#else samplerate = 48000; channels = 2; use_float = true; +#endif gain = 0; separation = 100; filtertaps = 8; @@ -344,8 +350,13 @@ struct commandlineflags { end_time = 0.0; quiet = false; verbose = false; +#if defined(__DJGPP__) + terminal_width = 80; + terminal_height = 25; +#else terminal_width = 72; terminal_height = 23; +#endif #if defined(WIN32) CONSOLE_SCREEN_BUFFER_INFO csbi; ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); @@ -404,7 +415,7 @@ struct commandlineflags { shuffle = false; restart = false; playlist_index = 0; - output_extension = "wav"; + output_extension = "auto"; force_overwrite = false; paused = false; } @@ -418,8 +429,8 @@ struct commandlineflags { if ( !output_filename.empty() && ( device != commandlineflags().device || use_stdout ) ) { throw args_error_exception(); } - for ( std::vector::iterator i = filenames.begin(); i != filenames.end(); ++i ) { - if ( *i == "-" ) { + for ( const auto & filename : filenames ) { + if ( filename == "-" ) { canUI = false; } } @@ -485,21 +496,20 @@ struct commandlineflags { if ( samplerate < 0 ) { samplerate = commandlineflags().samplerate; } + if ( output_extension == "auto" ) { + output_extension = ""; + } + if ( mode != ModeRender && !output_extension.empty() ) { + throw args_error_exception(); + } if ( mode == ModeRender && !output_filename.empty() ) { - std::ostringstream warning; - warning << "Warning: --output is deprecated in --render mode. Use --output-type instead." << std::endl; - warnings += warning.str(); + throw args_error_exception(); } - if ( mode != ModeRender && output_extension != "wav" ) { - std::ostringstream warning; - warning << "Warning: --output-type is deprecated in modes other than --render. Use --output instead." << std::endl; - warnings += warning.str(); - } - if ( !output_filename.empty() ) { + if ( mode != ModeRender && !output_filename.empty() ) { output_extension = get_extension( output_filename ); } - if ( mode == ModeRender && output_extension.empty() ) { - throw args_error_exception(); + if ( output_extension.empty() ) { + output_extension = "wav"; } } }; @@ -510,8 +520,8 @@ template < > float convert_sample_to( float val ) { } template < > std::int16_t convert_sample_to( float val ) { std::int32_t tmp = static_cast( val * 32768.0f ); - tmp = std::min( tmp, 32767 ); - tmp = std::max( tmp, -32768 ); + tmp = std::min( tmp, std::int32_t( 32767 ) ); + tmp = std::max( tmp, std::int32_t( -32768 ) ); return static_cast( tmp ); } @@ -591,7 +601,7 @@ private: } } public: - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { lock(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { @@ -601,7 +611,7 @@ public: } unlock(); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { lock(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { @@ -613,7 +623,120 @@ public: } virtual void lock() = 0; virtual void unlock() = 0; - virtual bool sleep( int ms ) = 0; + bool sleep( int ms ) override = 0; +}; + +class write_buffers_polling_wrapper : public write_buffers_interface { +protected: + std::size_t channels; + std::size_t sampleQueueMaxFrames; + std::deque sampleQueue; +protected: + virtual ~write_buffers_polling_wrapper() { + return; + } +protected: + write_buffers_polling_wrapper( const commandlineflags & flags ) + : channels(flags.channels) + , sampleQueueMaxFrames(0) + { + return; + } + void set_queue_size_frames( std::size_t frames ) { + sampleQueueMaxFrames = frames; + } + template < typename Tsample > + Tsample pop_queue() { + float val = 0.0f; + if ( !sampleQueue.empty() ) { + val = sampleQueue.front(); + sampleQueue.pop_front(); + } + return convert_sample_to( val ); + } +public: + void write( const std::vector buffers, std::size_t frames ) override { + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleQueue.push_back( buffers[channel][frame] ); + } + while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { + while ( !forward_queue() ) { + sleep( 1 ); + } + } + } + } + void write( const std::vector buffers, std::size_t frames ) override { + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleQueue.push_back( buffers[channel][frame] * (1.0f/32768.0f) ); + } + while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { + while ( !forward_queue() ) { + sleep( 1 ); + } + } + } + } + virtual bool forward_queue() = 0; + bool sleep( int ms ) override = 0; +}; + +class write_buffers_polling_wrapper_int : public write_buffers_interface { +protected: + std::size_t channels; + std::size_t sampleQueueMaxFrames; + std::deque sampleQueue; +protected: + virtual ~write_buffers_polling_wrapper_int() { + return; + } +protected: + write_buffers_polling_wrapper_int( const commandlineflags & flags ) + : channels(flags.channels) + , sampleQueueMaxFrames(0) + { + return; + } + void set_queue_size_frames( std::size_t frames ) { + sampleQueueMaxFrames = frames; + } + std::int16_t pop_queue() { + std::int16_t val = 0; + if ( !sampleQueue.empty() ) { + val = sampleQueue.front(); + sampleQueue.pop_front(); + } + return val; + } +public: + void write( const std::vector buffers, std::size_t frames ) override { + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleQueue.push_back( convert_sample_to( buffers[channel][frame] ) ); + } + while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { + while ( !forward_queue() ) { + sleep( 1 ); + } + } + } + } + void write( const std::vector buffers, std::size_t frames ) override { + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleQueue.push_back( buffers[channel][frame] ); + } + while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { + while ( !forward_queue() ) { + sleep( 1 ); + } + } + } + } + virtual bool forward_queue() = 0; + bool sleep( int ms ) override = 0; }; class void_audio_stream : public write_buffers_interface { @@ -622,15 +745,15 @@ public: return; } public: - virtual void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { (void)buffers; (void)frames; } - virtual void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { (void)buffers; (void)frames; } - virtual bool is_dummy() const { + bool is_dummy() const override { return true; } }; @@ -641,16 +764,16 @@ protected: return; } public: - virtual void write_metadata( std::map metadata ) { + void write_metadata( std::map metadata ) override { (void)metadata; return; } - virtual void write_updated_metadata( std::map metadata ) { + void write_updated_metadata( std::map metadata ) override { (void)metadata; return; } - virtual void write( const std::vector buffers, std::size_t frames ) = 0; - virtual void write( const std::vector buffers, std::size_t frames ) = 0; + void write( const std::vector buffers, std::size_t frames ) override = 0; + void write( const std::vector buffers, std::size_t frames ) override = 0; virtual ~file_audio_stream_base() { return; } diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_allegro42.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_allegro42.hpp new file mode 100644 index 000000000..87cf1bf7f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_allegro42.hpp @@ -0,0 +1,176 @@ +/* + * openmpt123_allegro42.hpp + * ------------------------ + * Purpose: libopenmpt command line player + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef OPENMPT123_ALLEGRO42_HPP +#define OPENMPT123_ALLEGRO42_HPP + +#include "openmpt123_config.hpp" +#include "openmpt123.hpp" + +#if defined(MPT_WITH_ALLEGRO42) + +#include + +namespace openmpt123 { + +struct allegro42_exception : public exception { + static std::string error_to_string() { + try { + return allegro_error ? std::string( allegro_error ) : std::string(); + } catch ( const std::bad_alloc & ) { + return std::string(); + } + } + allegro42_exception() + : exception( error_to_string() ) + { + } +}; + +class allegro42_raii { +public: + allegro42_raii() { + if ( allegro_init() != 0 ) { + throw allegro42_exception(); + } + } + ~allegro42_raii() { + allegro_exit(); + } +}; + +class allegro42_sound_raii { +public: + allegro42_sound_raii() { + if ( install_sound( DIGI_AUTODETECT, MIDI_NONE, NULL ) != 0 ) { + throw allegro42_exception(); + } + if ( digi_card == DIGI_NONE ) { + remove_sound(); + throw exception( "no audio device found" ); + } + } + ~allegro42_sound_raii() { + remove_sound(); + } +}; + +class allegro42_stream_raii : public write_buffers_polling_wrapper_int { +private: + allegro42_raii allegro; + allegro42_sound_raii allegro_sound; + AUDIOSTREAM * stream; + std::size_t bits; + std::size_t channels; + std::uint32_t period_frames; +private: + std::uint32_t round_up_power2(std::uint32_t x) + { + std::uint32_t result = 1; + while ( result < x ) { + result *= 2; + } + return result; + } +public: + allegro42_stream_raii( commandlineflags & flags, std::ostream & log ) + : write_buffers_polling_wrapper_int(flags) + , stream(NULL) + , bits(16) + , channels(flags.channels) + , period_frames(1024) + { + if ( flags.use_float ) { + throw exception( "floating point is unsupported" ); + } + if ( ( flags.channels != 1 ) && ( flags.channels != 2 ) ) { + throw exception( "only mono or stereo supported" ); + } + if ( flags.buffer == default_high ) { + flags.buffer = 1024 * 2 * 1000 / flags.samplerate; + } else if ( flags.buffer == default_low ) { + flags.buffer = 512 * 2 * 1000 / flags.samplerate; + } + if ( flags.period == default_high ) { + flags.period = 1024 / 2 * 1000 / flags.samplerate; + } else if ( flags.period == default_low ) { + flags.period = 512 / 2 * 1000 / flags.samplerate; + } + flags.apply_default_buffer_sizes(); + period_frames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); + set_queue_size_frames( period_frames ); + if ( flags.verbose ) { + log << "Allegro-4.2:" << std::endl; + log << " latency: " << flags.buffer << std::endl; + log << " period: " << flags.period << std::endl; + log << " frames per buffer: " << period_frames << std::endl; + log << " ui redraw: " << flags.ui_redraw_interval << std::endl; + } + stream = play_audio_stream( period_frames, 16, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 ); + if ( !stream ) { + bits = 8; + stream = play_audio_stream( period_frames, 8, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 ); + if ( !stream ) { + throw allegro42_exception(); + } + } + } + ~allegro42_stream_raii() { + if ( stream ) { + stop_audio_stream( stream ); + stream = NULL; + } + } +public: + bool forward_queue() override { + void * p = get_audio_stream_buffer( stream ); + if ( !p ) { + return false; + } + for ( std::size_t frame = 0; frame < period_frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + std::int16_t sample = pop_queue(); + if ( bits == 8 ) { + std::uint8_t u8sample = ( static_cast( sample ) + 0x8000u ) >> 8; + std::memcpy( reinterpret_cast( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint8_t ) ), &u8sample, sizeof( std::uint8_t ) ); + } else { + std::uint16_t u16sample = static_cast( sample ) + 0x8000u; + std::memcpy( reinterpret_cast( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint16_t ) ), &u16sample, sizeof( std::uint16_t ) ); + } + } + } + free_audio_stream_buffer( stream ); + return true; + } + bool unpause() override { + voice_start( stream->voice ); + return true; + } + bool pause() override { + voice_stop( stream->voice ); + return true; + } + bool sleep( int ms ) override { + rest( ms ); + return true; + } +}; + +static std::string show_allegro42_devices( std::ostream & /* log */ ) { + std::ostringstream devices; + devices << " allegro42:" << std::endl; + devices << " " << "0" << ": Default Device" << std::endl; + return devices.str(); +} + +} // namespace openmpt123 + +#endif // MPT_WITH_ALLEGRO42 + +#endif // OPENMPT123_ALLEGRO42_HPP diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp index 037931c86..bc5b75628 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp @@ -66,7 +66,7 @@ public: flac_metadata[0] = 0; } } - void write_metadata( std::map metadata ) { + void write_metadata( std::map metadata ) override { if ( called_init ) { return; } @@ -91,7 +91,7 @@ public: } FLAC__stream_encoder_set_metadata( encoder, flac_metadata, 1 ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { if ( !called_init ) { FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 ); called_init = true; @@ -113,7 +113,7 @@ public: } FLAC__stream_encoder_process_interleaved( encoder, interleaved_buffer.data(), static_cast( frames ) ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { if ( !called_init ) { FLAC__stream_encoder_init_file( encoder, filename.c_str(), NULL, 0 ); called_init = true; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp index 097d35c9a..cb44587bd 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp @@ -88,7 +88,7 @@ public: } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { if ( data_info.pchEndWrite - data_info.pchNext < static_cast( sizeof( float ) ) ) { @@ -101,7 +101,7 @@ public: } } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { if ( data_info.pchEndWrite - data_info.pchNext < static_cast( sizeof( std::int16_t ) ) ) { diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp index e598b8614..75065142e 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp @@ -205,7 +205,7 @@ private: } } public: - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { if ( interleaved ) { sampleBufFloat.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { @@ -218,7 +218,7 @@ public: write_frames( buffers, frames ); } } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { if ( interleaved ) { sampleBufInt.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { @@ -231,15 +231,15 @@ public: write_frames( buffers, frames ); } } - bool unpause() { + bool unpause() override { check_portaudio_error( Pa_StartStream( stream ) ); return true; } - bool pause() { + bool pause() override { check_portaudio_error( Pa_StopStream( stream ) ); return true; } - bool sleep( int ms ) { + bool sleep( int ms ) override { Pa_Sleep( ms ); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_pulseaudio.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_pulseaudio.hpp index 389a60af0..9650005fa 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_pulseaudio.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_pulseaudio.hpp @@ -117,7 +117,7 @@ private: } } public: - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { sampleBufFloat.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { @@ -126,7 +126,7 @@ public: } write_frames( sampleBufFloat.data(), frames ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { sampleBufInt.clear(); for ( std::size_t frame = 0; frame < frames; ++frame ) { for ( std::size_t channel = 0; channel < channels; ++channel ) { @@ -135,10 +135,10 @@ public: } write_frames( sampleBufInt.data(), frames ); } - bool unpause() { + bool unpause() override { return true; } - bool pause() { + bool pause() override { int error = 0; error = 0; if ( pa_simple_drain( stream, &error ) < 0 ) { @@ -146,7 +146,7 @@ public: } return true; } - bool sleep( int ms ) { + bool sleep( int ms ) override { pa_msleep( ms ); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_raw.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_raw.hpp index 25b2e2580..cb7d8d5f2 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_raw.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_raw.hpp @@ -30,10 +30,10 @@ public: ~raw_stream_raii() { return; } - void write_metadata( std::map /* metadata */ ) { + void write_metadata( std::map /* metadata */ ) override { return; } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { interleaved_float_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { @@ -42,7 +42,7 @@ public: } file.write( reinterpret_cast( interleaved_float_buffer.data() ), frames * buffers.size() * sizeof( float ) ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { interleaved_int_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp index da444f320..55d64e04a 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp @@ -105,21 +105,21 @@ private: } } public: - bool pause() { + bool pause() override { SDL_PauseAudio( 1 ); return true; } - bool unpause() { + bool unpause() override { SDL_PauseAudio( 0 ); return true; } - void lock() { + void lock() override { SDL_LockAudio(); } - void unlock() { + void unlock() override { SDL_UnlockAudio(); } - bool sleep( int ms ) { + bool sleep( int ms ) override { SDL_Delay( ms ); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp index eec273781..307d67ffe 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp @@ -132,21 +132,21 @@ private: } } public: - bool pause() { + bool pause() override { SDL_PauseAudioDevice( dev, 1 ); return true; } - bool unpause() { + bool unpause() override { SDL_PauseAudioDevice( dev, 0 ); return true; } - void lock() { + void lock() override { SDL_LockAudioDevice( dev ); } - void unlock() { + void unlock() override { SDL_UnlockAudioDevice( dev ); } - bool sleep( int ms ) { + bool sleep( int ms ) override { SDL_Delay( ms ); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sndfile.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sndfile.hpp index 533d2c278..8fc3156e9 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sndfile.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sndfile.hpp @@ -174,14 +174,14 @@ public: sf_close( sndfile ); sndfile = 0; } - void write_metadata( std::map metadata ) { + void write_metadata( std::map metadata ) override { write_metadata_field( SF_STR_TITLE, metadata[ "title" ] ); write_metadata_field( SF_STR_ARTIST, metadata[ "artist" ] ); write_metadata_field( SF_STR_DATE, metadata[ "date" ] ); write_metadata_field( SF_STR_COMMENT, metadata[ "message" ] ); write_metadata_field( SF_STR_SOFTWARE, append_software_tag( metadata[ "tracker" ] ) ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { interleaved_float_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { @@ -190,7 +190,7 @@ public: } sf_writef_float( sndfile, interleaved_float_buffer.data(), frames ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { interleaved_int_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_stdout.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_stdout.hpp index 6b18198f9..585ce91c2 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_stdout.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_stdout.hpp @@ -24,7 +24,7 @@ public: return; } public: - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { interleaved_float_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { @@ -33,7 +33,7 @@ public: } std::cout.write( reinterpret_cast( interleaved_float_buffer.data() ), interleaved_float_buffer.size() * sizeof( float ) ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { interleaved_int_buffer.clear(); for ( std::size_t frame = 0; frame < frames; frame++ ) { for ( std::size_t channel = 0; channel < buffers.size(); channel++ ) { diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp index baf7b99e2..e19a0640d 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp @@ -157,21 +157,21 @@ private: write_or_wait(); } public: - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { write_buffers( buffers, frames ); } - void write( const std::vector buffers, std::size_t frames ) { + void write( const std::vector buffers, std::size_t frames ) override { write_buffers( buffers, frames ); } - bool pause() { + bool pause() override { waveOutPause( waveout ); return true; } - bool unpause() { + bool unpause() override { waveOutRestart( waveout ); return true; } - bool sleep( int ms ) { + bool sleep( int ms ) override { Sleep( ms ); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h index 29d13b06c..dbe34cf23 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h @@ -9,10 +9,15 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN +struct int24; + + enum SampleFormatEnum { SampleFormatUnsigned8 = 8, // do not change value (for compatibility with old configuration settings) diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h index a10e54652..88c7a24d6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/Endianness.h" @@ -105,7 +107,7 @@ struct DecodeInt7 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return Clamp(static_cast(*inBuf), static_cast(-64), static_cast(63)) * 2; + return Clamp(mpt::byte_cast(*inBuf), static_cast(-64), static_cast(63)) * 2; } }; @@ -116,7 +118,7 @@ struct DecodeInt8 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return static_cast(*inBuf); + return mpt::byte_cast(*inBuf); } }; @@ -127,7 +129,7 @@ struct DecodeUint8 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return static_cast(int(static_cast(*inBuf)) - 128); + return static_cast(int(mpt::byte_cast(*inBuf)) - 128); } }; @@ -140,7 +142,7 @@ struct DecodeInt8Delta DecodeInt8Delta() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - delta += static_cast(*inBuf); + delta += mpt::byte_cast(*inBuf); return static_cast(delta); } }; @@ -153,7 +155,7 @@ struct DecodeInt16 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 2; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return (static_cast(inBuf[loByteIndex]) | (static_cast(inBuf[hiByteIndex]) << 8)) - offset; + return (mpt::byte_cast(inBuf[loByteIndex]) | (mpt::byte_cast(inBuf[hiByteIndex]) << 8)) - offset; } }; @@ -167,7 +169,7 @@ struct DecodeInt16Delta DecodeInt16Delta() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - delta += static_cast(inBuf[loByteIndex]) | (static_cast(inBuf[hiByteIndex]) << 8); + delta += mpt::byte_cast(inBuf[loByteIndex]) | (mpt::byte_cast(inBuf[hiByteIndex]) << 8); return static_cast(delta); } }; @@ -181,9 +183,9 @@ struct DecodeInt16Delta8 DecodeInt16Delta8() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - delta += static_cast(inBuf[0]); + delta += mpt::byte_cast(inBuf[0]); int16 result = delta & 0xFF; - delta += static_cast(inBuf[1]); + delta += mpt::byte_cast(inBuf[1]); result |= (delta << 8); return result; } @@ -197,7 +199,7 @@ struct DecodeInt24 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 3; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return ((static_cast(inBuf[loByteIndex]) << 8) | (static_cast(inBuf[midByteIndex]) << 16) | (static_cast(inBuf[hiByteIndex]) << 24)) - offset; + return ((mpt::byte_cast(inBuf[loByteIndex]) << 8) | (mpt::byte_cast(inBuf[midByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiByteIndex]) << 24)) - offset; } }; @@ -209,7 +211,7 @@ struct DecodeInt32 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return (static_cast(inBuf[loLoByteIndex]) | (static_cast(inBuf[loHiByteIndex]) << 8) | (static_cast(inBuf[hiLoByteIndex]) << 16) | (static_cast(inBuf[hiHiByteIndex]) << 24)) - offset; + return (mpt::byte_cast(inBuf[loLoByteIndex]) | (mpt::byte_cast(inBuf[loHiByteIndex]) << 8) | (mpt::byte_cast(inBuf[hiLoByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiHiByteIndex]) << 24)) - offset; } }; @@ -222,14 +224,14 @@ struct DecodeInt64 MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return (uint64(0) - | (static_cast(uint8(inBuf[b0])) << 0) - | (static_cast(uint8(inBuf[b1])) << 8) - | (static_cast(uint8(inBuf[b2])) << 16) - | (static_cast(uint8(inBuf[b3])) << 24) - | (static_cast(uint8(inBuf[b4])) << 32) - | (static_cast(uint8(inBuf[b5])) << 40) - | (static_cast(uint8(inBuf[b6])) << 48) - | (static_cast(uint8(inBuf[b7])) << 56) + | (static_cast(mpt::byte_cast(inBuf[b0])) << 0) + | (static_cast(mpt::byte_cast(inBuf[b1])) << 8) + | (static_cast(mpt::byte_cast(inBuf[b2])) << 16) + | (static_cast(mpt::byte_cast(inBuf[b3])) << 24) + | (static_cast(mpt::byte_cast(inBuf[b4])) << 32) + | (static_cast(mpt::byte_cast(inBuf[b5])) << 40) + | (static_cast(mpt::byte_cast(inBuf[b6])) << 48) + | (static_cast(mpt::byte_cast(inBuf[b7])) << 56) ) - offset; } }; @@ -242,7 +244,7 @@ struct DecodeFloat32 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return IEEE754binary32LE(static_cast(inBuf[loLoByteIndex]), static_cast(inBuf[loHiByteIndex]), static_cast(inBuf[hiLoByteIndex]), static_cast(inBuf[hiHiByteIndex])); + return IEEE754binary32LE(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex]); } }; @@ -255,7 +257,7 @@ struct DecodeScaledFloat32 float factor; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return factor * IEEE754binary32LE(static_cast(inBuf[loLoByteIndex]), static_cast(inBuf[loHiByteIndex]), static_cast(inBuf[hiLoByteIndex]), static_cast(inBuf[hiHiByteIndex])); + return factor * IEEE754binary32LE(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex]); } MPT_FORCEINLINE DecodeScaledFloat32(float scaleFactor) : factor(scaleFactor) @@ -272,7 +274,7 @@ struct DecodeFloat64 static MPT_CONSTEXPR11_VAR std::size_t input_inc = 8; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { - return IEEE754binary64LE(uint8(inBuf[b0]), uint8(inBuf[b1]), uint8(inBuf[b2]), uint8(inBuf[b3]), uint8(inBuf[b4]), uint8(inBuf[b5]), uint8(inBuf[b6]), uint8(inBuf[b7])); + return IEEE754binary64LE(inBuf[b0], inBuf[b1], inBuf[b2], inBuf[b3], inBuf[b4], inBuf[b5], inBuf[b6], inBuf[b7]); } }; @@ -787,7 +789,7 @@ struct Convert typedef double output_t; MPT_FORCEINLINE output_t operator() (input_t val) { - return val; + return static_cast(val); } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h index 958385dec..85906b237 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/Endianness.h" #include "SampleFormatConverters.h" @@ -79,7 +81,7 @@ void CopyInterleavedSampleStreams(typename SampleConversion::output_t * MPT_REST template void ConvertInterleavedFixedPointToInterleaved(Tsample * MPT_RESTRICT p, const Tfixed * MPT_RESTRICT mixbuffer, std::size_t channels, std::size_t count) { - SC::ConvertFixedPoint conv; + SC::ConvertFixedPoint conv; count *= channels; for(std::size_t i = 0; i < count; ++i) { @@ -90,7 +92,7 @@ void ConvertInterleavedFixedPointToInterleaved(Tsample * MPT_RESTRICT p, const T template void ConvertInterleavedFixedPointToNonInterleaved(Tsample * const * const MPT_RESTRICT buffers, const Tfixed * MPT_RESTRICT mixbuffer, std::size_t channels, std::size_t count) { - SC::ConvertFixedPoint conv; + SC::ConvertFixedPoint conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.cpp index 5051c5763..fac3408a4 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.cpp @@ -10,7 +10,6 @@ #include "stdafx.h" -#include "../soundlib/Sndfile.h" #include "../sounddsp/AGC.h" diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.h index 4c50ea6f2..f9537a00f 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/AGC.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp index b9103c38a..228236d9e 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp @@ -10,7 +10,6 @@ #include "stdafx.h" -#include "../soundlib/Sndfile.h" #include "../sounddsp/DSP.h" #include @@ -72,9 +71,9 @@ static void ShelfEQ(int32 scale, b1 = ((beta1 + rho*beta0) * quad); a1 = - ((rho + alpha) * quad); - outA1 = Util::Round(a1 * scale); - outB0 = Util::Round(b0 * scale); - outB1 = Util::Round(b1 * scale); + outA1 = mpt::saturate_round(a1 * scale); + outB0 = mpt::saturate_round(b0 * scale); + outB1 = mpt::saturate_round(b1 * scale); } diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h index 0b9361df3..982ba6a1f 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp index 9a998d5a3..1b39297fe 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp @@ -10,7 +10,6 @@ #include "stdafx.h" -#include "../soundlib/Sndfile.h" #include "../soundlib/MixerLoops.h" #include "../sounddsp/EQ.h" @@ -201,7 +200,7 @@ mainloop: #endif // ENABLE_X86_AMD -#ifdef ENABLE_SSE +#if defined(ENABLE_X86) && defined(ENABLE_SSE) static void SSE_StereoEQ(EQBANDSTRUCT *pbl, EQBANDSTRUCT *pbr, float32 *pbuffer, UINT nCount) { @@ -294,7 +293,7 @@ done:; } } -#endif // ENABLE_SSE +#endif // ENABLE_X86 && ENABLE_SSE #if MPT_COMPILER_MSVC #pragma warning(pop) @@ -333,7 +332,7 @@ void CEQ::ProcessMono(int *pbuffer, float *MixFloatBuffer, UINT nCount) void CEQ::ProcessStereo(int *pbuffer, float *MixFloatBuffer, UINT nCount) { -#ifdef ENABLE_SSE +#if defined(ENABLE_X86) && defined(ENABLE_SSE) if(GetProcSupport() & PROCSUPPORT_SSE) { @@ -354,7 +353,7 @@ void CEQ::ProcessStereo(int *pbuffer, float *MixFloatBuffer, UINT nCount) } else -#endif // ENABLE_SSE +#endif // ENABLE_X86 && ENABLE_SSE #ifdef ENABLE_X86_AMD @@ -395,10 +394,6 @@ void CEQ::ProcessStereo(int *pbuffer, float *MixFloatBuffer, UINT nCount) CEQ::CEQ() { - #if defined(ENABLE_SSE) || defined(ENABLE_X86_AMD) - MPT_ASSERT_ALWAYS(((uintptr_t)&(gEQ[0])) % 4 == 0); - MPT_ASSERT_ALWAYS(((uintptr_t)&(gEQ[1])) % 4 == 0); - #endif // ENABLE_SSE || ENABLE_X86_AMD memcpy(gEQ, gEQDefaults, sizeof(gEQ)); } diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.h index 0fd073a04..229681d84 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + #include "../soundlib/Mixer.h" // For MIXBUFFERSIZE OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.cpp index ce7817fd9..c6d4ba160 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.cpp @@ -55,38 +55,11 @@ static MPT_FORCEINLINE void Store64SSE(int32 *dst, __m128i src) { return _mm_sto static MPT_FORCEINLINE void Store64SSE(LR16 *dst, __m128i src) { return _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), src); } #endif -CReverbSettings::CReverbSettings() -{ - m_nReverbType = 0; - m_nReverbDepth = 8; // 50% -} - CReverb::CReverb() { - m_currentPreset = nullptr; - // Shared reverb state InitMixBuffer(MixReverbBuffer, static_cast(mpt::size(MixReverbBuffer))); - gnRvbROfsVol = 0; - gnRvbLOfsVol = 0; - - gnReverbSend = 0; - - gnReverbSamples = 0; - gnReverbDecaySamples = 0; - - // Internal reverb state - g_bLastInPresent = 0; - g_bLastOutPresent = 0; - g_nLastRvbIn_xl = 0; - g_nLastRvbIn_xr = 0; - g_nLastRvbIn_yl = 0; - g_nLastRvbIn_yr = 0; - g_nLastRvbOut_xl = 0; - g_nLastRvbOut_xr = 0; - MemsetZero(gnDCRRvb_Y1); - MemsetZero(gnDCRRvb_X1); // Reverb mix buffers MemsetZero(g_RefDelay); @@ -102,7 +75,7 @@ static int32 OnePoleLowPassCoef(int32 scale, float g, float F_c, float F_s) g *= g; double scale_over_1mg = scale / (1.0 - g); double cosw = std::cos(2.0 * M_PI * F_c / F_s); - return Util::Round((1.0 - (std::sqrt((g + g) * (1.0 - cosw) - g * g * (1.0 - cosw * cosw)) + g * cosw)) * scale_over_1mg); + return mpt::saturate_round((1.0 - (std::sqrt((g + g) * (1.0 - cosw) - g * g * (1.0 - cosw * cosw)) + g * cosw)) * scale_over_1mg); } static float mBToLinear(int32 value_mB) @@ -116,7 +89,7 @@ static float mBToLinear(int32 value_mB) static int32 mBToLinear(int32 scale, int32 value_mB) { - return Util::Round(mBToLinear(value_mB) * scale); + return mpt::saturate_round(mBToLinear(value_mB) * scale); } @@ -137,46 +110,46 @@ struct SNDMIX_REVERB_PROPERTIES struct SNDMIX_RVBPRESET { SNDMIX_REVERB_PROPERTIES Preset; - const char *name; + const MPT_UCHAR_TYPE *name; }; -static SNDMIX_RVBPRESET gRvbPresets[NUM_REVERBTYPES] = +static const SNDMIX_RVBPRESET gRvbPresets[NUM_REVERBTYPES] = { - {{ SNDMIX_REVERB_PRESET_PLATE }, "GM Plate"}, - {{ SNDMIX_REVERB_PRESET_SMALLROOM }, "GM Small Room"}, - {{ SNDMIX_REVERB_PRESET_MEDIUMROOM }, "GM Medium Room"}, - {{ SNDMIX_REVERB_PRESET_LARGEROOM }, "GM Large Room"}, - {{ SNDMIX_REVERB_PRESET_MEDIUMHALL }, "GM Medium Hall"}, - {{ SNDMIX_REVERB_PRESET_LARGEHALL }, "GM Large Hall"}, - {{ SNDMIX_REVERB_PRESET_GENERIC }, "Generic"}, - {{ SNDMIX_REVERB_PRESET_PADDEDCELL }, "Padded Cell"}, - {{ SNDMIX_REVERB_PRESET_ROOM }, "Room"}, - {{ SNDMIX_REVERB_PRESET_BATHROOM }, "Bathroom"}, - {{ SNDMIX_REVERB_PRESET_LIVINGROOM }, "Living Room"}, - {{ SNDMIX_REVERB_PRESET_STONEROOM }, "Stone Room"}, - {{ SNDMIX_REVERB_PRESET_AUDITORIUM }, "Auditorium"}, - {{ SNDMIX_REVERB_PRESET_CONCERTHALL }, "Concert Hall"}, - {{ SNDMIX_REVERB_PRESET_CAVE }, "Cave"}, - {{ SNDMIX_REVERB_PRESET_ARENA }, "Arena"}, - {{ SNDMIX_REVERB_PRESET_HANGAR }, "Hangar"}, - {{ SNDMIX_REVERB_PRESET_CARPETEDHALLWAY }, "Carpeted Hallway"}, - {{ SNDMIX_REVERB_PRESET_HALLWAY }, "Hallway"}, - {{ SNDMIX_REVERB_PRESET_STONECORRIDOR }, "Stone Corridor"}, - {{ SNDMIX_REVERB_PRESET_ALLEY }, "Alley"}, - {{ SNDMIX_REVERB_PRESET_FOREST }, "Forest"}, - {{ SNDMIX_REVERB_PRESET_CITY }, "City"}, - {{ SNDMIX_REVERB_PRESET_MOUNTAINS }, "Mountains"}, - {{ SNDMIX_REVERB_PRESET_QUARRY }, "Quarry"}, - {{ SNDMIX_REVERB_PRESET_PLAIN }, "Plain"}, - {{ SNDMIX_REVERB_PRESET_PARKINGLOT }, "Parking Lot"}, - {{ SNDMIX_REVERB_PRESET_SEWERPIPE }, "Sewer Pipe"}, - {{ SNDMIX_REVERB_PRESET_UNDERWATER }, "Underwater"}, + {{ SNDMIX_REVERB_PRESET_PLATE }, UL_("GM Plate")}, + {{ SNDMIX_REVERB_PRESET_SMALLROOM }, UL_("GM Small Room")}, + {{ SNDMIX_REVERB_PRESET_MEDIUMROOM }, UL_("GM Medium Room")}, + {{ SNDMIX_REVERB_PRESET_LARGEROOM }, UL_("GM Large Room")}, + {{ SNDMIX_REVERB_PRESET_MEDIUMHALL }, UL_("GM Medium Hall")}, + {{ SNDMIX_REVERB_PRESET_LARGEHALL }, UL_("GM Large Hall")}, + {{ SNDMIX_REVERB_PRESET_GENERIC }, UL_("Generic")}, + {{ SNDMIX_REVERB_PRESET_PADDEDCELL }, UL_("Padded Cell")}, + {{ SNDMIX_REVERB_PRESET_ROOM }, UL_("Room")}, + {{ SNDMIX_REVERB_PRESET_BATHROOM }, UL_("Bathroom")}, + {{ SNDMIX_REVERB_PRESET_LIVINGROOM }, UL_("Living Room")}, + {{ SNDMIX_REVERB_PRESET_STONEROOM }, UL_("Stone Room")}, + {{ SNDMIX_REVERB_PRESET_AUDITORIUM }, UL_("Auditorium")}, + {{ SNDMIX_REVERB_PRESET_CONCERTHALL }, UL_("Concert Hall")}, + {{ SNDMIX_REVERB_PRESET_CAVE }, UL_("Cave")}, + {{ SNDMIX_REVERB_PRESET_ARENA }, UL_("Arena")}, + {{ SNDMIX_REVERB_PRESET_HANGAR }, UL_("Hangar")}, + {{ SNDMIX_REVERB_PRESET_CARPETEDHALLWAY }, UL_("Carpeted Hallway")}, + {{ SNDMIX_REVERB_PRESET_HALLWAY }, UL_("Hallway")}, + {{ SNDMIX_REVERB_PRESET_STONECORRIDOR }, UL_("Stone Corridor")}, + {{ SNDMIX_REVERB_PRESET_ALLEY }, UL_("Alley")}, + {{ SNDMIX_REVERB_PRESET_FOREST }, UL_("Forest")}, + {{ SNDMIX_REVERB_PRESET_CITY }, UL_("City")}, + {{ SNDMIX_REVERB_PRESET_MOUNTAINS }, UL_("Mountains")}, + {{ SNDMIX_REVERB_PRESET_QUARRY }, UL_("Quarry")}, + {{ SNDMIX_REVERB_PRESET_PLAIN }, UL_("Plain")}, + {{ SNDMIX_REVERB_PRESET_PARKINGLOT }, UL_("Parking Lot")}, + {{ SNDMIX_REVERB_PRESET_SEWERPIPE }, UL_("Sewer Pipe")}, + {{ SNDMIX_REVERB_PRESET_UNDERWATER }, UL_("Underwater")}, }; -const char *GetReverbPresetName(uint32 nPreset) +mpt::ustring GetReverbPresetName(uint32 nPreset) { - return (nPreset < NUM_REVERBTYPES) ? gRvbPresets[nPreset].name : nullptr; + return (nPreset < NUM_REVERBTYPES) ? mpt::ustring(gRvbPresets[nPreset].name) : mpt::ustring(); } ////////////////////////////////////////////////////////////////////////// diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h index 1b4c08da6..8a777ea55 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #ifndef NO_REVERB #include "../soundlib/Mixer.h" // For MIXBUFFERSIZE @@ -21,7 +23,7 @@ OPENMPT_NAMESPACE_BEGIN #define NUM_REVERBTYPES 29 -const char *GetReverbPresetName(uint32 nPreset); +mpt::ustring GetReverbPresetName(uint32 nPreset); ///////////////////////////////////////////////////////////////////////////// // @@ -128,10 +130,8 @@ struct EnvironmentReverb class CReverbSettings { public: - uint32 m_nReverbDepth; - uint32 m_nReverbType; -public: - CReverbSettings(); + uint32 m_nReverbDepth = 8; // 50% + uint32 m_nReverbType = 0; }; @@ -144,27 +144,27 @@ public: private: mixsample_t MixReverbBuffer[MIXBUFFERSIZE * 2]; public: - mixsample_t gnRvbROfsVol, gnRvbLOfsVol; + mixsample_t gnRvbROfsVol = 0, gnRvbLOfsVol = 0; private: - const SNDMIX_REVERB_PROPERTIES *m_currentPreset; + const SNDMIX_REVERB_PROPERTIES *m_currentPreset = nullptr; - uint32 gnReverbSend; + uint32 gnReverbSend = 0; - uint32 gnReverbSamples; - uint32 gnReverbDecaySamples; + uint32 gnReverbSamples = 0; + uint32 gnReverbDecaySamples = 0; // Internal reverb state - bool g_bLastInPresent; - bool g_bLastOutPresent; - int g_nLastRvbIn_xl; - int g_nLastRvbIn_xr; - int g_nLastRvbIn_yl; - int g_nLastRvbIn_yr; - int g_nLastRvbOut_xl; - int g_nLastRvbOut_xr; - int32 gnDCRRvb_Y1[2]; - int32 gnDCRRvb_X1[2]; + bool g_bLastInPresent = 0; + bool g_bLastOutPresent = 0; + int g_nLastRvbIn_xl = 0; + int g_nLastRvbIn_xr = 0; + int g_nLastRvbIn_yl = 0; + int g_nLastRvbIn_yr = 0; + int g_nLastRvbOut_xl = 0; + int g_nLastRvbOut_xr = 0; + int32 gnDCRRvb_Y1[2] = { 0, 0 }; + int32 gnDCRRvb_X1[2] = { 0, 0 }; // Reverb mix buffers SWRvbRefDelay g_RefDelay; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h index 5ce530078..8735e46a1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN #if defined(MODPLUG_TRACKER) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h index 042cb730a..e5da84af8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h @@ -7,9 +7,10 @@ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ - #pragma once +#include "BuildSettings.h" + #include "Sndfile.h" #include "Dither.h" #include "../soundbase/SampleFormat.h" @@ -41,10 +42,9 @@ public: { MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat).IsValid()); } - virtual ~AudioReadTargetBuffer() { } std::size_t GetRenderedCount() const { return countRendered; } public: - virtual void DataCallback(int *MixSoundBuffer, std::size_t channels, std::size_t countChunk) + void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { // Convert to output sample format and optionally perform dithering and clipping if needed @@ -94,7 +94,7 @@ public: { MPT_ASSERT_ALWAYS(sampleFormat.IsValid()); } - virtual void DataCallback(int *MixSoundBuffer, std::size_t channels, std::size_t countChunk) + void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { switch(sampleFormat.value) { @@ -146,17 +146,89 @@ public: }; +class AudioSourceBuffer + : public IAudioSource +{ +private: + std::size_t countRendered; +protected: + SampleFormat sampleFormat; + const void *inputBuffer; +public: + AudioSourceBuffer(SampleFormat sampleFormat, const void *buffer) + : countRendered(0) + , sampleFormat(sampleFormat) + , inputBuffer(buffer) + { + MPT_ASSERT(sampleFormat.IsValid()); + } + virtual ~AudioSourceBuffer() { } + std::size_t GetRenderedCount() const { return countRendered; } +private: + template + void Fill(const Tsample *inputBuffer, int32 * const *MixInputBuffers, std::size_t channels, std::size_t countChunk) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + SC::ConvertToFixedPoint conv; + for(std::size_t frame = 0; frame < countChunk; ++frame) + { + MixInputBuffers[channel][frame] = conv(inputBuffer[channel + ((countRendered + frame) * channels)]); + } + } + countRendered += countChunk; + } +public: + virtual void FillCallback(int32 * const *MixInputBuffers, std::size_t channels, std::size_t countChunk) + { + switch(sampleFormat.value) + { + case SampleFormatUnsigned8: + { + typedef SampleFormatToType::type Tsample; + Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); + } + break; + case SampleFormatInt16: + { + typedef SampleFormatToType::type Tsample; + Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); + } + break; + case SampleFormatInt24: + { + typedef SampleFormatToType::type Tsample; + Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); + } + break; + case SampleFormatInt32: + { + typedef SampleFormatToType::type Tsample; + Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); + } + break; + case SampleFormatFloat32: + { + typedef SampleFormatToType::type Tsample; + Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); + } + break; + } + } +}; + + #else // !MODPLUG_TRACKER template -void ApplyGainBeforeConversionIfAppropriate(int *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) +void ApplyGainBeforeConversionIfAppropriate(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) { // Apply final output gain for non floating point output - ApplyGain(MixSoundBuffer, channels, countChunk, Util::Round(gainFactor * (1<<16))); + ApplyGain(MixSoundBuffer, channels, countChunk, mpt::saturate_round(gainFactor * (1<<16))); } template<> -void ApplyGainBeforeConversionIfAppropriate(int * /*MixSoundBuffer*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) +void ApplyGainBeforeConversionIfAppropriate(int32 * /*MixSoundBuffer*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) { // nothing } @@ -188,9 +260,8 @@ public: { return; } - virtual ~AudioReadTargetGainBuffer() { } public: - virtual void DataCallback(int *MixSoundBuffer, std::size_t channels, std::size_t countChunk) + void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { const std::size_t countRendered_ = Tbase::GetRenderedCount(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h b/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h new file mode 100644 index 000000000..e4f52d3b3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h @@ -0,0 +1,81 @@ +/* + * BitReader.h + * ----------- + * Purpose: An extended FileReader to read bit-oriented rather than byte-oriented streams. + * Notes : The current implementation can only read bit widths up to 32 bits, and it always + * reads bits starting from the least significant bit, as this is all that is + * required by the class users at the moment. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "../common/FileReader.h" +#include + + +OPENMPT_NAMESPACE_BEGIN + + +//================================== +class BitReader : private FileReader +//================================== +{ +protected: + off_t m_bufPos = 0, m_bufSize = 0; + uint32 bitBuf = 0; // Current bit buffer + int m_bitNum = 0; // Currently available number of bits + mpt::byte buffer[mpt::IO::BUFFERSIZE_TINY]; + +public: + + class eof : public std::range_error + { + public: + eof() : std::range_error("Truncated bit buffer") { } + }; + + BitReader(mpt::span bytedata) : FileReader(bytedata) { } + BitReader(const FileReader &other = FileReader()) : FileReader(other) { } + + off_t GetLength() const + { + return FileReader::GetLength(); + } + + off_t GetPosition() const + { + return FileReader::GetPosition() - m_bufSize + m_bufPos; + } + + uint32 ReadBits(int numBits) + { + while(m_bitNum < numBits) + { + // Fetch more bits + if(m_bufPos >= m_bufSize) + { + m_bufSize = ReadRaw(buffer, sizeof(buffer)); + m_bufPos = 0; + if(!m_bufSize) + { + throw eof(); + } + } + bitBuf |= (static_cast(buffer[m_bufPos++]) << m_bitNum); + m_bitNum += 8; + } + + uint32 v = bitBuf & ((1 << numBits) - 1); + bitBuf >>= numBits; + m_bitNum -= numBits; + return v; + } +}; + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ChunkReader.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ChunkReader.h index a51c73c84..0e6378f7a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ChunkReader.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ChunkReader.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/FileReader.h" #include diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Container.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Container.h index 4760d88b8..19dd67ca5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Container.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Container.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/FileReader.h" #include diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp index 76a6412d3..92450b4c3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp @@ -14,8 +14,7 @@ #include "../common/FileReader.h" #include "Container.h" #include "Sndfile.h" - -#include +#include "BitReader.h" OPENMPT_NAMESPACE_BEGIN @@ -73,40 +72,9 @@ MPT_BINARY_STRUCT(MMCMPSUBBLOCK, 8) #define MMCMP_ABS16 0x0200 #define MMCMP_ENDIAN 0x0400 -struct MMCMPBITBUFFER -{ - uint32 bitcount; - uint32 bitbuffer; - const uint8 *pSrc; - uint32 bytesLeft; - - uint32 GetBits(uint32 nBits); -}; - - -uint32 MMCMPBITBUFFER::GetBits(uint32 nBits) -{ - uint32 d; - if (!nBits) return 0; - while (bitcount < 24) - { - if(bytesLeft) - { - bitbuffer |= *pSrc << bitcount; - pSrc++; - bytesLeft--; - } - bitcount += 8; - } - d = bitbuffer & ((1 << nBits) - 1); - bitbuffer >>= nBits; - bitcount -= nBits; - return d; -} - static const uint8 MMCMP8BitCommands[8] = { - 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 + 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 }; static const uint8 MMCMP8BitFetch[8] = @@ -116,7 +84,7 @@ static const uint8 MMCMP8BitFetch[8] = static const uint16 MMCMP16BitCommands[16] = { - 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, + 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0 }; @@ -278,7 +246,6 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C // Data is 16-bit packed if (blk.flags & MMCMP_16BIT) { - MMCMPBITBUFFER bb; uint32 subblk = 0; if(!psubblk) return false; if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; @@ -294,70 +261,72 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C if (pblk->flags & MMCMP_ABS16) Log("ABS16 "); Log("\n"); #endif - bb.bitcount = 0; - bb.bitbuffer = 0; if(!file.Seek(memPos + blk.tt_entries)) return false; if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false; - bb.pSrc = file.GetRawData(); - bb.bytesLeft = blk.pk_size - blk.tt_entries; - while (subblk < blk.sub_blk) - { - uint32 newval = 0x10000; - uint32 d = bb.GetBits(numbits+1); + BitReader bitFile{ file.GetChunk(blk.pk_size - blk.tt_entries) }; - uint32 command = MMCMP16BitCommands[numbits & 0x0F]; - if (d >= command) + try + { + while (subblk < blk.sub_blk) { - uint32 nFetch = MMCMP16BitFetch[numbits & 0x0F]; - uint32 newbits = bb.GetBits(nFetch) + ((d - command) << nFetch); - if (newbits != numbits) + uint32 newval = 0x10000; + uint32 d = bitFile.ReadBits(numbits + 1); + + uint32 command = MMCMP16BitCommands[numbits & 0x0F]; + if (d >= command) { - numbits = newbits & 0x0F; - } else - { - if ((d = bb.GetBits(4)) == 0x0F) + uint32 nFetch = MMCMP16BitFetch[numbits & 0x0F]; + uint32 newbits = bitFile.ReadBits(nFetch) + ((d - command) << nFetch); + if (newbits != numbits) { - if (bb.GetBits(1)) break; - newval = 0xFFFF; + numbits = newbits & 0x0F; } else { - newval = 0xFFF0 + d; + if ((d = bitFile.ReadBits(4)) == 0x0F) + { + if (bitFile.ReadBits(1)) break; + newval = 0xFFFF; + } else + { + newval = 0xFFF0 + d; + } } - } - } else - { - newval = d; - } - if (newval < 0x10000) - { - newval = (newval & 1) ? (uint32)(-(int32)((newval+1) >> 1)) : (uint32)(newval >> 1); - if (blk.flags & MMCMP_DELTA) - { - newval += oldval; - oldval = newval; } else - if (!(blk.flags & MMCMP_ABS16)) { - newval ^= 0x8000; + newval = d; + } + if (newval < 0x10000) + { + newval = (newval & 1) ? (uint32)(-(int32)((newval+1) >> 1)) : (uint32)(newval >> 1); + if (blk.flags & MMCMP_DELTA) + { + newval += oldval; + oldval = newval; + } else + if (!(blk.flags & MMCMP_ABS16)) + { + newval ^= 0x8000; + } + pDest[dwPos + 0] = (uint8)(((uint16)newval) & 0xFF); + pDest[dwPos + 1] = (uint8)(((uint16)newval) >> 8); + dwPos += 2; + } + if (dwPos >= dwSize) + { + subblk++; + dwPos = 0; + if(!(subblk < blk.sub_blk)) break; + if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; + dwSize = psubblk[subblk].unpk_size; + pDest = &(unpackedData[psubblk[subblk].unpk_pos]); } - pDest[dwPos + 0] = (uint8)(((uint16)newval) & 0xFF); - pDest[dwPos + 1] = (uint8)(((uint16)newval) >> 8); - dwPos += 2; - } - if (dwPos >= dwSize) - { - subblk++; - dwPos = 0; - if(!(subblk < blk.sub_blk)) break; - if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; - dwSize = psubblk[subblk].unpk_size; - pDest = &(unpackedData[psubblk[subblk].unpk_pos]); } + } catch(const BitReader::eof &) + { } } else // Data is 8-bit packed { - MMCMPBITBUFFER bb; uint32 subblk = 0; if(!psubblk) return false; if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; @@ -371,58 +340,61 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C || file.ReadRaw(ptable, blk.tt_entries) < blk.tt_entries) return false; - bb.bitcount = 0; - bb.bitbuffer = 0; if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false; - bb.pSrc = file.GetRawData(); - bb.bytesLeft = blk.pk_size - blk.tt_entries; - while (subblk < blk.sub_blk) - { - uint32 newval = 0x100; - uint32 d = bb.GetBits(numbits+1); + BitReader bitFile{ file.GetChunk(blk.pk_size - blk.tt_entries) }; - uint32 command = MMCMP8BitCommands[numbits & 0x07]; - if (d >= command) + try + { + while (subblk < blk.sub_blk) { - uint32 nFetch = MMCMP8BitFetch[numbits & 0x07]; - uint32 newbits = bb.GetBits(nFetch) + ((d - command) << nFetch); - if (newbits != numbits) + uint32 newval = 0x100; + uint32 d = bitFile.ReadBits(numbits + 1); + + uint32 command = MMCMP8BitCommands[numbits & 0x07]; + if (d >= command) { - numbits = newbits & 0x07; - } else - { - if ((d = bb.GetBits(3)) == 7) + uint32 nFetch = MMCMP8BitFetch[numbits & 0x07]; + uint32 newbits = bitFile.ReadBits(nFetch) + ((d - command) << nFetch); + if (newbits != numbits) { - if (bb.GetBits(1)) break; - newval = 0xFF; + numbits = newbits & 0x07; } else { - newval = 0xF8 + d; + if ((d = bitFile.ReadBits(3)) == 7) + { + if (bitFile.ReadBits(1)) break; + newval = 0xFF; + } else + { + newval = 0xF8 + d; + } } - } - } else - { - newval = d; - } - if (newval < sizeof(ptable)) - { - int n = ptable[newval]; - if (blk.flags & MMCMP_DELTA) + } else { - n += oldval; - oldval = n; + newval = d; + } + if (newval < sizeof(ptable)) + { + int n = ptable[newval]; + if (blk.flags & MMCMP_DELTA) + { + n += oldval; + oldval = n; + } + pDest[dwPos++] = (uint8)n; + } + if (dwPos >= dwSize) + { + subblk++; + dwPos = 0; + if(!(subblk < blk.sub_blk)) break; + if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; + dwSize = psubblk[subblk].unpk_size; + pDest = &(unpackedData[psubblk[subblk].unpk_pos]); } - pDest[dwPos++] = (uint8)n; - } - if (dwPos >= dwSize) - { - subblk++; - dwPos = 0; - if(!(subblk < blk.sub_blk)) break; - if(!MMCMP_IsDstBlockValid(unpackedData, psubblk[subblk])) return false; - dwSize = psubblk[subblk].unpk_size; - pDest = &(unpackedData[psubblk[subblk].unpk_pos]); } + } catch(const BitReader::eof &) + { } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp index 8d2869eb5..286d68411 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp @@ -21,9 +21,6 @@ OPENMPT_NAMESPACE_BEGIN -//#define MMCMP_LOG - - struct PPBITBUFFER { uint32 bitcount; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp index a5c4bfe0c..9ec76941e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp @@ -18,20 +18,6 @@ OPENMPT_NAMESPACE_BEGIN -static bool ValidateHeader(const UMXFileHeader &fileHeader) -{ - if(std::memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) - || fileHeader.nameCount == 0 - || fileHeader.exportCount == 0 - || fileHeader.importCount == 0 - ) - { - return false; - } - return true; -} - - CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize) { UMXFileHeader fileHeader; @@ -39,7 +25,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUMX(MemoryFileReader file, co { return ProbeWantMoreData; } - if(!ValidateHeader(fileHeader)) + if(!fileHeader.IsValid()) { return ProbeFailure; } @@ -62,7 +48,7 @@ bool UnpackUMX(std::vector &containerItems, FileReader &file, Con { return false; } - if(!ValidateHeader(fileHeader)) + if(!fileHeader.IsValid()) { return false; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp index a04d67172..c09bcc45e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp @@ -27,11 +27,11 @@ mpt::ustring Dither::GetModeName(DitherMode mode) { switch(mode) { - case DitherNone : return MPT_USTRING("no" ); break; - case DitherDefault: return MPT_USTRING("default"); break; - case DitherModPlug: return MPT_USTRING("0.5 bit"); break; - case DitherSimple : return MPT_USTRING("1 bit" ); break; - default : return MPT_USTRING("" ); break; + case DitherNone : return U_("no" ); break; + case DitherDefault: return U_("default"); break; + case DitherModPlug: return U_("0.5 bit"); break; + case DitherSimple : return U_("1 bit" ); break; + default : return U_("" ); break; } } @@ -43,7 +43,7 @@ mpt::ustring Dither::GetModeName(DitherMode mode) #ifdef ENABLE_X86 -void X86_Dither(int *pBuffer, uint32 nSamples, uint32 nBits, DitherModPlugState *state) +void X86_Dither(int32 *pBuffer, uint32 nSamples, uint32 nBits, DitherModPlugState *state) { if(nBits + MIXING_ATTENUATION + 1 >= 32) //if(nBits>16) { @@ -102,7 +102,7 @@ static MPT_FORCEINLINE int32 dither_rand(uint32 &a, uint32 &b) return static_cast(b); } -static void C_Dither(int *pBuffer, std::size_t count, uint32 nBits, DitherModPlugState *state) +static void C_Dither(int32 *pBuffer, std::size_t count, uint32 nBits, DitherModPlugState *state) { if(nBits + MIXING_ATTENUATION + 1 >= 32) //if(nBits>16) { @@ -126,7 +126,7 @@ static void C_Dither(int *pBuffer, std::size_t count, uint32 nBits, DitherModPlu } -static void Dither_ModPlug(int *pBuffer, std::size_t count, std::size_t channels, uint32 nBits, DitherModPlugState &state) +static void Dither_ModPlug(int32 *pBuffer, std::size_t count, std::size_t channels, uint32 nBits, DitherModPlugState &state) { #ifdef ENABLE_X86 X86_Dither(pBuffer, count * channels, nBits, &state); @@ -139,7 +139,7 @@ static void Dither_ModPlug(int *pBuffer, std::size_t count, std::size_t channels template struct Dither_SimpleTemplate { -MPT_NOINLINE void operator () (int *mixbuffer, std::size_t count, DitherSimpleState &state, mpt::fast_prng &prng) +MPT_NOINLINE void operator () (int32 *mixbuffer, std::size_t count, DitherSimpleState &state, mpt::fast_prng &prng) { STATIC_ASSERT(sizeof(int) == 4); const int rshift = (32-targetbits) - MIXING_ATTENUATION; @@ -181,7 +181,7 @@ MPT_NOINLINE void operator () (int *mixbuffer, std::size_t count, DitherSimpleSt } }; -static void Dither_Simple(int *mixbuffer, std::size_t count, std::size_t channels, int bits, DitherSimpleState &state, mpt::fast_prng &prng) +static void Dither_Simple(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits, DitherSimpleState &state, mpt::fast_prng &prng) { switch(bits) { @@ -249,7 +249,7 @@ DitherMode Dither::GetMode() const } -void Dither::Process(int *mixbuffer, std::size_t count, std::size_t channels, int bits) +void Dither::Process(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits) { switch(mode) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h index 9ba6ac0c2..db83780d8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/mptRandom.h" @@ -82,7 +84,7 @@ public: void SetMode(DitherMode mode_); DitherMode GetMode() const; void Reset(); - void Process(int *mixbuffer, std::size_t count, std::size_t channels, int bits); + void Process(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits); static mpt::ustring GetModeName(DitherMode mode); }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp index 5a8d17226..381b91801 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp @@ -12,11 +12,11 @@ #include "stdafx.h" #include "Sndfile.h" #ifdef MODPLUG_TRACKER -#include "../mptrack/mptrack.h" +#include "../mptrack/Mptrack.h" #include "../common/mptFileIO.h" #endif #include "Dlsbank.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" #include "../common/FileReader.h" #include "../common/Endianness.h" #include "SampleIO.h" @@ -186,7 +186,11 @@ MPT_BINARY_STRUCT(IFFCHUNK, 8) struct RIFFCHUNKID { uint32le id_RIFF; - uint32le riff_len; + union + { + uint32le riff_len; + uint32be riff_len_be; + }; uint32le id_DLS; }; @@ -508,45 +512,46 @@ CDLSBank::CDLSBank() bool CDLSBank::IsDLSBank(const mpt::PathString &filename) { RIFFCHUNKID riff; - FILE *f; if(filename.empty()) return false; - if((f = mpt_fopen(filename, "rb")) == nullptr) return false; + mpt::ifstream f(filename, std::ios::binary); + if(!f) + { + return false; + } MemsetZero(riff); - fread(&riff, sizeof(RIFFCHUNKID), 1, f); + mpt::IO::Read(f, riff); // Check for embedded DLS sections if (riff.id_RIFF == IFFID_FORM) { // Miles Sound System do { - uint32 len = riff.riff_len; - len = SwapBytesBE(len); + uint32 len = riff.riff_len_be; if (len <= 4) break; if (riff.id_DLS == IFFID_XDLS) { - fread(&riff, sizeof(RIFFCHUNKID), 1, f); + mpt::IO::Read(f, riff); break; } if((len % 2u) != 0) len++; - if (fseek(f, len-4, SEEK_CUR) != 0) break; - } while (fread(&riff, sizeof(RIFFCHUNKID), 1, f) != 0); + if (!mpt::IO::SeekRelative(f, len-4)) break; + } while (mpt::IO::Read(f, riff)); } else if ((riff.id_RIFF == IFFID_RIFF) && (riff.id_DLS == IFFID_RMID)) { for (;;) { - if(!fread(&riff, sizeof(RIFFCHUNKID), 1, f)) + if(!mpt::IO::Read(f, riff)) break; if (riff.id_DLS == IFFID_DLS) break; // found it int len = riff.riff_len; if((len % 2u) != 0) len++; - if ((len <= 4) || (fseek(f, len-4, SEEK_CUR) != 0)) break; + if ((len <= 4) || !mpt::IO::SeekRelative(f, len-4)) break; } } - fclose(f); return ((riff.id_RIFF == IFFID_RIFF) && ((riff.id_DLS == IFFID_DLS) || (riff.id_DLS == IFFID_MLS) || (riff.id_DLS == IFFID_sfbk)) && (riff.riff_len >= 256)); @@ -556,47 +561,74 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename) /////////////////////////////////////////////////////////////// // Find an instrument based on the given parameters -DLSINSTRUMENT *CDLSBank::FindInstrument(bool bDrum, uint32 nBank, uint32 dwProgram, uint32 dwKey, uint32 *pInsNo) +const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 program, uint32 key, uint32 *pInsNo) const { if (m_Instruments.empty()) return NULL; for (uint32 iIns=0; iInsulBank & 0x7F00) >> 1) | (pDlsIns->ulBank & 0x7F); - if ((nBank >= 0x4000) || (insbank == nBank)) + const DLSINSTRUMENT &dlsIns = m_Instruments[iIns]; + uint32 insbank = ((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F); + if ((bank >= 0x4000) || (insbank == bank)) { - if (bDrum) + if (isDrum) { - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + if (dlsIns.ulBank & F_INSTRUMENT_DRUMS) { - if ((dwProgram >= 0x80) || (dwProgram == (pDlsIns->ulInstrument & 0x7F))) + if ((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { - for (uint32 iRgn=0; iRgnnRegions; iRgn++) + for (uint32 iRgn=0; iRgn= 0x80) - || ((dwKey >= pDlsIns->Regions[iRgn].uKeyMin) - && (dwKey <= pDlsIns->Regions[iRgn].uKeyMax))) + if ((!key) || (key >= 0x80) + || ((key >= dlsIns.Regions[iRgn].uKeyMin) + && (key <= dlsIns.Regions[iRgn].uKeyMax))) { if (pInsNo) *pInsNo = iIns; - return pDlsIns; + return &dlsIns; } } } } } else { - if (!(pDlsIns->ulBank & F_INSTRUMENT_DRUMS)) + if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS)) { - if ((dwProgram >= 0x80) || (dwProgram == (pDlsIns->ulInstrument & 0x7F))) + if ((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F))) { if (pInsNo) *pInsNo = iIns; - return pDlsIns; + return &dlsIns; } } } } } - return NULL; + return nullptr; +} + + +bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum) const +{ + ModInstrument *pIns = sndFile.Instruments[ins]; + if(pIns == nullptr) + return false; + + uint32 dlsIns = 0, drumRgn = 0; + const uint32 program = (pIns->nMidiProgram != 0) ? pIns->nMidiProgram - 1 : 0; + const uint32 key = isDrum ? (pIns->nMidiDrumKey & 0x7F) : 0xFF; + if(FindInstrument(isDrum, (pIns->wMidiBank - 1) & 0x3FFF, program, key, &dlsIns) + || FindInstrument(isDrum, 0xFFFF, isDrum ? 0xFF : program, key, &dlsIns)) + { + if(key < 0x80) drumRgn = GetRegionFromKey(dlsIns, key); + if(ExtractInstrument(sndFile, ins, dlsIns, drumRgn)) + { + pIns = sndFile.Instruments[ins]; // Reset pointer because ExtractInstrument may delete the previous value. + if((key >= 24) && (key < 24 + mpt::size(szMidiPercussionNames))) + { + mpt::String::CopyN(pIns->name, szMidiPercussionNames[key - 24]); + } + return true; + } + } + return false; } @@ -705,7 +737,7 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK if (pDlsIns->nRegions >= DLSMAXREGIONS) break; } else { - pDlsIns->nMelodicEnv = m_Envelopes.size() + 1; + pDlsIns->nMelodicEnv = static_cast(m_Envelopes.size() + 1); } if (p->cbSize+p->cConnectionBlocks*sizeof(CONNECTIONBLOCK) > p->len) break; DLSENVELOPE dlsEnv; @@ -815,7 +847,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade case IFFID_phdr: if (m_Instruments.empty()) { - uint32 numIns = chunk.GetLength() / sizeof(SFPRESETHEADER); + uint32 numIns = static_cast(chunk.GetLength() / sizeof(SFPRESETHEADER)); if(numIns <= 1) break; // The terminal sfPresetHeader record should never be accessed, and exists only to provide a terminal wPresetBagNdx with which to determine the number of zones in the last preset. @@ -843,7 +875,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade case IFFID_pbag: if (!m_Instruments.empty()) { - uint32 nBags = chunk.GetLength() / sizeof(SFPRESETBAG); + uint32 nBags = static_cast(chunk.GetLength() / sizeof(SFPRESETBAG)); if (nBags) { sf2info.nPresetBags = nBags; @@ -858,7 +890,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade case IFFID_pgen: if (!m_Instruments.empty()) { - uint32 nGens = chunk.GetLength() / sizeof(SFGENLIST); + uint32 nGens = static_cast(chunk.GetLength() / sizeof(SFGENLIST)); if (nGens) { sf2info.nPresetGens = nGens; @@ -873,7 +905,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade case IFFID_inst: if (!m_Instruments.empty()) { - uint32 nIns = chunk.GetLength() / sizeof(SFINST); + uint32 nIns = static_cast(chunk.GetLength() / sizeof(SFINST)); sf2info.nInsts = nIns; sf2info.pInsts = reinterpret_cast(chunk.GetRawData()); } @@ -882,7 +914,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade case IFFID_ibag: if (!m_Instruments.empty()) { - uint32 nBags = chunk.GetLength() / sizeof(SFINSTBAG); + uint32 nBags = static_cast(chunk.GetLength() / sizeof(SFINSTBAG)); if (nBags) { sf2info.nInstBags = nBags; @@ -894,7 +926,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade case IFFID_igen: if (!m_Instruments.empty()) { - uint32 nGens = chunk.GetLength() / sizeof(SFINSTGENLIST); + uint32 nGens = static_cast(chunk.GetLength() / sizeof(SFINSTGENLIST)); if (nGens) { sf2info.nInstGens = nGens; @@ -906,7 +938,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade case IFFID_shdr: if (m_SamplesEx.empty()) { - uint32 numSmp = chunk.GetLength() / sizeof(SFSAMPLE); + uint32 numSmp = static_cast(chunk.GetLength() / sizeof(SFSAMPLE)); if (numSmp < 1) break; m_SamplesEx.resize(numSmp); m_WaveForms.resize(numSmp); @@ -1025,7 +1057,7 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LOADERINFO &sf2info) if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS)) { m_Envelopes.push_back(dlsEnv); - dlsIns.nMelodicEnv = m_Envelopes.size(); + dlsIns.nMelodicEnv = static_cast(m_Envelopes.size()); } // Load Instrument Bags if ((!nInstrNdx) || (nInstrNdx >= sf2info.nInsts) || (!sf2info.pInsts)) continue; @@ -1161,8 +1193,8 @@ bool CDLSBank::Open(FileReader file) file.Rewind(); const uint8 *lpMemFile = file.GetRawData(); - uint32 dwMemLength = file.GetLength(); - uint32 dwMemPos = 0; + size_t dwMemLength = file.GetLength(); + size_t dwMemPos = 0; if(!file.CanRead(256)) { return false; @@ -1198,7 +1230,7 @@ bool CDLSBank::Open(FileReader file) file.ReadStruct(riff); break; } - uint32 len = SwapBytesBE(riff.riff_len); + uint32 len = riff.riff_len_be; if((len % 2u) != 0) len++; file.SkipBack(4); @@ -1391,15 +1423,13 @@ bool CDLSBank::Open(FileReader file) //////////////////////////////////////////////////////////////////////////////////////// // Extracts the WaveForms from a DLS bank -uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) +uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const { - DLSINSTRUMENT *pDlsIns; - if (nIns >= m_Instruments.size()) return 0; - pDlsIns = &m_Instruments[nIns]; - for (uint32 rgn=0; rgnnRegions; rgn++) + const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; + for (uint32 rgn = 0; rgn < dlsIns.nRegions; rgn++) { - if ((nKey >= pDlsIns->Regions[rgn].uKeyMin) && (nKey <= pDlsIns->Regions[rgn].uKeyMax)) + if ((nKey >= dlsIns.Regions[rgn].uKeyMin) && (nKey <= dlsIns.Regions[rgn].uKeyMax)) { return rgn; } @@ -1408,7 +1438,7 @@ uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) } -bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &waveData, uint32 &length) +bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &waveData, uint32 &length) const { waveData.clear(); length = 0; @@ -1420,7 +1450,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav #endif return false; } - DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; + const DLSINSTRUMENT &dlsIns = m_Instruments[nIns]; if (nRgn >= dlsIns.nRegions) { #ifdef DLSBANK_LOG @@ -1437,22 +1467,25 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav return false; } - uint32 dwOffset = m_WaveForms[nWaveLink] + m_dwWavePoolOffset; - FILE *f = mpt_fopen(m_szFileName, "rb"); - if(f == nullptr) return false; - if (fseek(f, dwOffset, SEEK_SET) == 0) + long dwOffset = mpt::saturate_cast(m_WaveForms[nWaveLink] + m_dwWavePoolOffset); + mpt::ifstream f(m_szFileName, std::ios::binary); + if(!f) + { + return false; + } + if (mpt::IO::SeekAbsolute(f, dwOffset)) { if (m_nType & SOUNDBANK_TYPE_SF2) { if (m_SamplesEx[nWaveLink].dwLen) { - if (fseek(f, 8, SEEK_CUR) == 0) + if (mpt::IO::SeekRelative(f, 8)) { length = m_SamplesEx[nWaveLink].dwLen; try { waveData.assign(length + 8, 0); - fread(waveData.data(), 1, length, f); + mpt::IO::ReadRaw(f, waveData.data(), length); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); @@ -1462,7 +1495,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav } else { LISTCHUNK chunk; - if (fread(&chunk, 1, 12, f) == 12) + if (mpt::IO::Read(f, chunk)) { if ((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4)) { @@ -1471,7 +1504,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav { waveData.assign(chunk.len + 8, 0); memcpy(waveData.data(), &chunk, 12); - fread(&waveData[12], 1, length - 12, f); + mpt::IO::ReadRaw(f, &waveData[12], length - 12); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); @@ -1480,20 +1513,18 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav } } } - fclose(f); return !waveData.empty(); } -bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose) +bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose) const { - DLSINSTRUMENT *pDlsIns; std::vector pWaveForm; uint32 dwLen = 0; bool bOk, bWaveForm; if (nIns >= m_Instruments.size()) return false; - pDlsIns = &m_Instruments[nIns]; + const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns]; if (nRgn >= pDlsIns->nRegions) return false; if (!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) return false; if (dwLen < 16) return false; @@ -1508,7 +1539,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI if (sndFile.m_nSamples < nSample) sndFile.m_nSamples = nSample; if (nWaveLink < m_SamplesEx.size()) { - DLSSAMPLEEX *p = &m_SamplesEx[nWaveLink]; + const DLSSAMPLEEX &p = m_SamplesEx[nWaveLink]; #ifdef DLSINSTR_LOG Log(" SF2 WaveLink #%3d: %5dHz\n", nWaveLink, p->dwSampleRate); #endif @@ -1516,11 +1547,11 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI sample.nLength = dwLen / 2; sample.nLoopStart = pDlsIns->Regions[nRgn].ulLoopStart; sample.nLoopEnd = pDlsIns->Regions[nRgn].ulLoopEnd; - sample.nC5Speed = p->dwSampleRate; - sample.RelativeTone = p->byOriginalPitch; - sample.nFineTune = p->chPitchCorrection; - if (p->szName[0]) - mpt::String::Copy(sndFile.m_szNames[nSample], p->szName); + sample.nC5Speed = p.dwSampleRate; + sample.RelativeTone = p.byOriginalPitch; + sample.nFineTune = p.chPitchCorrection; + if (p.szName[0]) + mpt::String::Copy(sndFile.m_szNames[nSample], p.szName); else if(pDlsIns->szName[0]) mpt::String::Copy(sndFile.m_szNames[nSample], pDlsIns->szName); @@ -1532,7 +1563,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI SampleIO::signedPCM) .ReadSample(sample, chunk); } - bWaveForm = sample.pSample != nullptr; + bWaveForm = sample.HasSampleData(); } else { FileReader file(mpt::as_span(pWaveForm.data(), dwLen)); @@ -1543,23 +1574,23 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI if (bWaveForm) { ModSample &sample = sndFile.GetSample(nSample); - DLSREGION *pRgn = &pDlsIns->Regions[nRgn]; + const DLSREGION &rgn = pDlsIns->Regions[nRgn]; sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); - if (pRgn->fuOptions & DLSREGION_SAMPLELOOP) sample.uFlags.set(CHN_LOOP); - if (pRgn->fuOptions & DLSREGION_SUSTAINLOOP) sample.uFlags.set(CHN_SUSTAINLOOP); - if (pRgn->fuOptions & DLSREGION_PINGPONGLOOP) sample.uFlags.set(CHN_PINGPONGLOOP); + if (rgn.fuOptions & DLSREGION_SAMPLELOOP) sample.uFlags.set(CHN_LOOP); + if (rgn.fuOptions & DLSREGION_SUSTAINLOOP) sample.uFlags.set(CHN_SUSTAINLOOP); + if (rgn.fuOptions & DLSREGION_PINGPONGLOOP) sample.uFlags.set(CHN_PINGPONGLOOP); if (sample.uFlags[CHN_LOOP | CHN_SUSTAINLOOP]) { - if (pRgn->ulLoopEnd > pRgn->ulLoopStart) + if (rgn.ulLoopEnd > rgn.ulLoopStart) { if (sample.uFlags[CHN_SUSTAINLOOP]) { - sample.nSustainStart = pRgn->ulLoopStart; - sample.nSustainEnd = pRgn->ulLoopEnd; + sample.nSustainStart = rgn.ulLoopStart; + sample.nSustainEnd = rgn.ulLoopEnd; } else { - sample.nLoopStart = pRgn->ulLoopStart; - sample.nLoopEnd = pRgn->ulLoopEnd; + sample.nLoopStart = rgn.ulLoopStart; + sample.nLoopEnd = rgn.ulLoopEnd; } } else { @@ -1568,12 +1599,12 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI } // WSMP chunk { - uint32 usUnityNote = pRgn->uUnityNote; - int sFineTune = pRgn->sFineTune; - int lVolume = pRgn->usVolume; + uint32 usUnityNote = rgn.uUnityNote; + int sFineTune = rgn.sFineTune; + int lVolume = rgn.usVolume; WSMPCHUNK wsmp; - if(!(pRgn->fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.ReadStructPartial(wsmp)) + if(!(rgn.fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.ReadStructPartial(wsmp)) { usUnityNote = wsmp.usUnityNote; sFineTune = wsmp.sFineTune; @@ -1618,19 +1649,17 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI static uint16 ScaleEnvelope(uint32 time, float tempoScale) { - return std::max(Util::Round(time * tempoScale), 1); + return std::max(mpt::saturate_round(time * tempoScale), 1); } -bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) +bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const { SAMPLEINDEX RgnToSmp[DLSMAXREGIONS]; - DLSINSTRUMENT *pDlsIns; - ModInstrument *pIns; uint32 nRgnMin, nRgnMax, nEnv; if (nIns >= m_Instruments.size()) return false; - pDlsIns = &m_Instruments[nIns]; + const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns]; if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { if (nDrumRgn >= pDlsIns->nRegions) return false; @@ -1650,7 +1679,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui Log(" %2d regions, nMelodicEnv=%d\n", pDlsIns->nRegions, pDlsIns->nMelodicEnv); for (uint32 iDbg=0; iDbgnRegions; iDbg++) { - DLSREGION *prgn = &pDlsIns->Regions[iDbg]; + const DLSREGION *prgn = &pDlsIns->Regions[iDbg]; Log(" Region %d:\n", iDbg); Log(" WaveLink = %d (loop [%5d, %5d])\n", prgn->nWaveLink, prgn->ulLoopStart, prgn->ulLoopEnd); Log(" Key Range: [%2d, %2d]\n", prgn->uKeyMin, prgn->uKeyMax); @@ -1659,7 +1688,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui } #endif - pIns = new (std::nothrow) ModInstrument(); + ModInstrument *pIns = new (std::nothrow) ModInstrument(); if(pIns == nullptr) { return false; @@ -1725,12 +1754,12 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui { bool bDupRgn = false; SAMPLEINDEX nSmp = 0; - DLSREGION *pRgn = &pDlsIns->Regions[nRgn]; + const DLSREGION *pRgn = &pDlsIns->Regions[nRgn]; // Elimitate Duplicate Regions uint32 iDup; for (iDup=nRgnMin; iDupRegions[iDup]; + const DLSREGION *pRgn2 = &pDlsIns->Regions[iDup]; if (((pRgn2->nWaveLink == pRgn->nWaveLink) && (pRgn2->ulLoopEnd == pRgn->ulLoopEnd) && (pRgn2->ulLoopStart == pRgn->ulLoopStart)) @@ -1771,7 +1800,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui } } // Load the sample - if(!bDupRgn || sndFile.GetSample(nSmp).pSample == nullptr) + if(!bDupRgn || !sndFile.GetSample(nSmp).HasSampleData()) { ExtractSample(sndFile, nSmp, nIns, nRgn, nTranspose); } else if(sndFile.GetSample(nSmp).GetNumChannels() == 1) @@ -1788,7 +1817,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui { SmpLength len = sample.nLength; const int16 *src = reinterpret_cast(pWaveForm.data()); - int16 *dst = sample.pSample16 + ((pan1 == 0) ? 0 : 1); + int16 *dst = sample.sample16() + ((pan1 == 0) ? 0 : 1); while(len--) { *dst = *src; @@ -1813,7 +1842,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui // Initializes Envelope if ((nEnv) && (nEnv <= m_Envelopes.size())) { - DLSENVELOPE *part = &m_Envelopes[nEnv-1]; + const DLSENVELOPE *part = &m_Envelopes[nEnv-1]; // Volume Envelope if ((part->wVolAttack) || (part->wVolDecay < 20*50) || (part->nVolSustainLevel) || (part->wVolRelease < 20*50)) { @@ -1824,8 +1853,8 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui pIns->VolEnv.clear(); if (part->wVolAttack) { - pIns->VolEnv.push_back(0, (uint8)(ENVELOPE_MAX / (part->wVolAttack / 2 + 2) + 8)); // /----- - pIns->VolEnv.push_back(ScaleEnvelope(part->wVolAttack, tempoScale), ENVELOPE_MAX); // | + pIns->VolEnv.push_back(0, (uint8)(ENVELOPE_MAX / (part->wVolAttack / 2 + 2) + 8)); // /----- + pIns->VolEnv.push_back(ScaleEnvelope(part->wVolAttack, tempoScale), ENVELOPE_MAX); // | } else { pIns->VolEnv.push_back(0, ENVELOPE_MAX); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h index 01adb84d7..8b85ef8fc 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN class CSoundFile; OPENMPT_NAMESPACE_END @@ -102,11 +104,11 @@ class CDLSBank protected: SOUNDBANKINFO m_BankInfo; mpt::PathString m_szFileName; + size_t m_dwWavePoolOffset; uint32 m_nType; - uint32 m_dwWavePoolOffset; // DLS Information uint32 m_nMaxWaveLink; - std::vector m_WaveForms; + std::vector m_WaveForms; std::vector m_Instruments; std::vector m_SamplesEx; std::vector m_Envelopes; @@ -127,12 +129,13 @@ public: public: uint32 GetNumInstruments() const { return static_cast(m_Instruments.size()); } uint32 GetNumSamples() const { return static_cast(m_WaveForms.size()); } - DLSINSTRUMENT *GetInstrument(uint32 iIns) { return iIns < m_Instruments.size() ? &m_Instruments[iIns] : nullptr; } - DLSINSTRUMENT *FindInstrument(bool bDrum, uint32 nBank=0xFF, uint32 dwProgram=0xFF, uint32 dwKey=0xFF, uint32 *pInsNo=nullptr); - uint32 GetRegionFromKey(uint32 nIns, uint32 nKey); - bool ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &waveData, uint32 &length); - bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose=0); - bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn); + const DLSINSTRUMENT *GetInstrument(uint32 iIns) const { return iIns < m_Instruments.size() ? &m_Instruments[iIns] : nullptr; } + const DLSINSTRUMENT *FindInstrument(bool isDrum, uint32 bank = 0xFF, uint32 program = 0xFF, uint32 key = 0xFF, uint32 *pInsNo = nullptr) const; + bool FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum) const; + uint32 GetRegionFromKey(uint32 nIns, uint32 nKey) const; + bool ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &waveData, uint32 &length) const; + bool ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose = 0) const; + bool ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const; const char *GetRegionName(uint32 nIns, uint32 nRgn) const; uint8 GetPanning(uint32 ins, uint32 region) const; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp index e9d337021..40fd9a2be 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp @@ -252,9 +252,9 @@ struct MixLoopState } } - Limit(nSmpCount, 1u, nSamples); + Limit(nSmpCount, uint32(1u), nSamples); -#ifdef _DEBUG +#ifdef MPT_BUILD_DEBUG { SmpLength posDest = (nPos + nInc * (nSmpCount - 1)).GetUInt(); if (posDest < 0 || posDest > chn.nLength) @@ -373,26 +373,40 @@ void CSoundFile::CreateStereoMix(int count) chn.nROfs = chn.nLOfs = 0; pbuffer += nSmpCount * 2; naddmix = 0; - } else + } +#ifdef MODPLUG_TRACKER + else if(m_SamplePlayLengths != nullptr) + { + // Detecting the longest play time for each sample for optimization + chn.position += chn.increment * nSmpCount; + size_t smp = std::distance(Samples, chn.pModSample); + if(smp < m_SamplePlayLengths->size()) + { + m_SamplePlayLengths->at(smp) = std::max(m_SamplePlayLengths->at(smp), chn.position.GetUInt()); + } + } +#endif + else { // Do mixing mixsample_t *pbufmax = pbuffer + (nSmpCount * 2); - chn.nROfs = - *(pbufmax-2); - chn.nLOfs = - *(pbufmax-1); + chn.nROfs = -*(pbufmax - 2); + chn.nLOfs = -*(pbufmax - 1); -#ifdef _DEBUG +#ifdef MPT_BUILD_DEBUG SamplePosition targetpos = chn.position + chn.increment * nSmpCount; #endif MixFuncTable::Functions[functionNdx | (chn.nRampLength ? MixFuncTable::ndxRamp : 0)](chn, m_Resampler, pbuffer, nSmpCount); -#ifdef _DEBUG +#ifdef MPT_BUILD_DEBUG MPT_ASSERT(chn.position.GetUInt() == targetpos.GetUInt()); #endif - chn.nROfs += *(pbufmax-2); - chn.nLOfs += *(pbufmax-1); + chn.nROfs += *(pbufmax - 2); + chn.nLOfs += *(pbufmax - 1); pbuffer = pbufmax; naddmix = 1; } + nsamples -= nSmpCount; if (chn.nRampLength) { @@ -422,7 +436,7 @@ void CSoundFile::CreateStereoMix(int count) // Test case: PTInstrSwap.mod const ModSample &smp = Samples[chn.nNewIns]; chn.pModSample = &smp; - chn.pCurrentSample = smp.pSample; + chn.pCurrentSample = smp.samplev(); chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | smp.uFlags; chn.nLength = smp.uFlags[CHN_LOOP] ? smp.nLoopEnd : smp.nLength; chn.nLoopStart = smp.nLoopStart; @@ -543,7 +557,7 @@ void CSoundFile::ProcessPlugins(uint32 nCount) if(!plugin.IsMasterEffect() && !plugin.pMixPlugin->ShouldProcessSilence() && !(plugin.pMixPlugin->m_MixState.dwFlags & SNDMIXPLUGINSTATE::psfHasInput)) { // If plugin has no inputs and isn't a master plugin, we shouldn't let it process silence if possible. - // I have yet to encounter a plugin which actually sets this flag. + // I have yet to encounter a VST plugin which actually sets this flag. bool hasInput = false; for(PLUGINDEX inPlug = 0; inPlug < plug; inPlug++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/FloatMixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/FloatMixer.h index 442c837a1..8f9301ff5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/FloatMixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/FloatMixer.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "MixerInterface.h" #include "Resampler.h" @@ -45,7 +47,7 @@ struct LinearInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t fract = posLo / static_cast(0x100000000); //CResampler::LinearTablef[posLo >> 24]; for(int i = 0; i < Traits::numChannelsIn; i++) @@ -67,7 +69,7 @@ struct FastSincInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t *lut = CResampler::FastSincTablef + ((posLo >> 22) & 0x3FC); for(int i = 0; i < Traits::numChannelsIn; i++) @@ -97,7 +99,7 @@ struct PolyphaseInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t *lut = sinc + ((posLo >> (32 - SINC_PHASES_BITS)) & SINC_MASK) * SINC_WIDTH; for(int i = 0; i < Traits::numChannelsIn; i++) @@ -130,7 +132,7 @@ struct FIRFilterInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t * const lut = WFIRlut + ((((posLo >> 16) + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK); for(int i = 0; i < Traits::numChannelsIn; i++) @@ -294,7 +296,7 @@ struct ResonantFilter MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); for(int i = 0; i < Traits::numChannelsIn; i++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp index 07edc81ce..ddf509b0a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp @@ -26,12 +26,20 @@ struct IT16BitParams typedef int16 sample_t; static const int16 lowerTab[]; static const int16 upperTab[]; - static const int8 fetchA = 4, lowerB = -8, upperB = 7, defWidth = 17; - static const int mask = 0xFFFF; + static const int8 fetchA; + static const int8 lowerB; + static const int8 upperB; + static const int8 defWidth; + static const int mask; }; const int16 IT16BitParams::lowerTab[] = { 0, -1, -3, -7, -15, -31, -56, -120, -248, -504, -1016, -2040, -4088, -8184, -16376, -32760, -32768 }; const int16 IT16BitParams::upperTab[] = { 0, 1, 3, 7, 15, 31, 55, 119, 247, 503, 1015, 2039, 4087, 8183, 16375, 32759, 32767 }; +const int8 IT16BitParams::fetchA = 4; +const int8 IT16BitParams::lowerB = -8; +const int8 IT16BitParams::upperB = 7; +const int8 IT16BitParams::defWidth = 17; +const int IT16BitParams::mask = 0xFFFF; // Algorithm parameters for 8-Bit samples struct IT8BitParams @@ -39,12 +47,20 @@ struct IT8BitParams typedef int8 sample_t; static const int8 lowerTab[]; static const int8 upperTab[]; - static const int8 fetchA = 3, lowerB = -4, upperB = 3, defWidth = 9; - static const int mask = 0xFF; + static const int8 fetchA; + static const int8 lowerB; + static const int8 upperB; + static const int8 defWidth; + static const int mask; }; const int8 IT8BitParams::lowerTab[] = { 0, -1, -3, -7, -15, -31, -60, -124, -128 }; const int8 IT8BitParams::upperTab[] = { 0, 1, 3, 7, 15, 31, 59, 123, 127 }; +const int8 IT8BitParams::fetchA = 3; +const int8 IT8BitParams::lowerB = -4; +const int8 IT8BitParams::upperB = 3; +const int8 IT8BitParams::defWidth = 9; +const int IT8BitParams::mask = 0xFF; static const int8 ITWidthChangeSize[] = { 4, 5, 6, 7, 8, 9, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; @@ -80,9 +96,9 @@ ITCompression::ITCompression(const ModSample &sample, bool it215, std::ostream * byteVal = 0; if(mptSample.GetElementarySampleSize() > 1) - Compress(sample.pSample16 + chn, offset, remain); + Compress(sample.sample16() + chn, offset, remain); else - Compress(sample.pSample8 + chn, offset, remain); + Compress(sample.sample8() + chn, offset, remain); if(file) mpt::IO::WriteRaw(*file, packedData, packedLength); packedTotalLength += packedLength; @@ -137,17 +153,15 @@ void ITCompression::Compress(const void *data, SmpLength offset, SmpLength actua Deltafy(); } - const int8 defWidth = Properties::defWidth; // gcc static const member reference workaround - // Initialise bit width table with initial values - bwt.assign(baseLength, defWidth); + bwt.assign(baseLength, Properties::defWidth); // Recurse! - SquishRecurse(defWidth, defWidth, defWidth, defWidth - 2, 0, baseLength); + SquishRecurse(Properties::defWidth, Properties::defWidth, Properties::defWidth, Properties::defWidth - 2, 0, baseLength); // Write those bits! const typename Properties::sample_t *p = static_cast(sampleData); - int8 width = defWidth; + int8 width = Properties::defWidth; for(size_t i = 0; i < baseLength; i++) { if(bwt[i] != width) @@ -158,7 +172,7 @@ void ITCompression::Compress(const void *data, SmpLength offset, SmpLength actua MPT_ASSERT(width); WriteBits(width, (1 << (width - 1))); WriteBits(Properties::fetchA, ConvertWidth(width, bwt[i])); - } else if(width < defWidth) + } else if(width < Properties::defWidth) { // Mode B: 7 to 8 / 16 bits int xv = (1 << (width - 1)) + Properties::lowerB + ConvertWidth(width, bwt[i]); @@ -319,21 +333,25 @@ ITDecompression::ITDecompression(FileReader &file, ModSample &sample, bool it215 writtenSamples = writePos = 0; while(writtenSamples < sample.nLength && file.CanRead(sizeof(uint16))) { - dataSize = file.ReadUint16LE(); - if(!dataSize) + uint16 compressedSize = file.ReadUint16LE(); + if(!compressedSize) continue; // Malformed sample? - file.ReadRaw(chunk, dataSize); + bitFile = file.ReadChunk(compressedSize); // Initialise bit reader - dataPos = 0; - bitPos = 0; - remBits = 8; mem1 = mem2 = 0; - if(mptSample.GetElementarySampleSize() > 1) - Uncompress(mptSample.pSample16 + chn); - else - Uncompress(mptSample.pSample8 + chn); + try + { + if(mptSample.GetElementarySampleSize() > 1) + Uncompress(mptSample.sample16() + chn); + else + Uncompress(mptSample.sample8() + chn); + } catch(const BitReader::eof &) + { + // Data is not sufficient to decode the block + //AddToLog(LogWarning, "Truncated IT sample block"); + } } } } @@ -344,27 +362,25 @@ void ITDecompression::Uncompress(typename Properties::sample_t *target) { curLength = std::min(mptSample.nLength - writtenSamples, SmpLength(ITCompression::blockSize / sizeof(typename Properties::sample_t))); - const int defWidth = Properties::defWidth; // gcc static const member reference workaround - - int width = defWidth; + int width = Properties::defWidth; while(curLength > 0) { - if(width < 1 || width > defWidth || dataPos >= dataSize) + if(width > Properties::defWidth) { // Error! return; } - int v = ReadBits(width); + int v = bitFile.ReadBits(width); const int topBit = (1 << (width - 1)); if(width <= 6) { // Mode A: 1 to 6 bits if(v == topBit) - ChangeWidth(width, ReadBits(Properties::fetchA)); + ChangeWidth(width, bitFile.ReadBits(Properties::fetchA)); else Write(v, topBit, target); - } else if(width < defWidth) + } else if(width < Properties::defWidth) { // Mode B: 7 to 8 / 16 bits if(v >= topBit + Properties::lowerB && v <= topBit + Properties::upperB) @@ -383,11 +399,6 @@ void ITDecompression::Uncompress(typename Properties::sample_t *target) } -#if MPT_MSVC_AT_LEAST(2017,3) && MPT_MSVC_BEFORE(2017,5) -// Work-around compiler crash in VS2017.3 / cl 19.11.25506 -// https://developercommunity.visualstudio.com/content/problem/96687/c1063-and-c1001-while-compiling-trivial-code-in-vs.html -MPT_NOINLINE -#endif void ITDecompression::ChangeWidth(int &curWidth, int width) { width++; @@ -397,35 +408,6 @@ void ITDecompression::ChangeWidth(int &curWidth, int width) } -#if MPT_MSVC_AT_LEAST(2017,3) && MPT_MSVC_BEFORE(2017,5) -// Work-around compiler crash in VS2017.3 / cl 19.11.25506 -// https://developercommunity.visualstudio.com/content/problem/96687/c1063-and-c1001-while-compiling-trivial-code-in-vs.html -MPT_NOINLINE -#endif -int ITDecompression::ReadBits(int width) -{ - int v = 0, vPos = 0, vMask = (1 << width) - 1; - while(width >= remBits && dataPos < dataSize) - { - v |= (chunk[dataPos] >> bitPos) << vPos; - vPos += remBits; - width -= remBits; - dataPos++; - remBits = 8; - bitPos = 0; - } - - if(width > 0 && dataPos < dataSize) - { - v |= (chunk[dataPos] >> bitPos) << vPos; - v &= vMask; - remBits -= width; - bitPos += width; - } - return v; -} - - template void ITDecompression::Write(int v, int topBit, typename Properties::sample_t *target) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h index 07ce11086..ca0f9b4f2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h @@ -10,10 +10,12 @@ #pragma once +#include "BuildSettings.h" + #include #include #include "Snd_defs.h" -#include "../common/FileReader.h" +#include "BitReader.h" OPENMPT_NAMESPACE_BEGIN @@ -26,8 +28,8 @@ public: ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength = 0); size_t GetCompressedSize() const { return packedTotalLength; } - static const size_t bufferSize = 2 + 0xFFFF; // Our output buffer can't be longer than this. - static const size_t blockSize = 0x8000; // Block size (in bytes) in which samples are being processed + enum : size_t { bufferSize = 2 + 0xFFFF }; // Our output buffer can't be longer than this. + enum : size_t { blockSize = 0x8000 }; // Block size (in bytes) in which samples are being processed protected: std::vector bwt; // Bit width table for each sampling point @@ -73,26 +75,18 @@ public: ITDecompression(FileReader &file, ModSample &sample, bool it215); protected: + BitReader bitFile; ModSample &mptSample; // Sample that is being processed SmpLength writtenSamples; // Number of samples so far written on this channel SmpLength writePos; // Absolut write position in sample (for stereo samples) SmpLength curLength; // Length of currently processed block unsigned int mem1, mem2; // Integrator memory - int dataPos, dataSize; // Position in and size of input block - - // Bit reader - int bitPos; // Current bit position in this byte - int remBits; // Remaining bits in this byte - bool is215; // Use IT2.15 compression (double deltas) - mpt::byte chunk[65535]; // Input block - template void Uncompress(typename Properties::sample_t *target); static void ChangeWidth(int &curWidth, int width); - int ReadBits(int width); template void Write(int v, int topbit, typename Properties::sample_t *target); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp index 8c9823a11..56cce3e8c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp @@ -12,7 +12,7 @@ #include "Loaders.h" #include "ITTools.h" #include "Tables.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" #include "../common/version.h" @@ -112,8 +112,8 @@ void ITOldInstrument::ConvertToMPT(ModInstrument &mptIns) const mptIns.nPan = 128; // NNA Stuff - mptIns.nNNA = nna; - mptIns.nDCT = dnc; + mptIns.nNNA = static_cast(nna.get()); + mptIns.nDCT = static_cast(dnc.get()); // Sample Map for(size_t i = 0; i < 120; i++) @@ -168,7 +168,7 @@ uint32 ITInstrument::ConvertToIT(const ModInstrument &mptIns, bool compatExport, // Header memcpy(id, "IMPI", 4); - trkvers = 0x5000 | static_cast(MptVersion::num >> 16); + trkvers = 0x5000 | static_cast(Version::Current().GetRawVersion() >> 16); mpt::String::Write(filename, mptIns.filename); mpt::String::Write(name, mptIns.name); @@ -277,9 +277,9 @@ uint32 ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) cons mptIns.nPanSwing = std::min(rp, 64); // NNA Stuff - mptIns.nNNA = nna; - mptIns.nDCT = dct; - mptIns.nDNA = dca; + mptIns.nNNA = static_cast(nna.get()); + mptIns.nDCT = static_cast(dct.get()); + mptIns.nDNA = static_cast(dca.get()); // Pitch / Pan Separation mptIns.nPPS = pps; @@ -451,7 +451,7 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr if(mptSmp.uFlags[CHN_PANNING]) dfp |= ITSample::enablePanning; // Sample Format / Loop Flags - if(mptSmp.nLength && mptSmp.pSample) + if(mptSmp.HasSampleData()) { flags = ITSample::sampleDataPresent; if(mptSmp.uFlags[CHN_LOOP]) flags |= ITSample::sampleLoop; @@ -504,7 +504,11 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr vir = 255 - vir; } - if(mptSmp.uFlags[SMP_KEEPONDISK]) + if(mptSmp.uFlags[CHN_ADLIB]) + { + length = 12; + cvt = ITSample::cvtOPLInstrument; + } else if(mptSmp.uFlags[SMP_KEEPONDISK]) { #ifndef MPT_EXTERNAL_SAMPLES MPT_UNREFERENCED_PARAMETER(allowExternal); @@ -562,12 +566,16 @@ uint32 ITSample::ConvertToMPT(ModSample &mptSmp) const mptSmp.SanitizeLoops(); // Auto Vibrato settings - mptSmp.nVibType = AutoVibratoIT2XM[vit & 7]; + mptSmp.nVibType = static_cast(AutoVibratoIT2XM[vit & 7]); mptSmp.nVibRate = vis; mptSmp.nVibDepth = vid & 0x7F; mptSmp.nVibSweep = vir; - if(cvt == ITSample::cvtExternalSample) + if(cvt == ITSample::cvtOPLInstrument) + { + // FM instrument in MPTM + mptSmp.uFlags.set(CHN_ADLIB); + } else if(cvt == ITSample::cvtExternalSample) { // Read external sample (filename at sample pointer) mptSmp.uFlags.set(SMP_KEEPONDISK); @@ -635,7 +643,7 @@ void ITHistoryStruct::ConvertToMPT(FileHistory &mptHistory) const mptHistory.loadDate.tm_hour = Clamp((fattime >> 11) & 0x1F, 0, 23); mptHistory.loadDate.tm_min = Clamp((fattime >> 5) & 0x3F, 0, 59); mptHistory.loadDate.tm_sec = Clamp((fattime & 0x1F) * 2, 0, 59); - mptHistory.openTime = static_cast(runtime * (HISTORY_TIMER_PRECISION / 18.2f)); + mptHistory.openTime = static_cast(runtime * (HISTORY_TIMER_PRECISION / 18.2)); } @@ -645,7 +653,7 @@ void ITHistoryStruct::ConvertToIT(const FileHistory &mptHistory) // Create FAT file dates fatdate = static_cast(mptHistory.loadDate.tm_mday | ((mptHistory.loadDate.tm_mon + 1) << 5) | ((mptHistory.loadDate.tm_year - 80) << 9)); fattime = static_cast((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11)); - runtime = static_cast(mptHistory.openTime * (18.2f / HISTORY_TIMER_PRECISION)); + runtime = static_cast(mptHistory.openTime * (18.2 / HISTORY_TIMER_PRECISION)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h index f49ab824e..d2b3a1a2a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../soundlib/ModInstrument.h" #include "../soundlib/ModSample.h" #include "../soundlib/SampleIO.h" @@ -227,6 +229,7 @@ struct ITSample enablePanning = 0x80, cvtSignedSample = 0x01, + cvtOPLInstrument = 0x40, // FM instrument in MPTM cvtExternalSample = 0x80, // Keep MPTM sample on disk cvtADPCMSample = 0xFF, // MODPlugin :( @@ -298,4 +301,20 @@ enum IT_ReaderBitMasks IT_bitmask_patternChanUsed_c = 0x0f }; + +// Calculate Schism Tracker version field for IT / S3M header based on specified release date +// Date calculation derived from https://alcor.concordia.ca/~gpkatch/gdate-algorithm.html +template +struct SchismVersionFromDate +{ + enum : int32 { mm = (m + 9) % 12 }; + enum : int32 { yy = y - mm / 10 }; + enum : int32 { date = yy * 365 + yy / 4 - yy / 100 + yy / 400 + (mm * 306 + 5) / 10 + (d - 1) }; + + static constexpr int32 Version(const int32 trackerID = 0x1000) + { + return trackerID + 0x0050 + date - SchismVersionFromDate<2009, 10, 31>::date; + } +}; + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp index 1f82ba72b..10127bd9d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp @@ -26,7 +26,7 @@ MODULAR (in/out) ModInstrument : - both following functions need to be updated when adding a new member in ModInstrument : -void WriteInstrumentHeaderStructOrField(ModInstrument * input, FILE * file, uint32 only_this_code, int16 fixedsize); +void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, int16 fixedsize); bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, int16 fsize, FileReader &file); - see below for body declaration. @@ -181,8 +181,7 @@ bool IsNegative(const T &val) if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ type tmp = input-> name; \ - tmp = SwapBytesLE(tmp); \ - fwrite(&tmp , 1 , fsize , file); \ + mpt::IO::WriteIntLE(file, tmp); \ } \ /**/ @@ -198,8 +197,7 @@ bool IsNegative(const T &val) mpt::IO::WriteIntLE(file, fcode); \ mpt::IO::WriteIntLE(file, fsize); \ type tmp = (type)(input-> name ); \ - tmp = SwapBytesLE(tmp); \ - fwrite(&tmp , 1 , fsize , file); \ + mpt::IO::WriteIntLE(file, tmp); \ } else if(only_this_code == fcode)\ { \ /* hackish workaround to resolve mismatched size values: */ \ @@ -207,14 +205,13 @@ bool IsNegative(const T &val) /* This worked fine on little-endian, on big-endian not so much. Thus support writing size-mismatched fields. */ \ MPT_ASSERT(fixedsize >= fsize); \ type tmp = (type)(input-> name ); \ - tmp = SwapBytesLE(tmp); \ - fwrite(&tmp , 1 , fsize , file); \ + mpt::IO::WriteIntLE(file, tmp); \ if(fixedsize > fsize) \ { \ for(int16 i = 0; i < fixedsize - fsize; ++i) \ { \ uint8 fillbyte = !IsNegative(tmp) ? 0 : 0xff; /* sign extend */ \ - fwrite(&fillbyte, 1, 1, file); \ + mpt::IO::WriteIntLE(file, fillbyte); \ } \ } \ } \ @@ -243,8 +240,7 @@ bool IsNegative(const T &val) { \ type tmp; \ tmp = input-> name [i]; \ - tmp = SwapBytesLE(tmp); \ - fwrite(&tmp, 1, sizeof(type), file); \ + mpt::IO::WriteIntLE(file, tmp); \ } \ } \ /**/ @@ -274,16 +270,14 @@ bool IsNegative(const T &val) for(uint32 i = 0; i < maxNodes; ++i) \ { \ type tmp; \ - tmp = env[i]. envField; \ - tmp = SwapBytesLE(tmp); \ - fwrite(&tmp, 1, sizeof(type), file); \ + tmp = env[i]. envField ; \ + mpt::IO::WriteIntLE(file, tmp); \ } \ /* Not every instrument's envelope will be the same length. fill up with zeros. */ \ for(uint32 i = maxNodes; i < fsize/sizeof(type); ++i) \ { \ type tmp = 0; \ - tmp = SwapBytesLE(tmp); \ - fwrite(&tmp, 1, sizeof(type), file); \ + mpt::IO::WriteIntLE(file, tmp); \ } \ } \ }\ @@ -291,7 +285,7 @@ bool IsNegative(const T &val) // Write (in 'file') 'input' ModInstrument with 'code' & 'size' extra field infos for each member -void WriteInstrumentHeaderStructOrField(ModInstrument * input, FILE * file, uint32 only_this_code, uint16 fixedsize) +void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, uint16 fixedsize) { uint32 fcode; uint16 fsize; @@ -304,37 +298,60 @@ if(!writeAll) MPT_ASSERT(fixedsize > 0); } - WRITE_MPTHEADER_sized_member( nFadeOut , uint32 , MAGIC4BE('F','O','.','.') ) - WRITE_MPTHEADER_sized_member( nPan , uint32 , MAGIC4BE('P','.','.','.') ) - WRITE_MPTHEADER_sized_member( VolEnv.size() , uint32 , MAGIC4BE('V','E','.','.') ) - WRITE_MPTHEADER_sized_member( PanEnv.size() , uint32 , MAGIC4BE('P','E','.','.') ) - WRITE_MPTHEADER_sized_member( PitchEnv.size() , uint32 , MAGIC4BE('P','i','E','.') ) - WRITE_MPTHEADER_sized_member( wMidiBank , uint16 , MAGIC4BE('M','B','.','.') ) - WRITE_MPTHEADER_sized_member( nMidiProgram , uint8 , MAGIC4BE('M','P','.','.') ) - WRITE_MPTHEADER_sized_member( nMidiChannel , uint8 , MAGIC4BE('M','C','.','.') ) - WRITE_MPTHEADER_envelope_member( ENV_VOLUME , tick , uint16 , MAGIC4BE('V','P','[','.') ) - WRITE_MPTHEADER_envelope_member( ENV_PANNING , tick , uint16 , MAGIC4BE('P','P','[','.') ) - WRITE_MPTHEADER_envelope_member( ENV_PITCH , tick , uint16 , MAGIC4BE('P','i','P','[') ) - WRITE_MPTHEADER_envelope_member( ENV_VOLUME , value , uint8 , MAGIC4BE('V','E','[','.') ) - WRITE_MPTHEADER_envelope_member( ENV_PANNING , value , uint8 , MAGIC4BE('P','E','[','.') ) - WRITE_MPTHEADER_envelope_member( ENV_PITCH , value , uint8 , MAGIC4BE('P','i','E','[') ) - WRITE_MPTHEADER_sized_member( nMixPlug , uint8 , MAGIC4BE('M','i','P','.') ) - WRITE_MPTHEADER_sized_member( nVolRampUp , uint16 , MAGIC4BE('V','R','.','.') ) - WRITE_MPTHEADER_trunc_member( nResampling , uint16 , MAGIC4BE('R','.','.','.') ) - WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MAGIC4BE('C','S','.','.') ) - WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MAGIC4BE('R','S','.','.') ) - WRITE_MPTHEADER_sized_member( nFilterMode , uint8 , MAGIC4BE('F','M','.','.') ) - WRITE_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MAGIC4BE('P','V','E','H') ) - WRITE_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MAGIC4BE('P','V','O','H') ) - WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MAGIC4BE('P','T','T','L') ) - WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetFract() , uint16 , MAGIC4LE('P','T','T','F') ) - WRITE_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MAGIC4BE('P','E','R','N') ) - WRITE_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MAGIC4BE('A','E','R','N') ) - WRITE_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MAGIC4BE('V','E','R','N') ) - WRITE_MPTHEADER_sized_member( PitchEnv.dwFlags , uint32 , MAGIC4BE('P','F','L','G') ) - WRITE_MPTHEADER_sized_member( PanEnv.dwFlags , uint32 , MAGIC4BE('A','F','L','G') ) - WRITE_MPTHEADER_sized_member( VolEnv.dwFlags , uint32 , MAGIC4BE('V','F','L','G') ) - WRITE_MPTHEADER_sized_member( midiPWD , int8 , MAGIC4BE('M','P','W','D') ) + WRITE_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") ) + WRITE_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") ) + WRITE_MPTHEADER_sized_member( VolEnv.size() , uint32 , MagicBE("VE..") ) + WRITE_MPTHEADER_sized_member( PanEnv.size() , uint32 , MagicBE("PE..") ) + WRITE_MPTHEADER_sized_member( PitchEnv.size() , uint32 , MagicBE("PiE.") ) + WRITE_MPTHEADER_sized_member( wMidiBank , uint16 , MagicBE("MB..") ) + WRITE_MPTHEADER_sized_member( nMidiProgram , uint8 , MagicBE("MP..") ) + WRITE_MPTHEADER_sized_member( nMidiChannel , uint8 , MagicBE("MC..") ) + WRITE_MPTHEADER_envelope_member( ENV_VOLUME , tick , uint16 , MagicBE("VP[.") ) + WRITE_MPTHEADER_envelope_member( ENV_PANNING , tick , uint16 , MagicBE("PP[.") ) + WRITE_MPTHEADER_envelope_member( ENV_PITCH , tick , uint16 , MagicBE("PiP[") ) + WRITE_MPTHEADER_envelope_member( ENV_VOLUME , value , uint8 , MagicBE("VE[.") ) + WRITE_MPTHEADER_envelope_member( ENV_PANNING , value , uint8 , MagicBE("PE[.") ) + WRITE_MPTHEADER_envelope_member( ENV_PITCH , value , uint8 , MagicBE("PiE[") ) + WRITE_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) + WRITE_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) + WRITE_MPTHEADER_trunc_member( nResampling , uint16 , MagicBE("R...") ) + WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) + WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) + WRITE_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) + WRITE_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MagicBE("PVEH") ) + WRITE_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MagicBE("PVOH") ) + WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MagicBE("PTTL") ) + WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetFract() , uint16 , MagicLE("PTTF") ) + WRITE_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) + WRITE_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") ) + WRITE_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") ) + WRITE_MPTHEADER_sized_member( PitchEnv.dwFlags , uint8 , MagicBE("PFLG") ) + WRITE_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") ) + WRITE_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") ) + WRITE_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") ) +} + + +template +static bool IsPropertyNeeded(const TIns &Instruments, PropType ModInstrument::*Prop) +{ + const ModInstrument defaultIns; + for(const auto &ins : Instruments) + { + if(ins != nullptr && defaultIns.*Prop != ins->*Prop) + return true; + } + return false; +} + + +template +static void WritePropertyIfNeeded(const CSoundFile &sndFile, PropType ModInstrument::*Prop, uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX numInstruments) +{ + if(IsPropertyNeeded(sndFile.Instruments, Prop)) + { + sndFile.WriteInstrumentPropertyForAllInstruments(code, size, f, numInstruments); + } } @@ -343,71 +360,83 @@ if(!writeAll) // The reason is that ITs and XMs save [code][size][ins1.Value][ins2.Value]... // whereas ITP saves [code][size][ins1.Value][code][size][ins2.Value]... // too late to turn back.... -void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX nInstruments, FILE *f) const +void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX numInstruments, std::ostream &f) const { - uint32 code = MAGIC4BE('M','P','T','X'); // write extension header code + uint32 code = MagicBE("MPTX"); // write extension header code mpt::IO::WriteIntLE(f, code); - if (nInstruments == 0) + if (numInstruments == 0) return; - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('V','R','.','.'), sizeof(ModInstrument().nVolRampUp), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('M','i','P','.'), sizeof(ModInstrument().nMixPlug), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('M','C','.','.'), sizeof(ModInstrument().nMidiChannel),f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('M','P','.','.'), sizeof(ModInstrument().nMidiProgram),f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('M','B','.','.'), sizeof(ModInstrument().wMidiBank), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','.','.','.'), sizeof(ModInstrument().nPan), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('F','O','.','.'), sizeof(ModInstrument().nFadeOut), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('R','.','.','.'), sizeof(ModInstrument().nResampling), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('C','S','.','.'), sizeof(ModInstrument().nCutSwing), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('R','S','.','.'), sizeof(ModInstrument().nResSwing), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('F','M','.','.'), sizeof(ModInstrument().nFilterMode), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','E','R','N'), sizeof(ModInstrument().PitchEnv.nReleaseNode ), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('A','E','R','N'), sizeof(ModInstrument().PanEnv.nReleaseNode), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('V','E','R','N'), sizeof(ModInstrument().VolEnv.nReleaseNode), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','T','T','L'), sizeof(uint16), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4LE('P','T','T','F'), sizeof(uint16), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','V','E','H'), sizeof(ModInstrument().nPluginVelocityHandling), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','V','O','H'), sizeof(ModInstrument().nPluginVolumeHandling), f, nInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nVolRampUp, MagicBE("VR.."), sizeof(ModInstrument::nVolRampUp), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nMixPlug, MagicBE("MiP."), sizeof(ModInstrument::nMixPlug), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nMidiChannel, MagicBE("MC.."), sizeof(ModInstrument::nMidiChannel), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nMidiProgram, MagicBE("MP.."), sizeof(ModInstrument::nMidiProgram), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::wMidiBank, MagicBE("MB.."), sizeof(ModInstrument::wMidiBank), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nResampling, MagicBE("R..."), sizeof(ModInstrument::nResampling), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nPluginVelocityHandling, MagicBE("PVEH"), sizeof(ModInstrument::nPluginVelocityHandling), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nPluginVolumeHandling, MagicBE("PVOH"), sizeof(ModInstrument::nPluginVolumeHandling), f, numInstruments); if(!(GetType() & MOD_TYPE_XM)) { + // XM instrument headers already stores full-precision fade-out + WritePropertyIfNeeded(*this, &ModInstrument::nFadeOut, MagicBE("FO.."), sizeof(ModInstrument::nFadeOut), f, numInstruments); // XM instrument headers already have support for this - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('M','P','W','D'), sizeof(ModInstrument().midiPWD), f, nInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::midiPWD, MagicBE("MPWD"), sizeof(ModInstrument::midiPWD), f, numInstruments); + // We never supported these as hacks in XM (luckily!) + WritePropertyIfNeeded(*this, &ModInstrument::nPan, MagicBE("P..."), sizeof(ModInstrument::nPan), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nCutSwing, MagicBE("CS.."), sizeof(ModInstrument::nCutSwing), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nResSwing, MagicBE("RS.."), sizeof(ModInstrument::nResSwing), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::nFilterMode, MagicBE("FM.."), sizeof(ModInstrument::nFilterMode), f, numInstruments); + if(IsPropertyNeeded(Instruments, &ModInstrument::pitchToTempoLock)) + { + WriteInstrumentPropertyForAllInstruments(MagicBE("PTTL"), sizeof(uint16), f, numInstruments); + WriteInstrumentPropertyForAllInstruments(MagicLE("PTTF"), sizeof(uint16), f, numInstruments); + } } if(GetType() & MOD_TYPE_MPT) { - uint32 maxNodes[3] = { 0 }; - for(INSTRUMENTINDEX i = 1; i <= m_nInstruments; i++) if(Instruments[i] != nullptr) + uint32 maxNodes[3] = { 0, 0, 0 }; + bool hasReleaseNode[3] = { false, false, false }; + for(INSTRUMENTINDEX i = 1; i <= numInstruments; i++) if(Instruments[i] != nullptr) { maxNodes[0] = std::max(maxNodes[0], Instruments[i]->VolEnv.size()); maxNodes[1] = std::max(maxNodes[1], Instruments[i]->PanEnv.size()); maxNodes[2] = std::max(maxNodes[2], Instruments[i]->PitchEnv.size()); + hasReleaseNode[0] |= (Instruments[i]->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET); + hasReleaseNode[1] |= (Instruments[i]->PanEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET); + hasReleaseNode[2] |= (Instruments[i]->PitchEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET); } // write full envelope information for MPTM files (more env points) if(maxNodes[0] > 25) { - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('V','E','.','.'), sizeof(ModInstrument().VolEnv.size()), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('V','P','[','.'), static_cast(maxNodes[0] * sizeof(EnvelopeNode().tick)), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('V','E','[','.'), static_cast(maxNodes[0] * sizeof(EnvelopeNode().value)), f, nInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("VE.."), sizeof(ModInstrument::VolEnv.size()), f, numInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("VP[."), static_cast(maxNodes[0] * sizeof(EnvelopeNode::tick)), f, numInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("VE[."), static_cast(maxNodes[0] * sizeof(EnvelopeNode::value)), f, numInstruments); } if(maxNodes[1] > 25) { - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','E','.','.'), sizeof(ModInstrument().PanEnv.size()), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','P','[','.'), static_cast(maxNodes[1] * sizeof(EnvelopeNode().tick)), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','E','[','.'), static_cast(maxNodes[1] * sizeof(EnvelopeNode().value)), f, nInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("PE.."), sizeof(ModInstrument::PanEnv.size()), f, numInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("PP[."), static_cast(maxNodes[1] * sizeof(EnvelopeNode::tick)), f, numInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("PE[."), static_cast(maxNodes[1] * sizeof(EnvelopeNode::value)), f, numInstruments); } if(maxNodes[2] > 25) { - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','i','E','.'), sizeof(ModInstrument().PitchEnv.size()), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','i','P','['), static_cast(maxNodes[2] * sizeof(EnvelopeNode().tick)), f, nInstruments); - WriteInstrumentPropertyForAllInstruments(MAGIC4BE('P','i','E','['), static_cast(maxNodes[2] * sizeof(EnvelopeNode().value)), f, nInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("PiE."), sizeof(ModInstrument::PitchEnv.size()), f, numInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("PiP["), static_cast(maxNodes[2] * sizeof(EnvelopeNode::tick)), f, numInstruments); + WriteInstrumentPropertyForAllInstruments(MagicBE("PiE["), static_cast(maxNodes[2] * sizeof(EnvelopeNode::value)), f, numInstruments); } + if(hasReleaseNode[0]) + WriteInstrumentPropertyForAllInstruments(MagicBE("VERN"), sizeof(ModInstrument::VolEnv.nReleaseNode), f, numInstruments); + if(hasReleaseNode[1]) + WriteInstrumentPropertyForAllInstruments(MagicBE("AERN"), sizeof(ModInstrument::PanEnv.nReleaseNode), f, numInstruments); + if(hasReleaseNode[2]) + WriteInstrumentPropertyForAllInstruments(MagicBE("PERN"), sizeof(ModInstrument::PitchEnv.nReleaseNode), f, numInstruments); } } -void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 size, FILE *f, INSTRUMENTINDEX nInstruments) const +void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX nInstruments) const { mpt::IO::WriteIntLE(f, code); //write code mpt::IO::WriteIntLE(f, size); //write size @@ -467,6 +496,23 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si } \ } break; +// -------------------------------------------------------------------------------------------- +// Convenient macro to help GET_HEADER declaration for character array members ONLY +// -------------------------------------------------------------------------------------------- +#define GET_MPTHEADER_chararray_member(name,type,code) \ + case code: \ + {\ + if( fsize <= sizeof( type ) * CountOf(input-> name) ) \ + { \ + FileReader arrayChunk = file.ReadChunk(fsize); \ + for(std::size_t i = 0; i < CountOf(input-> name); ++i) \ + { \ + input-> name [i] = arrayChunk.ReadChar(); \ + } \ + result = true; \ + } \ + } break; + // -------------------------------------------------------------------------------------------- // Convenient macro to help GET_HEADER declaration for envelope tick/value members // -------------------------------------------------------------------------------------------- @@ -493,82 +539,82 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, // Members which can be found in this table but not in the write table are only required in the legacy ITP format. switch(fcode) { - GET_MPTHEADER_sized_member( nFadeOut , uint32 , MAGIC4BE('F','O','.','.') ) - GET_MPTHEADER_sized_member( dwFlags , uint32 , MAGIC4BE('d','F','.','.') ) - GET_MPTHEADER_sized_member( nGlobalVol , uint32 , MAGIC4BE('G','V','.','.') ) - GET_MPTHEADER_sized_member( nPan , uint32 , MAGIC4BE('P','.','.','.') ) - GET_MPTHEADER_sized_member( VolEnv.nLoopStart , uint8 , MAGIC4BE('V','L','S','.') ) - GET_MPTHEADER_sized_member( VolEnv.nLoopEnd , uint8 , MAGIC4BE('V','L','E','.') ) - GET_MPTHEADER_sized_member( VolEnv.nSustainStart , uint8 , MAGIC4BE('V','S','B','.') ) - GET_MPTHEADER_sized_member( VolEnv.nSustainEnd , uint8 , MAGIC4BE('V','S','E','.') ) - GET_MPTHEADER_sized_member( PanEnv.nLoopStart , uint8 , MAGIC4BE('P','L','S','.') ) - GET_MPTHEADER_sized_member( PanEnv.nLoopEnd , uint8 , MAGIC4BE('P','L','E','.') ) - GET_MPTHEADER_sized_member( PanEnv.nSustainStart , uint8 , MAGIC4BE('P','S','B','.') ) - GET_MPTHEADER_sized_member( PanEnv.nSustainEnd , uint8 , MAGIC4BE('P','S','E','.') ) - GET_MPTHEADER_sized_member( PitchEnv.nLoopStart , uint8 , MAGIC4BE('P','i','L','S') ) - GET_MPTHEADER_sized_member( PitchEnv.nLoopEnd , uint8 , MAGIC4BE('P','i','L','E') ) - GET_MPTHEADER_sized_member( PitchEnv.nSustainStart , uint8 , MAGIC4BE('P','i','S','B') ) - GET_MPTHEADER_sized_member( PitchEnv.nSustainEnd , uint8 , MAGIC4BE('P','i','S','E') ) - GET_MPTHEADER_sized_member( nNNA , uint8 , MAGIC4BE('N','N','A','.') ) - GET_MPTHEADER_sized_member( nDCT , uint8 , MAGIC4BE('D','C','T','.') ) - GET_MPTHEADER_sized_member( nDNA , uint8 , MAGIC4BE('D','N','A','.') ) - GET_MPTHEADER_sized_member( nPanSwing , uint8 , MAGIC4BE('P','S','.','.') ) - GET_MPTHEADER_sized_member( nVolSwing , uint8 , MAGIC4BE('V','S','.','.') ) - GET_MPTHEADER_sized_member( nIFC , uint8 , MAGIC4BE('I','F','C','.') ) - GET_MPTHEADER_sized_member( nIFR , uint8 , MAGIC4BE('I','F','R','.') ) - GET_MPTHEADER_sized_member( wMidiBank , uint16 , MAGIC4BE('M','B','.','.') ) - GET_MPTHEADER_sized_member( nMidiProgram , uint8 , MAGIC4BE('M','P','.','.') ) - GET_MPTHEADER_sized_member( nMidiChannel , uint8 , MAGIC4BE('M','C','.','.') ) - GET_MPTHEADER_sized_member( nPPS , int8 , MAGIC4BE('P','P','S','.') ) - GET_MPTHEADER_sized_member( nPPC , uint8 , MAGIC4BE('P','P','C','.') ) - GET_MPTHEADER_envelope_member(ENV_VOLUME , tick , uint16 , MAGIC4BE('V','P','[','.') ) - GET_MPTHEADER_envelope_member(ENV_PANNING , tick , uint16 , MAGIC4BE('P','P','[','.') ) - GET_MPTHEADER_envelope_member(ENV_PITCH , tick , uint16 , MAGIC4BE('P','i','P','[') ) - GET_MPTHEADER_envelope_member(ENV_VOLUME , value , uint8 , MAGIC4BE('V','E','[','.') ) - GET_MPTHEADER_envelope_member(ENV_PANNING , value , uint8 , MAGIC4BE('P','E','[','.') ) - GET_MPTHEADER_envelope_member(ENV_PITCH , value , uint8 , MAGIC4BE('P','i','E','[') ) - GET_MPTHEADER_array_member( NoteMap , uint8 , MAGIC4BE('N','M','[','.') ) - GET_MPTHEADER_array_member( Keyboard , uint16 , MAGIC4BE('K','[','.','.') ) - GET_MPTHEADER_array_member( name , char , MAGIC4BE('n','[','.','.') ) - GET_MPTHEADER_array_member( filename , char , MAGIC4BE('f','n','[','.') ) - GET_MPTHEADER_sized_member( nMixPlug , uint8 , MAGIC4BE('M','i','P','.') ) - GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MAGIC4BE('V','R','.','.') ) - GET_MPTHEADER_sized_member( nResampling , uint32 , MAGIC4BE('R','.','.','.') ) - GET_MPTHEADER_sized_member( nCutSwing , uint8 , MAGIC4BE('C','S','.','.') ) - GET_MPTHEADER_sized_member( nResSwing , uint8 , MAGIC4BE('R','S','.','.') ) - GET_MPTHEADER_sized_member( nFilterMode , uint8 , MAGIC4BE('F','M','.','.') ) - GET_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MAGIC4BE('P','V','E','H') ) - GET_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MAGIC4BE('P','V','O','H') ) - GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MAGIC4BE('P','E','R','N') ) - GET_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MAGIC4BE('A','E','R','N') ) - GET_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MAGIC4BE('V','E','R','N') ) - GET_MPTHEADER_sized_member( PitchEnv.dwFlags , uint32 , MAGIC4BE('P','F','L','G') ) - GET_MPTHEADER_sized_member( PanEnv.dwFlags , uint32 , MAGIC4BE('A','F','L','G') ) - GET_MPTHEADER_sized_member( VolEnv.dwFlags , uint32 , MAGIC4BE('V','F','L','G') ) - GET_MPTHEADER_sized_member( midiPWD , int8 , MAGIC4BE('M','P','W','D') ) - case MAGIC4BE('P','T','T','L'): + GET_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") ) + GET_MPTHEADER_sized_member( dwFlags , uint8 , MagicBE("dF..") ) + GET_MPTHEADER_sized_member( nGlobalVol , uint32 , MagicBE("GV..") ) + GET_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") ) + GET_MPTHEADER_sized_member( VolEnv.nLoopStart , uint8 , MagicBE("VLS.") ) + GET_MPTHEADER_sized_member( VolEnv.nLoopEnd , uint8 , MagicBE("VLE.") ) + GET_MPTHEADER_sized_member( VolEnv.nSustainStart , uint8 , MagicBE("VSB.") ) + GET_MPTHEADER_sized_member( VolEnv.nSustainEnd , uint8 , MagicBE("VSE.") ) + GET_MPTHEADER_sized_member( PanEnv.nLoopStart , uint8 , MagicBE("PLS.") ) + GET_MPTHEADER_sized_member( PanEnv.nLoopEnd , uint8 , MagicBE("PLE.") ) + GET_MPTHEADER_sized_member( PanEnv.nSustainStart , uint8 , MagicBE("PSB.") ) + GET_MPTHEADER_sized_member( PanEnv.nSustainEnd , uint8 , MagicBE("PSE.") ) + GET_MPTHEADER_sized_member( PitchEnv.nLoopStart , uint8 , MagicBE("PiLS") ) + GET_MPTHEADER_sized_member( PitchEnv.nLoopEnd , uint8 , MagicBE("PiLE") ) + GET_MPTHEADER_sized_member( PitchEnv.nSustainStart , uint8 , MagicBE("PiSB") ) + GET_MPTHEADER_sized_member( PitchEnv.nSustainEnd , uint8 , MagicBE("PiSE") ) + GET_MPTHEADER_sized_member( nNNA , uint8 , MagicBE("NNA.") ) + GET_MPTHEADER_sized_member( nDCT , uint8 , MagicBE("DCT.") ) + GET_MPTHEADER_sized_member( nDNA , uint8 , MagicBE("DNA.") ) + GET_MPTHEADER_sized_member( nPanSwing , uint8 , MagicBE("PS..") ) + GET_MPTHEADER_sized_member( nVolSwing , uint8 , MagicBE("VS..") ) + GET_MPTHEADER_sized_member( nIFC , uint8 , MagicBE("IFC.") ) + GET_MPTHEADER_sized_member( nIFR , uint8 , MagicBE("IFR.") ) + GET_MPTHEADER_sized_member( wMidiBank , uint16 , MagicBE("MB..") ) + GET_MPTHEADER_sized_member( nMidiProgram , uint8 , MagicBE("MP..") ) + GET_MPTHEADER_sized_member( nMidiChannel , uint8 , MagicBE("MC..") ) + GET_MPTHEADER_sized_member( nPPS , int8 , MagicBE("PPS.") ) + GET_MPTHEADER_sized_member( nPPC , uint8 , MagicBE("PPC.") ) + GET_MPTHEADER_envelope_member(ENV_VOLUME , tick , uint16 , MagicBE("VP[.") ) + GET_MPTHEADER_envelope_member(ENV_PANNING , tick , uint16 , MagicBE("PP[.") ) + GET_MPTHEADER_envelope_member(ENV_PITCH , tick , uint16 , MagicBE("PiP[") ) + GET_MPTHEADER_envelope_member(ENV_VOLUME , value , uint8 , MagicBE("VE[.") ) + GET_MPTHEADER_envelope_member(ENV_PANNING , value , uint8 , MagicBE("PE[.") ) + GET_MPTHEADER_envelope_member(ENV_PITCH , value , uint8 , MagicBE("PiE[") ) + GET_MPTHEADER_array_member( NoteMap , uint8 , MagicBE("NM[.") ) + GET_MPTHEADER_array_member( Keyboard , uint16 , MagicBE("K[..") ) + GET_MPTHEADER_chararray_member( name , char , MagicBE("n[..") ) + GET_MPTHEADER_chararray_member( filename , char , MagicBE("fn[.") ) + GET_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) + GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) + GET_MPTHEADER_sized_member( nResampling , uint32 , MagicBE("R...") ) + GET_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) + GET_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) + GET_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) + GET_MPTHEADER_sized_member( nPluginVelocityHandling , uint8 , MagicBE("PVEH") ) + GET_MPTHEADER_sized_member( nPluginVolumeHandling , uint8 , MagicBE("PVOH") ) + GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) + GET_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") ) + GET_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") ) + GET_MPTHEADER_sized_member( PitchEnv.dwFlags , uint8 , MagicBE("PFLG") ) + GET_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") ) + GET_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") ) + GET_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") ) + case MagicBE("PTTL"): { // Integer part of pitch/tempo lock uint16 tmp = file.ReadTruncatedIntLE(fsize); input->pitchToTempoLock.Set(tmp, input->pitchToTempoLock.GetFract()); result = true; } break; - case MAGIC4LE('P','T','T','F'): + case MagicLE("PTTF"): { // Fractional part of pitch/tempo lock uint16 tmp = file.ReadTruncatedIntLE(fsize); input->pitchToTempoLock.Set(input->pitchToTempoLock.GetInt(), tmp); result = true; } break; - case MAGIC4BE('V','E','.','.'): + case MagicBE("VE.."): input->VolEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); result = true; break; - case MAGIC4BE('P','E','.','.'): + case MagicBE("PE.."): input->PanEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); result = true; break; - case MAGIC4BE('P','i','E','.'): + case MagicBE("PiE."): input->PitchEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); result = true; break; @@ -627,7 +673,7 @@ static void ConvertReadExtendedFlags(ModInstrument *pIns) void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const uint16 size, FileReader &file) { - if(code == MAGIC4BE('K','[','.','.')) + if(code == MagicBE("K[..")) { // skip keyboard mapping file.Skip(size); @@ -642,12 +688,12 @@ void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const return; } - if(code == MAGIC4BE('n','[','.','.')) + if(code == MagicBE("n[..")) mpt::String::SetNullTerminator(pIns->name); - if(code == MAGIC4BE('f','n','[','.')) + if(code == MagicBE("fn[.")) mpt::String::SetNullTerminator(pIns->filename); - if(code == MAGIC4BE('d','F','.','.')) // 'dF..' field requires additional processing. + if(code == MagicBE("dF..")) // 'dF..' field requires additional processing. ConvertReadExtendedFlags(pIns); } @@ -677,27 +723,23 @@ void ReadExtendedInstrumentProperties(ModInstrument* pIns, FileReader &file) } -void CSoundFile::LoadExtendedInstrumentProperties(FileReader &file, bool *pInterpretMptMade) +bool CSoundFile::LoadExtendedInstrumentProperties(FileReader &file) { if(!file.ReadMagic("XTPM")) // 'MPTX' { - return; + return false; } - // Found MPTX, interpret the file MPT made. - if(pInterpretMptMade != nullptr) - *pInterpretMptMade = true; - while(file.CanRead(6)) { uint32 code = file.ReadUint32LE(); - if(code == MAGIC4BE('M','P','T','S') // Reached song extensions, break out of this loop - || code == MAGIC4LE('2','2','8',4) // Reached MPTM extensions (in case there are no song extensions) + if(code == MagicBE("MPTS") // Reached song extensions, break out of this loop + || code == MagicLE("228\x04") // Reached MPTM extensions (in case there are no song extensions) || (code & 0x80808080) || !(code & 0x60606060)) // Non-ASCII chunk ID { file.SkipBack(4); - return; + break; } // Read size of this property for *one* instrument @@ -711,6 +753,7 @@ void CSoundFile::LoadExtendedInstrumentProperties(FileReader &file, bool *pInter } } } + return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h index 294d066d2..bd7becb21 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + #include "Resampler.h" #include "MixerInterface.h" #include "Paula.h" @@ -108,7 +110,7 @@ struct LinearInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const typename Traits::output_t fract = posLo >> 18u; for(int i = 0; i < Traits::numChannelsIn; i++) @@ -130,7 +132,7 @@ struct FastSincInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const int16 *lut = CResampler::FastSincTable + ((posLo >> 22) & 0x3FC); for(int i = 0; i < Traits::numChannelsIn; i++) @@ -166,7 +168,7 @@ struct PolyphaseInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const SINC_TYPE *lut = sinc + ((posLo >> (32 - SINC_PHASES_BITS)) & SINC_MASK) * SINC_WIDTH; for(int i = 0; i < Traits::numChannelsIn; i++) @@ -199,7 +201,7 @@ struct FIRFilterInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const MPT_RESTRICT inBuffer, const uint32 posLo) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); const int16 * const lut = WFIRlut + ((((posLo >> 16) + WFIR_FRACHALVE) >> WFIR_FRACSHIFT) & WFIR_FRACMASK); for(int i = 0; i < Traits::numChannelsIn; i++) @@ -368,7 +370,7 @@ struct ResonantFilter MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const ModChannel &chn) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); for(int i = 0; i < Traits::numChannelsIn; i++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp index d6953e203..007d4463c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp @@ -145,10 +145,10 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags.set(SONG_LINEARSLIDES); #endif // MODPLUG_TRACKER - if(!memcmp(fileHeader.magic, "if", 2)) - m_madeWithTracker = MPT_USTRING("Composer 669"); - else - m_madeWithTracker = MPT_USTRING("UNIS 669"); + m_modFormat.formatName = U_("Composer 669"); + m_modFormat.type = U_("669"); + m_modFormat.madeWithTracker = !memcmp(fileHeader.magic, "if", 2) ? UL_("Composer 669") : UL_("UNIS 669"); + m_modFormat.charset = mpt::CharsetCP437; m_nSamples = fileHeader.samples; for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp index 91ff8f450..df683325a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp @@ -146,7 +146,10 @@ bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) { Order().SetRestartPos(fileHeader.restartPos); } - m_songName.clear(); + + m_modFormat.formatName = U_("ASYLUM Music Format"); + m_modFormat.type = U_("amf"); + m_modFormat.charset = mpt::CharsetCP437; uint8 orders[256]; file.ReadArray(orders); @@ -436,6 +439,10 @@ bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_AMF); InitializeChannels(); + m_modFormat.formatName = mpt::format(U_("DSMI v%1"))(fileHeader.version); + m_modFormat.type = U_("amf"); + m_modFormat.charset = mpt::CharsetCP437; + m_nChannels = fileHeader.numChannels; m_nSamples = fileHeader.numSamples; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp index 412690f39..47aa9795c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp @@ -75,8 +75,7 @@ static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patte CMD_GLOBALVOLUME, // Global volume (0... 127) }; - ModCommand dummy = ModCommand::Empty(); - + ModCommand dummy; for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++) { PatternRow baseRow = pattern.GetRow(row); @@ -406,7 +405,11 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) m_nChannels = (fileHeader.channelConfig & 0x1F) + 1; m_nSamples = fileHeader.numSamps; SetupMODPanning(true); - m_madeWithTracker = mpt::format(MPT_USTRING("Extreme's Tracker %1.%2"))(fileHeader.versionHigh, fileHeader.versionLow); + + m_modFormat.formatName = U_("Extreme's Tracker"); + m_modFormat.type = U_("ams"); + m_modFormat.madeWithTracker = mpt::format(U_("Extreme's Tracker %1.%2"))(fileHeader.versionHigh, fileHeader.versionLow); + m_modFormat.charset = mpt::CharsetCP437; std::vector packSample(fileHeader.numSamps); @@ -478,12 +481,12 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) ReadOrderFromFile(Order(), file, fileHeader.numOrds); // Read patterns - for(PATTERNINDEX pat = 0; pat < fileHeader.numPats; pat++) + for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++) { uint32 patLength = file.ReadUint32LE(); FileReader patternChunk = file.ReadChunk(patLength); - if(loadFlags & loadPatternData) + if((loadFlags & loadPatternData) && Patterns.IsValidPat(pat)) { ReadAMSPattern(Patterns[pat], false, patternChunk); } @@ -770,14 +773,18 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) return true; } - InitializeGlobals(MOD_TYPE_AMS2); + InitializeGlobals(MOD_TYPE_AMS); m_songName = songName; m_nInstruments = fileHeader.numIns; m_nChannels = 32; SetupMODPanning(true); - m_madeWithTracker = mpt::format(MPT_USTRING("Velvet Studio %1.%2"))(fileHeader.versionHigh.get(), mpt::ufmt::dec0<2>(fileHeader.versionLow.get())); + + m_modFormat.formatName = U_("Velvet Studio"); + m_modFormat.type = U_("ams"); + m_modFormat.madeWithTracker = mpt::format(U_("Velvet Studio %1.%2"))(fileHeader.versionHigh.get(), mpt::ufmt::dec0<2>(fileHeader.versionLow.get())); + m_modFormat.charset = mpt::CharsetCP437; uint16 headerFlags; if(fileHeader.versionLow >= 2) @@ -950,7 +957,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) // Read Patterns if(loadFlags & loadPatternData) Patterns.ResizeArray(fileHeader.numPats); - for(PATTERNINDEX pat = 0; pat < fileHeader.numPats; pat++) + for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++) { uint32 patLength = file.ReadUint32LE(); FileReader patternChunk = file.ReadChunk(patLength); @@ -1005,7 +1012,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) } SAMPLEINDEX sourceSample = ((sampleSettings[smp] & sampleIndexMask) >> sampleIndexShift) + firstSample[sourceInstr]; - if(sourceSample > GetNumSamples() || Samples[sourceSample].pSample == nullptr) + if(sourceSample > GetNumSamples() || !Samples[sourceSample].HasSampleData()) { continue; } @@ -1017,7 +1024,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) sample.nLength = source.nLength; if(sample.AllocateSample()) { - memcpy(sample.pSample, source.pSample, source.GetSampleSizeInBytes()); + memcpy(sample.sampleb(), source.sampleb(), source.GetSampleSizeInBytes()); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp new file mode 100644 index 000000000..495812d76 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp @@ -0,0 +1,269 @@ +/* +* Load_c67.cpp +* ------------ +* Purpose: C67 (CDFM Composer) module loader +* Notes : C67 is the composer format; 670 files can be converted back to C67 using the converter that comes with CDFM. +* Authors: OpenMPT Devs +* The OpenMPT source code is released under the BSD license. Read LICENSE for more details. +*/ + + +#include "stdafx.h" +#include "Loaders.h" + +OPENMPT_NAMESPACE_BEGIN + +struct C67SampleHeader +{ + uint32le unknown; // Probably placeholder for in-memory address, 0 on disk + uint32le length; + uint32le loopStart; + uint32le loopEnd; +}; + +MPT_BINARY_STRUCT(C67SampleHeader, 16) + + +struct C67FileHeader +{ + uint8 speed; + uint8 restartPos; + char sampleNames[32][13]; + C67SampleHeader samples[32]; + char fmInstrNames[32][13]; + uint8 fmInstr[32][11]; + uint8 orders[256]; +}; + +MPT_BINARY_STRUCT(C67FileHeader, 1954) + + +static bool ValidateHeader(const C67FileHeader &fileHeader) +{ + if(fileHeader.speed < 1 || fileHeader.speed > 15) + return false; + for(auto ord : fileHeader.orders) + { + if(ord >= 128 && ord != 0xFF) + return false; + } + + bool anyNonSilent = false; + for(SAMPLEINDEX smp = 0; smp < 32; smp++) + { + if(fileHeader.sampleNames[smp][12] != 0 + || fileHeader.samples[smp].unknown != 0 + || fileHeader.samples[smp].length > 0xFFFFF + || fileHeader.fmInstrNames[smp][12] != 0 + || (fileHeader.fmInstr[smp][0] & 0xF0) // No OPL3 + || (fileHeader.fmInstr[smp][5] & 0xFC) // No OPL3 + || (fileHeader.fmInstr[smp][10] & 0xFC)) // No OPL3 + { + return false; + } + if(fileHeader.samples[smp].length != 0 && fileHeader.samples[smp].loopEnd < 0xFFFFF) + { + if(fileHeader.samples[smp].loopEnd > fileHeader.samples[smp].length + || fileHeader.samples[smp].loopStart > fileHeader.samples[smp].loopEnd) + { + return false; + } + } + if(!anyNonSilent && (fileHeader.samples[smp].length != 0 || memcmp(fileHeader.fmInstr[smp], "\0\0\0\0\0\0\0\0\0\0\0", 11))) + { + anyNonSilent = true; + } + } + return anyNonSilent; +} + + +static uint64 GetHeaderMinimumAdditionalSize(const C67FileHeader &) +{ + return 1024; // Pattern offsets and lengths +} + + +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderC67(MemoryFileReader file, const uint64 *pfilesize) +{ + C67FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!ValidateHeader(fileHeader)) + { + return ProbeFailure; + } + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); +} + + +static void TranslateVolume(ModCommand &m, uint8 volume, bool isFM) +{ + // CDFM uses a linear volume scale for FM instruments. + // ScreamTracker, on the other hand, directly uses the OPL chip's logarithmic volume scale. + // Neither FM nor PCM instruments can be fully muted in CDFM. + static const uint8 fmVolume[16] = + { + 0x08, 0x10, 0x18, 0x20, 0x28, 0x2C, 0x30, 0x34, + 0x36, 0x38, 0x3A, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, + }; + + volume &= 0x0F; + m.volcmd = VOLCMD_VOLUME; + m.vol = isFM ? fmVolume[volume] : (4u + volume * 4u); +} + + +bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) +{ + C67FileHeader fileHeader; + + file.Rewind(); + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(!ValidateHeader(fileHeader)) + { + return false; + } + if(loadFlags == onlyVerifyHeader) + { + return true; + } + + if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) + { + return false; + } + + // Validate pattern offsets and lengths + uint32le patOffsets[128], patLengths[128]; + file.ReadArray(patOffsets); + file.ReadArray(patLengths); + for(PATTERNINDEX pat = 0; pat < 128; pat++) + { + if(patOffsets[pat] > 0xFFFFFF + || patLengths[pat] < 3 // Smallest well-formed pattern consists of command 0x40 followed by command 0x60 + || patLengths[pat] > 0x1000 // Any well-formed pattern is smaller than this + || !file.LengthIsAtLeast(2978 + patOffsets[pat] + patLengths[pat])) + { + return false; + } + } + + InitializeGlobals(MOD_TYPE_S3M); + InitializeChannels(); + + m_modFormat.formatName = U_("CDFM"); + m_modFormat.type = U_("c67"); + m_modFormat.madeWithTracker = U_("Composer 670"); + m_modFormat.charset = mpt::CharsetCP437; + + m_nDefaultSpeed = fileHeader.speed; + m_nDefaultTempo.Set(143); + Order().SetRestartPos(fileHeader.restartPos); + m_nSamples = 64; + m_nChannels = 4 + 9; + m_playBehaviour.set(kOPLBeatingOscillators); + + // Pan PCM channels only + for(CHANNELINDEX chn = 0; chn < 4; chn++) + { + ChnSettings[chn].nPan = (chn & 1) ? 192 : 64; + } + + // PCM instruments + for(SAMPLEINDEX smp = 0; smp < 32; smp++) + { + ModSample &mptSmp = Samples[smp + 1]; + mptSmp.Initialize(MOD_TYPE_S3M); + mpt::String::Read(m_szNames[smp + 1], fileHeader.sampleNames[smp]); + mptSmp.nLength = fileHeader.samples[smp].length; + if(fileHeader.samples[smp].loopEnd <= fileHeader.samples[smp].length) + { + mptSmp.nLoopStart = fileHeader.samples[smp].loopStart; + mptSmp.nLoopEnd = fileHeader.samples[smp].loopEnd; + mptSmp.uFlags = CHN_LOOP; + } + mptSmp.nC5Speed = 8287; + } + // OPL instruments + for(SAMPLEINDEX smp = 0; smp < 32; smp++) + { + ModSample &mptSmp = Samples[smp + 33]; + mptSmp.Initialize(MOD_TYPE_S3M); + mpt::String::Read(m_szNames[smp + 33], fileHeader.fmInstrNames[smp]); + // Reorder OPL patch bytes (interleave modulator and carrier) + const auto &fm = fileHeader.fmInstr[smp]; + OPLPatch patch{{}}; + patch[0] = fm[1]; patch[1] = fm[6]; + patch[2] = fm[2]; patch[3] = fm[7]; + patch[4] = fm[3]; patch[5] = fm[8]; + patch[6] = fm[4]; patch[7] = fm[9]; + patch[8] = fm[5]; patch[9] = fm[10]; + patch[10] = fm[0]; + mptSmp.SetAdlib(true, patch); + } + + ReadOrderFromArray(Order(), fileHeader.orders, 256, 0xFF); + Patterns.ResizeArray(128); + for(PATTERNINDEX pat = 0; pat < 128; pat++) + { + file.Seek(2978 + patOffsets[pat]); + FileReader patChunk = file.ReadChunk(patLengths[pat]); + if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) + { + continue; + } + CPattern &pattern = Patterns[pat]; + ROWINDEX row = 0; + while(row < 64 && patChunk.CanRead(1)) + { + uint8 cmd = patChunk.ReadUint8(); + if(cmd <= 0x0C) + { + // Note, instrument, volume + ModCommand &m = *pattern.GetpModCommand(row, cmd); + uint8 data[2]; + patChunk.ReadArray(data); + uint8 note = data[0], instrVol = data[1]; + bool fmChn = (cmd >= 4); + m.note = NOTE_MIN + (fmChn ? 12 : 36) + (note & 0x0F) + ((note >> 4) & 0x07) * 12; + m.instr = (fmChn ? 33 : 1) + (instrVol >> 4) + ((note & 0x80) >> 3); + TranslateVolume(m, instrVol, fmChn); + } else if(cmd >= 0x20 && cmd <= 0x2C) + { + // Volume + TranslateVolume(*pattern.GetpModCommand(row, cmd - 0x20), patChunk.ReadUint8(), cmd >= 0x24); + } else if(cmd == 0x40) + { + // Delay (row done) + row += patChunk.ReadUint8(); + } else if(cmd == 0x60) + { + // End of pattern + if(row > 0) + { + pattern.GetpModCommand(row - 1, 0)->command = CMD_PATTERNBREAK; + } + break; + } else + { + return false; + } + } + } + if(loadFlags & loadSampleData) + { + for(SAMPLEINDEX smp = 1; smp <= 32; smp++) + { + SampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::unsignedPCM).ReadSample(Samples[smp], file); + } + } + return true; +} + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp index e17ec3fdd..d7b7bb25c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp @@ -11,7 +11,7 @@ #include "stdafx.h" #include "Loaders.h" #include "ChunkReader.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" #ifndef NO_PLUGINS #include "plugins/DigiBoosterEcho.h" #endif // NO_PLUGINS @@ -39,17 +39,17 @@ struct DBMChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idNAME = MAGIC4BE('N','A','M','E'), - idINFO = MAGIC4BE('I','N','F','O'), - idSONG = MAGIC4BE('S','O','N','G'), - idINST = MAGIC4BE('I','N','S','T'), - idVENV = MAGIC4BE('V','E','N','V'), - idPENV = MAGIC4BE('P','E','N','V'), - idPATT = MAGIC4BE('P','A','T','T'), - idPNAM = MAGIC4BE('P','N','A','M'), - idSMPL = MAGIC4BE('S','M','P','L'), - idDSPE = MAGIC4BE('D','S','P','E'), - idMPEG = MAGIC4BE('M','P','E','G'), + idNAME = MagicBE("NAME"), + idINFO = MagicBE("INFO"), + idSONG = MagicBE("SONG"), + idINST = MagicBE("INST"), + idVENV = MagicBE("VENV"), + idPENV = MagicBE("PENV"), + idPATT = MagicBE("PATT"), + idPNAM = MagicBE("PNAM"), + idSMPL = MagicBE("SMPL"), + idDSPE = MagicBE("DSPE"), + idMPEG = MagicBE("MPEG"), }; uint32be id; @@ -196,18 +196,15 @@ static void ConvertDBMEffect(uint8 &command, uint8 ¶m) case CMD_MODCMDEX: switch(param & 0xF0) { - case 0x00: // set filter - command = CMD_NONE; - break; - case 0x30: // play backwards + case 0x30: // Play backwards command = CMD_S3MCMDEX; param = 0x9F; break; - case 0x40: // turn off sound in channel (volume / portamento commands after this can't pick up the note anymore) + case 0x40: // Turn off sound in channel (volume / portamento commands after this can't pick up the note anymore) command = CMD_S3MCMDEX; param = 0xC0; break; - case 0x50: // turn on/off channel + case 0x50: // Turn on/off channel // TODO: Apparently this should also kill the playing note. if((param & 0x0F) <= 0x01) { @@ -215,8 +212,6 @@ static void ConvertDBMEffect(uint8 &command, uint8 ¶m) param = (param == 0x50) ? 0x00 : 0x40; } break; - case 0x60: // Pattern loop - break; case 0x70: // Coarse offset command = CMD_S3MCMDEX; param = 0xA0 | (param & 0x0F); @@ -234,7 +229,7 @@ static void ConvertDBMEffect(uint8 &command, uint8 ¶m) case CMD_KEYOFF: if (param == 0) { - // TODO key of at tick 0 + // TODO key off at tick 0 } break; @@ -352,11 +347,15 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) m_nChannels = Clamp(infoData.channels, 1, MAX_BASECHANNELS); // note: MAX_BASECHANNELS is currently 127, but DBPro 2 supports up to 128 channels, DBPro 3 apparently up to 254. m_nInstruments = std::min(infoData.instruments, MAX_INSTRUMENTS - 1); m_nSamples = std::min(infoData.samples, MAX_SAMPLES - 1); - m_madeWithTracker = mpt::format(MPT_USTRING("DigiBooster Pro %1.%2"))(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo)); m_playBehaviour.set(kSlidesAtSpeed1); m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITArpeggio); + m_modFormat.formatName = U_("DigiBooster Pro"); + m_modFormat.type = U_("dbm"); + m_modFormat.madeWithTracker = mpt::format(U_("DigiBooster Pro %1.%2"))(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo)); + m_modFormat.charset = mpt::CharsetISO8859_1; + // Name chunk FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME); nameChunk.ReadString(m_songName, nameChunk.GetLength()); @@ -621,10 +620,10 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) for(uint32 i = 0; i < 32; i++) { uint32 param = (i * 127u) / 32u; - sprintf(m_MidiCfg.szMidiZXXExt[i ], "F0F080%02X", param); - sprintf(m_MidiCfg.szMidiZXXExt[i + 32], "F0F081%02X", param); - sprintf(m_MidiCfg.szMidiZXXExt[i + 64], "F0F082%02X", param); - sprintf(m_MidiCfg.szMidiZXXExt[i + 96], "F0F083%02X", param); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i ]) = mpt::format("F0F080%1")(mpt::fmt::HEX0<2>(param)); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i + 32]) = mpt::format("F0F081%1")(mpt::fmt::HEX0<2>(param)); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i + 64]) = mpt::format("F0F082%1")(mpt::fmt::HEX0<2>(param)); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i + 96]) = mpt::format("F0F083%1")(mpt::fmt::HEX0<2>(param)); } } #endif // NO_PLUGINS @@ -666,10 +665,10 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) // Read whole MPEG stream into one sample and then split it up. FileReader chunk = mpegChunk.GetChunk(mpegChunk.BytesLeft()); - if(ReadMP3Sample(0, chunk)) + if(ReadMP3Sample(0, chunk, true)) { ModSample &srcSample = Samples[0]; - const int8 *smpData = srcSample.pSample8; + const mpt::byte *smpData = srcSample.sampleb(); SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000); LimitMax(predelay, srcSample.nLength); smpData += predelay * srcSample.GetBytesPerSample(); @@ -683,7 +682,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(sample.nLength) { sample.AllocateSample(); - memcpy(sample.pSample, smpData, sample.GetSampleSizeInBytes()); + memcpy(sample.sampleb(), smpData, sample.GetSampleSizeInBytes()); smpData += sample.GetSampleSizeInBytes(); srcSample.nLength -= sample.nLength; SmpLength gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp index 90c91d9e2..5029f67e8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp @@ -126,7 +126,11 @@ bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) m_nChannels = fileHeader.numChannels; m_nSamples = 31; m_nSamplePreAmp = 256 / m_nChannels; - m_madeWithTracker = mpt::format(MPT_USTRING("Digi Booster %1.%2"))(fileHeader.versionInt >> 4, fileHeader.versionInt & 0x0F); + + m_modFormat.formatName = U_("DigiBooster"); + m_modFormat.type = U_("digi"); + m_modFormat.madeWithTracker = mpt::format(U_("Digi Booster %1.%2"))(fileHeader.versionInt >> 4, fileHeader.versionInt & 0x0F); + m_modFormat.charset = mpt::CharsetISO8859_1; ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.lastOrdIndex + 1); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp index c585e3ed8..b345243e0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp @@ -16,7 +16,7 @@ #include "stdafx.h" #include "Loaders.h" #include "ChunkReader.h" -#include +#include "BitReader.h" OPENMPT_NAMESPACE_BEGIN @@ -40,14 +40,14 @@ struct DMFChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idCMSG = MAGIC4LE('C','M','S','G'), // Song message - idSEQU = MAGIC4LE('S','E','Q','U'), // Order list - idPATT = MAGIC4LE('P','A','T','T'), // Patterns - idSMPI = MAGIC4LE('S','M','P','I'), // Sample headers - idSMPD = MAGIC4LE('S','M','P','D'), // Sample data - idSMPJ = MAGIC4LE('S','M','P','J'), // Sample jump table (XTracker 32 only) - idENDE = MAGIC4LE('E','N','D','E'), // Last four bytes of DMF file - idSETT = MAGIC4LE('S','E','T','T'), // Probably contains GUI settings + idCMSG = MagicLE("CMSG"), // Song message + idSEQU = MagicLE("SEQU"), // Order list + idPATT = MagicLE("PATT"), // Patterns + idSMPI = MagicLE("SMPI"), // Sample headers + idSMPD = MagicLE("SMPD"), // Sample data + idSMPJ = MagicLE("SMPJ"), // Sample jump table (XTracker 32 only) + idENDE = MagicLE("ENDE"), // Last four bytes of DMF file + idSETT = MagicLE("SETT"), // Probably contains GUI settings }; uint32le id; @@ -166,38 +166,24 @@ struct DMFPatternSettings { struct ChannelState { - ModCommand::NOTE noteBuffer; // Note buffer - ModCommand::NOTE lastNote; // Last played note on channel - uint8 vibratoType; // Last used vibrato type on channel - uint8 tremoloType; // Last used tremolo type on channel - uint8 highOffset; // Last used high offset on channel - bool playDir; // Sample play direction... false = forward (default) - - ChannelState() - { - noteBuffer = lastNote = NOTE_NONE; - vibratoType = 8; - tremoloType = 4; - highOffset = 6; - playDir = false; - } + ModCommand::NOTE noteBuffer = NOTE_NONE; // Note buffer + ModCommand::NOTE lastNote = NOTE_NONE; // Last played note on channel + uint8 vibratoType = 8; // Last used vibrato type on channel + uint8 tremoloType = 4; // Last used tremolo type on channel + uint8 highOffset = 6; // Last used high offset on channel + bool playDir = false; // Sample play direction... false = forward (default) }; - std::vector channels; // Memory for each channel's state - bool realBPMmode; // true = BPM mode - uint8 beat; // Rows per beat - uint8 tempoTicks; // Tick mode param - uint8 tempoBPM; // BPM mode param - uint8 internalTicks; // Ticks per row in final pattern + std::vector channels; // Memory for each channel's state + bool realBPMmode = false; // true = BPM mode + uint8 beat = 0; // Rows per beat + uint8 tempoTicks = 32; // Tick mode param + uint8 tempoBPM = 120; // BPM mode param + uint8 internalTicks = 6; // Ticks per row in final pattern - DMFPatternSettings(CHANNELINDEX numChannels) : channels(numChannels) - { - realBPMmode = false; - beat = 0; - tempoTicks = 32; - tempoBPM = 120; - internalTicks = 6; - } + DMFPatternSettings(CHANNELINDEX numChannels) + : channels(numChannels) + { } }; @@ -254,9 +240,9 @@ static uint8 DMFvibrato2MPT(uint8 val, const uint8 internalTicks) { // MPT: 1 vibrato period == 64 ticks... we have internalTicks ticks per row. // X-Tracker: Period length specified in rows! - const int periodInTicks = MAX(1, (val >> 4)) * internalTicks; - const uint8 matchingPeriod = (uint8)Clamp((128 / periodInTicks), 1, 15); - return (matchingPeriod << 4) | MAX(1, (val & 0x0F)); + const int periodInTicks = std::max(1, (val >> 4)) * internalTicks; + const uint8 matchingPeriod = static_cast(Clamp((128 / periodInTicks), 1, 15)); + return (matchingPeriod << 4) | std::max(1, (val & 0x0F)); } @@ -609,7 +595,7 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett } break; case 5: // Tremolo Retrig Sample (who invented those stupid effect names?) - effectParam1 = MAX(1, DMFdelay2MPT(effectParam1, settings.internalTicks)); + effectParam1 = std::max(uint8(1), DMFdelay2MPT(effectParam1, settings.internalTicks)); effect1 = CMD_RETRIG; settings.channels[chn].playDir = false; break; @@ -659,7 +645,7 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett case 1: // Note Finetune effect2 = static_cast(effectParam2 < 128 ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN); if(effectParam2 > 128) effectParam2 = 255 - effectParam2 + 1; - effectParam2 = 0xF0 | MIN(0x0F, effectParam2); // Well, this is not too accurate... + effectParam2 = 0xF0 | std::min(uint8(0x0F), effectParam2); // Well, this is not too accurate... break; case 2: // Note Delay (wtf is the difference to Sample Delay?) effectParam2 = DMFdelay2MPT(effectParam2, settings.internalTicks); @@ -926,6 +912,11 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_DMF); + + m_modFormat.formatName = mpt::format(U_("X-Tracker v%1"))(fileHeader.version); + m_modFormat.type = U_("dmf"); + m_modFormat.charset = mpt::CharsetCP437; + mpt::String::Read(m_songName, fileHeader.songname); { std::string artist; @@ -934,7 +925,6 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) } FileHistory mptHistory; - MemsetZero(mptHistory); mptHistory.loadDate.tm_mday = Clamp(fileHeader.creationDay, uint8(1), uint8(31)); mptHistory.loadDate.tm_mon = Clamp(fileHeader.creationMonth, uint8(1), uint8(12)) - 1; mptHistory.loadDate.tm_year = fileHeader.creationYear; @@ -963,37 +953,34 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) chunk.ReadStruct(patHeader); m_nChannels = Clamp(patHeader.numTracks, 1, 32) + 1; // + 1 for global track (used for tempo stuff) - std::vector patternChunks; - patternChunks.reserve(patHeader.numPatterns); - // First, find out where all of our patterns are... - for(PATTERNINDEX pat = 0; pat < patHeader.numPatterns; pat++) + std::vector patternChunks(patHeader.numPatterns); + for(auto &patternChunk : patternChunks) { DMFPatternHeader header; chunk.ReadStruct(header); chunk.SkipBack(sizeof(header)); - patternChunks.push_back(chunk.ReadChunk(sizeof(header) + header.patternLength)); + patternChunk = chunk.ReadChunk(sizeof(header) + header.patternLength); } // Now go through the order list and load them. DMFPatternSettings settings(GetNumChannels()); Patterns.ResizeArray(Order().GetLength()); - for(ORDERINDEX ord = 0; ord < Order().GetLength(); ord++) + for(PATTERNINDEX &pat : Order()) { // Create one pattern for each order item, as the same pattern can be played with different settings - PATTERNINDEX pat = Order()[ord]; if(pat < patternChunks.size()) { pat = ConvertDMFPattern(patternChunks[pat], settings, *this); - Order()[ord] = pat; - // Loop end? - if(pat != PATTERNINDEX_INVALID && ord == seqHeader.loopEnd && (seqHeader.loopStart > 0 || ord < Order().GetLastIndex())) - { - Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, static_cast(seqHeader.loopStart)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow()); - } } } + // Write loop end if necessary + if(Order().IsValidPat(seqHeader.loopEnd) && (seqHeader.loopStart > 0 || seqHeader.loopEnd < Order().GetLastIndex())) + { + PATTERNINDEX pat = Order()[seqHeader.loopEnd]; + Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, static_cast(seqHeader.loopStart)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow()); + } } // Read song message @@ -1004,7 +991,7 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) // The skipped byte seems to always be 0. // This also matches how XT 1.03 itself displays the song message. chunk.Skip(1); - m_songMessage.ReadFixedLineLength(chunk, chunk.GetLength() - 1, 40, 0); + m_songMessage.ReadFixedLineLength(chunk, chunk.BytesLeft(), 40, 0); } // Read sample headers + data @@ -1064,53 +1051,34 @@ struct DMFHNode struct DMFHTree { - const uint8 *ibuf, *ibufmax; - uint32 bitbuf; - int bitnum; + BitReader file; int lastnode, nodecount; DMFHNode nodes[256]; - // DMF Huffman ReadBits - uint8 DMFReadBits(int nbits) + DMFHTree(FileReader &file) + : file(file) + , lastnode(0) + , nodecount(0) { - if(bitnum < nbits) - { - if(ibuf < ibufmax) - { - bitbuf |= (((uint32)(*ibuf++)) << bitnum); - bitnum += 8; - } else - { - throw std::range_error("Truncated DMF sample block"); - } - } - - uint8 v = static_cast(bitbuf & ((1 << nbits) - 1)); - bitbuf >>= nbits; - bitnum -= nbits; - return v; + MemsetZero(nodes); } - - + // // tree: [8-bit value][12-bit index][12-bit index] = 32-bit // void DMFNewNode() { - uint8 isleft, isright; - int actnode; - - actnode = nodecount; + int actnode = nodecount; if(actnode > 255) return; - nodes[actnode].value = DMFReadBits(7); - isleft = DMFReadBits(1); - isright = DMFReadBits(1); + nodes[actnode].value = static_cast(file.ReadBits(7)); + bool isLeft = file.ReadBits(1) != 0; + bool isRight = file.ReadBits(1) != 0; actnode = lastnode; if(actnode > 255) return; nodecount++; lastnode = nodecount; - if(isleft) + if(isLeft) { nodes[actnode].left = (int16)lastnode; DMFNewNode(); @@ -1119,7 +1087,7 @@ struct DMFHTree nodes[actnode].left = -1; } lastnode = nodecount; - if(isright) + if(isRight) { nodes[actnode].right = (int16)lastnode; DMFNewNode(); @@ -1131,25 +1099,21 @@ struct DMFHTree }; -uintptr_t DMFUnpack(uint8 *psample, const uint8 *ibuf, const uint8 *ibufmax, uint32 maxlen) +uintptr_t DMFUnpack(FileReader &file, uint8 *psample, uint32 maxlen) { - DMFHTree tree; - - MemsetZero(tree); - tree.ibuf = ibuf; - tree.ibufmax = ibufmax; - tree.DMFNewNode(); + DMFHTree tree(file); uint8 value = 0, delta = 0; try { + tree.DMFNewNode(); for(uint32 i = 0; i < maxlen; i++) { int actnode = 0; - uint8 sign = tree.DMFReadBits(1); + bool sign = tree.file.ReadBits(1) != 0; do { - if(tree.DMFReadBits(1)) + if(tree.file.ReadBits(1)) actnode = tree.nodes[actnode].right; else actnode = tree.nodes[actnode].left; @@ -1160,11 +1124,11 @@ uintptr_t DMFUnpack(uint8 *psample, const uint8 *ibuf, const uint8 *ibufmax, uin value += delta; psample[i] = value; } - } catch(const std::range_error &) + } catch(const BitReader::eof &) { //AddToLog(LogWarning, "Truncated DMF sample block"); } - return tree.ibuf - ibuf; + return tree.file.GetPosition(); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp index d5a3db99c..3f01635dd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp @@ -207,6 +207,11 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_DSM); + + m_modFormat.formatName = U_("DSIK Format"); + m_modFormat.type = U_("dsm"); + m_modFormat.charset = mpt::CharsetCP437; + mpt::String::Read(m_songName, songHeader.songName); m_nChannels = std::max(songHeader.numChannels, 1); m_nDefaultSpeed = songHeader.speed; @@ -252,19 +257,17 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) ModCommand dummy = ModCommand::Empty(); ROWINDEX row = 0; - PatternRow rowBase = Patterns[patNum].GetRow(0); while(chunk.CanRead(1) && row < 64) { uint8 flag = chunk.ReadUint8(); if(!flag) { row++; - rowBase = Patterns[patNum].GetRow(row); continue; } CHANNELINDEX chn = (flag & 0x0F); - ModCommand &m = (chn < GetNumChannels() ? rowBase[chn] : dummy); + ModCommand &m = (chn < GetNumChannels() ? *Patterns[patNum].GetpModCommand(row, chn) : dummy); if(flag & 0x80) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp index c821d9335..85de0e0cb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp @@ -17,8 +17,8 @@ OPENMPT_NAMESPACE_BEGIN enum PatternFormats : uint32 { DTM_PT_PATTERN_FORMAT = 0, - DTM_204_PATTERN_FORMAT = MAGIC4BE('2', '.', '0', '4'), - DTM_206_PATTERN_FORMAT = MAGIC4BE('2', '.', '0', '6'), + DTM_204_PATTERN_FORMAT = MagicBE("2.04"), + DTM_206_PATTERN_FORMAT = MagicBE("2.06"), }; @@ -44,17 +44,17 @@ struct DTMChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idS_Q_ = MAGIC4BE('S', '.', 'Q', '.'), - idPATT = MAGIC4BE('P', 'A', 'T', 'T'), - idINST = MAGIC4BE('I', 'N', 'S', 'T'), - idIENV = MAGIC4BE('I', 'E', 'N', 'V'), - idDAPT = MAGIC4BE('D', 'A', 'P', 'T'), - idDAIT = MAGIC4BE('D', 'A', 'I', 'T'), - idTEXT = MAGIC4BE('T', 'E', 'X', 'T'), - idPATN = MAGIC4BE('P', 'A', 'T', 'N'), - idTRKN = MAGIC4BE('T', 'R', 'K', 'N'), - idVERS = MAGIC4BE('V', 'E', 'R', 'S'), - idSV19 = MAGIC4BE('S', 'V', '1', '9'), + idS_Q_ = MagicBE("S.Q."), + idPATT = MagicBE("PATT"), + idINST = MagicBE("INST"), + idIENV = MagicBE("IENV"), + idDAPT = MagicBE("DAPT"), + idDAIT = MagicBE("DAIT"), + idTEXT = MagicBE("TEXT"), + idPATN = MagicBE("PATN"), + idTRKN = MagicBE("TRKN"), + idVERS = MagicBE("VERS"), + idSV19 = MagicBE("SV19"), }; uint32be id; @@ -586,17 +586,22 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) } // Is this accurate? + mpt::ustring tracker; if(patternFormat == DTM_206_PATTERN_FORMAT) { - m_madeWithTracker = MPT_USTRING("Digital Home Studio"); + tracker = U_("Digital Home Studio"); } else if(FileReader chunk = chunks.GetChunk(DTMChunk::idVERS)) { uint32 version = chunk.ReadUint32BE(); - m_madeWithTracker = mpt::format(MPT_USTRING("Digital Tracker %1.%2"))(version >> 4, version & 0x0F); + tracker = mpt::format(U_("Digital Tracker %1.%2"))(version >> 4, version & 0x0F); } else { - m_madeWithTracker = MPT_USTRING("Digital Tracker"); + tracker = U_("Digital Tracker"); } + m_modFormat.formatName = U_("Digital Tracker"); + m_modFormat.type = U_("dtm"); + m_modFormat.madeWithTracker = std::move(tracker); + m_modFormat.charset = mpt::CharsetISO8859_1; return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp index d7425a188..d902c74f1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp @@ -169,6 +169,10 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultTempo.Set(80); m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; + m_modFormat.formatName = U_("Farandole Composer"); + m_modFormat.type = U_("far"); + m_modFormat.charset = mpt::CharsetCP437; + mpt::String::Read(m_songName, fileHeader.songName); // Read channel settings diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp index 8125b8ac8..33492ce04 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp @@ -94,6 +94,23 @@ static const MODTYPE gdmFormatOrigin[] = { MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM }; +static const MPT_UCHAR_TYPE gdmFormatOriginType[][4] = +{ + UL_(""), UL_("mod"), UL_("mtm"), UL_("s3m"), UL_("669"), UL_("far"), UL_("ult"), UL_("stm"), UL_("med"), UL_("psm") +}; +static const MPT_UCHAR_TYPE * const gdmFormatOriginFormat[] = +{ + UL_(""), + UL_("Generic MOD"), + UL_("MultiTracker"), + UL_("ScreamTracker 3"), + UL_("Composer 669 / UNIS 669"), + UL_("Farandole Composer"), + UL_("UltraTracker"), + UL_("ScreamTracker 2"), + UL_("OctaMED"), + UL_("Epic Megagames MASI") +}; static bool ValidateHeader(const GDMFileHeader &fileHeader) @@ -146,8 +163,13 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]); - m_ContainerType = MOD_CONTAINERTYPE_GDM; - m_madeWithTracker = mpt::format(MPT_USTRING("BWSB 2GDM %1.%2 (converted from %3)"))(fileHeader.trackerMajorVer, fileHeader.formatMinorVer, ModTypeToTracker(GetType())); + + m_modFormat.formatName = U_("General Digital Music"); + m_modFormat.type = U_("gdm"); + m_modFormat.madeWithTracker = mpt::format(U_("BWSB 2GDM %1.%2"))(fileHeader.trackerMajorVer, fileHeader.formatMinorVer); + m_modFormat.originalType = gdmFormatOriginType[fileHeader.originalFormat]; + m_modFormat.originalFormatName = gdmFormatOriginFormat[fileHeader.originalFormat]; + m_modFormat.charset = mpt::CharsetCP437; // Song name mpt::String::Read(m_songName, fileHeader.songTitle); @@ -218,7 +240,7 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) mpt::String::Read(sample.filename, gdmSample.fileName); sample.nC5Speed = gdmSample.c4Hertz; - sample.nGlobalVol = 256; // Not supported in this format + sample.nGlobalVol = 64; // Not supported in this format sample.nLength = gdmSample.length; // in bytes @@ -229,21 +251,20 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) sample.nLength /= 2; } - sample.nLoopStart = gdmSample.loopBegin; // in samples - sample.nLoopEnd = gdmSample.loopEnd - 1; // ditto - sample.FrequencyToTranspose(); // set transpose + finetune for mod files + sample.nLoopStart = gdmSample.loopBegin; + sample.nLoopEnd = gdmSample.loopEnd - 1; - // Fix transpose + finetune for some rare cases where transpose is not C-5 (e.g. sample 4 in wander2.gdm) - if(m_nType == MOD_TYPE_MOD) + if(UseFinetuneAndTranspose()) { - if(sample.RelativeTone > 0) + // Use the same inaccurate table as 2GDM for translating back to finetune, as our own routines + // give slightly different results for the provided sample rates that may result in transpose != 0. + static const uint16 rate2finetune[] = { 8363, 8424, 8485, 8547, 8608, 8671, 8734, 8797, 7894, 7951, 8009, 8067, 8125, 8184, 8244, 8303 }; + for(uint8 i = 0; i < 16; i++) { - sample.RelativeTone -= 1; - sample.nFineTune += 128; - } else if(sample.RelativeTone < 0) - { - sample.RelativeTone += 1; - sample.nFineTune -= 128; + if(sample.nC5Speed == rate2finetune[i]) + { + sample.nFineTune = MOD2XMFineTune(i); + } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp index 411689cb8..72da4c02d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp @@ -355,8 +355,7 @@ static bool ValidateHeader(const IMFFileHeader &fileHeader) { if(std::memcmp(fileHeader.im10, "IM10", 4) || fileHeader.ordNum > 256 - || fileHeader.insNum >= MAX_INSTRUMENTS - ) + || fileHeader.insNum >= MAX_INSTRUMENTS) { return false; } @@ -388,8 +387,7 @@ static bool ValidateHeader(const IMFFileHeader &fileHeader) static uint64 GetHeaderMinimumAdditionalSize(const IMFFileHeader &fileHeader) { - MPT_UNREFERENCED_PARAMETER(fileHeader); - return 256; + return 256 + fileHeader.patNum * 4 + fileHeader.insNum * sizeof(IMFInstrument); } @@ -424,6 +422,10 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) { return false; } + if(loadFlags == onlyVerifyHeader) + { + return true; + } // Read channel configuration std::bitset<32> ignoreChannels; // bit set for each channel that's completely disabled @@ -450,23 +452,17 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) ignoreChannels[chn] = true; break; default: // uhhhh.... freak out - //fprintf(stderr, "imf: channel %d has unknown status %d\n", n, hdr.channels[n].status); return false; } } - if(!detectedChannels) - { - return false; - } - - if(loadFlags == onlyVerifyHeader) - { - return true; - } InitializeGlobals(MOD_TYPE_IMF); m_nChannels = detectedChannels; + m_modFormat.formatName = U_("Imago Orpheus"); + m_modFormat.type = U_("imf"); + m_modFormat.charset = mpt::CharsetCP437; + //From mikmod: work around an Orpheus bug if(fileHeader.channels[0].status == 0) { @@ -482,7 +478,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) // Song Name mpt::String::Read(m_songName, fileHeader.title); - m_SongFlags = (fileHeader.flags & IMFFileHeader::linearSlides) ? SONG_LINEARSLIDES : SongFlags(0); + m_SongFlags.set(SONG_LINEARSLIDES, fileHeader.flags & IMFFileHeader::linearSlides); m_nDefaultSpeed = fileHeader.tempo; m_nDefaultTempo.Set(fileHeader.bpm); m_nDefaultGlobalVolume = Clamp(fileHeader.master, 0, 64) * 4; @@ -508,7 +504,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) continue; } - ModCommand junkNote; + ModCommand dummy; ROWINDEX row = 0; while(row < numRows) { @@ -520,7 +516,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) } uint8 channel = mask & 0x1F; - ModCommand &m = ignoreChannels[channel] ? junkNote : *Patterns[pat].GetpModCommand(row, channel); + ModCommand &m = (channel < GetNumChannels()) ? *Patterns[pat].GetpModCommand(row, channel) : dummy; if(mask & 0x20) { @@ -591,6 +587,8 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) } if(m.command) ImportIMFEffect(m); + if(ignoreChannels[channel] && m.IsGlobalCommand()) + m.command = CMD_NONE; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp index fa6fc2a73..83a022590 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp @@ -343,21 +343,6 @@ static void CopyPatternName(CPattern &pattern, FileReader &file) } -// Date calculation derived from https://alcor.concordia.ca/~gpkatch/gdate-algorithm.html -template -struct SchismVersionFromDate -{ - static const int32 mm = (m + 9) % 12; - static const int32 yy = y - mm / 10; - static const int32 date = yy * 365 + yy / 4 - yy / 100 + yy / 400 + (mm * 306 + 5) / 10 + (d - 1); - - static constexpr int32 Version() - { - return 0x1050 + date - SchismVersionFromDate<2009, 10, 31>::date; - } -}; - - // Get version of Schism Tracker that was used to create an IT/S3M file. mpt::ustring CSoundFile::GetSchismTrackerVersion(uint16 cwtv) { @@ -380,13 +365,13 @@ mpt::ustring CSoundFile::GetSchismTrackerVersion(uint16 cwtv) ddd = date - (365 * y + y / 4 - y / 100 + y / 400); } int32 mi = (100 * ddd + 52) / 3060; - version = mpt::format(MPT_USTRING("Schism Tracker %1-%2-%3"))( + version = mpt::format(U_("Schism Tracker %1-%2-%3"))( mpt::ufmt::dec0<4>(y + (mi + 2) / 12), mpt::ufmt::dec0<2>((mi + 2) % 12 + 1), mpt::ufmt::dec0<2>(ddd - (mi * 306 + 5) / 10 + 1)); } else { - version = mpt::format(MPT_USTRING("Schism Tracker 0.%1"))(mpt::ufmt::hex(cwtv)); + version = mpt::format(U_("Schism Tracker 0.%1"))(mpt::ufmt::hex(cwtv)); } return version; } @@ -451,6 +436,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_IT); bool interpretModPlugMade = false; + mpt::ustring madeWithTracker; // OpenMPT crap at the end of file size_t mptStartPos = 0; @@ -492,7 +478,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) { // OpenMPT Version number (Major.Minor) // This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used. - m_dwLastSavedWithVersion = (fileHeader.cwtv & 0x0FFF) << 16; + m_dwLastSavedWithVersion = Version((fileHeader.cwtv & 0x0FFF) << 16); if(!memcmp(&fileHeader.reserved, "OMPT", 4)) interpretModPlugMade = true; } else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888) @@ -507,20 +493,20 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) { // ModPlug Tracker 1.16 (semi-raped IT format) or BeRoTracker (will be determined later) m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker 1.09 - 1.16"); + madeWithTracker = U_("ModPlug Tracker 1.09 - 1.16"); } else { // OpenMPT 1.17 disguised as this in compatible mode, // but never writes 0xFF in the pan map for unused channels (which is an invalid value). m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); - m_madeWithTracker = MPT_USTRING("OpenMPT 1.17 (compatibility export)"); + madeWithTracker = U_("OpenMPT 1.17 (compatibility export)"); } interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && fileHeader.reserved == 0) { // ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker b3.3 - 1.09"); + madeWithTracker = U_("ModPlug Tracker b3.3 - 1.09"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0300 && fileHeader.cmwt == 0x0300 && fileHeader.reserved == 0 && fileHeader.ordnum == 256 && fileHeader.sep == 128 && fileHeader.pwd == 0) { @@ -551,7 +537,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Luckily OpenMPT 1.17.03.02 should not be very wide-spread. // - In normal mode the time signature is always present in the song extensions anyway. So it's okay if we read // the signature here and maybe overwrite it later when parsing the song extensions. - if(m_dwLastSavedWithVersion == 0 || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02)) + if(!m_dwLastSavedWithVersion || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02)) { m_nDefaultRowsPerBeat = fileHeader.highlight_minor; m_nDefaultRowsPerMeasure = fileHeader.highlight_major; @@ -644,7 +630,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } if(oldUNMO3) { - m_madeWithTracker = MPT_USTRING("UNMO3 <= 2.4"); + madeWithTracker = U_("UNMO3 <= 2.4"); } } @@ -671,9 +657,9 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(possiblyUNMO3 && nflt == 0) { if(fileHeader.special & ITFileHeader::embedPatternHighlights) - m_madeWithTracker = MPT_USTRING("UNMO3 <= 2.4.0.1"); // Set together with MIDI macro embed flag + madeWithTracker = U_("UNMO3 <= 2.4.0.1"); // Set together with MIDI macro embed flag else - m_madeWithTracker = MPT_USTRING("UNMO3"); // Either 2.4.0.2+ or no MIDI macros embedded + madeWithTracker = U_("UNMO3"); // Either 2.4.0.2+ or no MIDI macros embedded } } else { @@ -688,7 +674,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Otherwise we end up here and might have to read the edit history length. if(file.ReadUint16LE() == 0) { - m_madeWithTracker = MPT_USTRING("UNMO3 <= 2.4"); + madeWithTracker = U_("UNMO3 <= 2.4"); } else { // These were not zero bytes, but potentially belong to the upcoming MIDI config - need to skip back. @@ -734,7 +720,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Read mix plugins information FileReader pluginChunk = file.ReadChunk((minPtr >= file.GetPosition()) ? minPtr - file.GetPosition() : file.BytesLeft()); - LoadMixPlugins(pluginChunk); + const bool isBeRoTracker = LoadMixPlugins(pluginChunk); // Read Song Message if((fileHeader.special & ITFileHeader::embedSongMessage) && fileHeader.msglength > 0 && file.Seek(fileHeader.msgoffset)) @@ -794,7 +780,15 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) continue; lastSampleCompressed = false; - if(!sample.uFlags[SMP_KEEPONDISK]) + if(sample.uFlags[CHN_ADLIB]) + { + // FM instrument in MPTM + OPLPatch patch; + if(file.ReadArray(patch)) + { + sample.SetAdlib(true, patch); + } + } else if(!sample.uFlags[SMP_KEEPONDISK]) { SampleIO sampleIO = sampleHeader.GetSampleFormat(fileHeader.cwtv); if(loadFlags & loadSampleData) @@ -825,7 +819,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) #if defined(MPT_EXTERNAL_SAMPLES) SetSamplePath(i + 1, mpt::PathString::FromUTF8(filenameU8)); #elif !defined(LIBOPENMPT_BUILD_TEST) - AddToLog(LogWarning, mpt::format(MPT_USTRING("Loading external sample %1 ('%2') failed: External samples are not supported."))(i, mpt::ToUnicode(mpt::CharsetUTF8, filenameU8))); + AddToLog(LogWarning, mpt::format(U_("Loading external sample %1 ('%2') failed: External samples are not supported."))(i, mpt::ToUnicode(mpt::CharsetUTF8, filenameU8))); #endif // MPT_EXTERNAL_SAMPLES } else { @@ -857,7 +851,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) possibleXMconversion = false; } if(possibleXMconversion) - m_madeWithTracker = MPT_USTRING("XM Conversion"); + madeWithTracker = U_("XM Conversion"); } m_nMinPeriod = 0; @@ -966,8 +960,8 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } // Load instrument and song extensions. - LoadExtendedInstrumentProperties(file, &interpretModPlugMade); - if(interpretModPlugMade && m_madeWithTracker != MPT_USTRING("BeRoTracker")) + interpretModPlugMade |= LoadExtendedInstrumentProperties(file); + if(interpretModPlugMade && !isBeRoTracker) { m_playBehaviour.reset(); m_nMixLevels = mixLevelsOriginal; @@ -1136,59 +1130,63 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); } - if(m_dwLastSavedWithVersion && m_madeWithTracker.empty()) + if(m_dwLastSavedWithVersion && madeWithTracker.empty()) { - m_madeWithTracker = MPT_USTRING("OpenMPT ") + MptVersion::ToUString(m_dwLastSavedWithVersion); + madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); if(memcmp(&fileHeader.reserved, "OMPT", 4) && (fileHeader.cwtv & 0xF000) == 0x5000) { - m_madeWithTracker += MPT_USTRING(" (compatibility export)"); - } else if(MptVersion::IsTestBuild(m_dwLastSavedWithVersion)) + madeWithTracker += U_(" (compatibility export)"); + } else if(m_dwLastSavedWithVersion.IsTestVersion()) { - m_madeWithTracker += MPT_USTRING(" (test build)"); + madeWithTracker += U_(" (test build)"); } } else { switch(fileHeader.cwtv >> 12) { case 0: - if(!m_madeWithTracker.empty()) + if(isBeRoTracker) { - // BeRoTracker has been detected above. + // Old versions + madeWithTracker = U_("BeRoTracker"); } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.flags == 9 && fileHeader.special == 0 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.insnum == 0 && fileHeader.patnum + 1 == fileHeader.ordnum && fileHeader.globalvol == 128 && fileHeader.mv == 100 && fileHeader.speed == 1 && fileHeader.sep == 128 && fileHeader.pwd == 0 && fileHeader.msglength == 0 && fileHeader.msgoffset == 0 && fileHeader.reserved == 0) { - m_madeWithTracker = MPT_USTRING("OpenSPC conversion"); - } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.reserved == 0) + madeWithTracker = U_("OpenSPC conversion"); + } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.reserved == 0) { // ModPlug Tracker 1.00a5, instruments 560 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker 1.00a5"); + madeWithTracker = U_("ModPlug Tracker 1.00a5"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(&fileHeader.reserved, "CHBI", 4)) { - m_madeWithTracker = MPT_USTRING("ChibiTracker"); + madeWithTracker = U_("ChibiTracker"); } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && fileHeader.special <= 1 && fileHeader.pwd == 0 && fileHeader.reserved == 0 && (fileHeader.flags & (ITFileHeader::vol0Optimisations | ITFileHeader::instrumentMode | ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig | ITFileHeader::extendedFilterRange)) == ITFileHeader::instrumentMode && m_nSamples > 0 && !strcmp(Samples[1].filename, "XXXXXXXX.YYY")) { - m_madeWithTracker = MPT_USTRING("CheeseTracker"); - } else if(fileHeader.cmwt < 0x0300) + madeWithTracker = U_("CheeseTracker"); + } else if(fileHeader.cwtv == 0) + { + madeWithTracker = U_("Unknown"); + } else if(fileHeader.cmwt < 0x0300 && madeWithTracker.empty()) { if(fileHeader.cmwt > 0x0214) { - m_madeWithTracker = MPT_USTRING("Impulse Tracker 2.15"); + madeWithTracker = U_("Impulse Tracker 2.15"); } else if(fileHeader.cwtv > 0x0214) { // Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3) // p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change // anything as far as file saving is concerned. - m_madeWithTracker = mpt::format(MPT_USTRING("Impulse Tracker 2.14p%1"))(fileHeader.cwtv - 0x0214); + madeWithTracker = mpt::format(U_("Impulse Tracker 2.14p%1"))(fileHeader.cwtv - 0x0214); } else { - m_madeWithTracker = mpt::format(MPT_USTRING("Impulse Tracker %1.%2"))((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>((fileHeader.cwtv & 0xFF))); + madeWithTracker = mpt::format(U_("Impulse Tracker %1.%2"))((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>((fileHeader.cwtv & 0xFF))); } if(m_FileHistory.empty() && fileHeader.reserved != 0) { @@ -1204,14 +1202,13 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } FileHistory hist; - MemsetZero(hist); - hist.openTime = static_cast(editTime * (HISTORY_TIMER_PRECISION / 18.2f)); + hist.openTime = static_cast(editTime * (HISTORY_TIMER_PRECISION / 18.2)); m_FileHistory.push_back(hist); } } break; case 1: - m_madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv); + madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv); // Hertz in linear mode: Added 2015-01-29, https://github.com/schismtracker/schismtracker/commit/671b30311082a0e7df041fca25f989b5d2478f69 if(fileHeader.cwtv < SchismVersionFromDate<2015, 01, 29>::Version()) m_playBehaviour.reset(kHertzInLinearMode); @@ -1220,19 +1217,19 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kITShortSampleRetrig); break; case 4: - m_madeWithTracker = mpt::format(MPT_USTRING("pyIT %1.%2"))((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>((fileHeader.cwtv & 0xFF))); + madeWithTracker = mpt::format(U_("pyIT %1.%2"))((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF)); break; case 6: - m_madeWithTracker = MPT_USTRING("BeRoTracker"); + madeWithTracker = U_("BeRoTracker"); break; case 7: if(fileHeader.cwtv == 0x7FFF && fileHeader.cmwt == 0x0215) - m_madeWithTracker = MPT_USTRING("munch.py"); + madeWithTracker = U_("munch.py"); else - m_madeWithTracker = mpt::format(MPT_USTRING("ITMCK %1.%2.%3"))((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F); + madeWithTracker = mpt::format(U_("ITMCK %1.%2.%3"))((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F); break; case 0xD: - m_madeWithTracker = MPT_USTRING("spc2it"); + madeWithTracker = U_("spc2it"); break; } } @@ -1246,6 +1243,11 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } } + m_modFormat.formatName = (GetType() == MOD_TYPE_MPT) ? U_("OpenMPT MPTM") : mpt::format(U_("Impulse Tracker %1.%2"))(fileHeader.cmwt >> 8, mpt::ufmt::hex0<2>(fileHeader.cmwt & 0xFF)); + m_modFormat.type = (GetType() == MOD_TYPE_MPT) ? U_("mptm") : U_("it"); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::CharsetWindows1252 : mpt::CharsetCP437; + return true; } @@ -1257,7 +1259,7 @@ void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) if(cwtv >= 0x88D) { srlztn::SsbRead ssb(iStrm); - ssb.BeginRead("mptm", MptVersion::num); + ssb.BeginRead("mptm", Version::Current().GetRawVersion()); ssb.ReadItem(GetTuneSpecificTunings(), "0", &ReadTuningCollection); ssb.ReadItem(*this, "1", &ReadTuningMap); ssb.ReadItem(Order, "2", &ReadModSequenceOld); @@ -1266,7 +1268,7 @@ void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) if(ssb.GetStatus() & srlztn::SNT_FAILURE) { - AddToLog(LogError, MPT_USTRING("Unknown error occurred while deserializing file.")); + AddToLog(LogError, U_("Unknown error occurred while deserializing file.")); } } else { @@ -1274,7 +1276,7 @@ void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) std::string name; if(GetTuneSpecificTunings().Deserialize(iStrm, name) != Tuning::SerializationResult::Success) { - AddToLog(LogError, MPT_USTRING("Loading tune specific tunings failed.")); + AddToLog(LogError, U_("Loading tune specific tunings failed.")); } else { ReadTuningMapImpl(iStrm, *this, 0, cwtv < 0x88C); @@ -1286,7 +1288,7 @@ void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) #ifndef MODPLUG_NO_FILESAVE // Save edit history. Pass a null pointer for *f to retrieve the number of bytes that would be written. -static uint32 SaveITEditHistory(const CSoundFile &sndFile, FILE *f) +static uint32 SaveITEditHistory(const CSoundFile &sndFile, std::ostream *file) { size_t num = sndFile.GetFileHistory().size(); #ifdef MODPLUG_TRACKER @@ -1297,8 +1299,11 @@ static uint32 SaveITEditHistory(const CSoundFile &sndFile, FILE *f) uint16 fnum = mpt::saturate_cast(num); // Number of entries that are actually going to be written const uint32 bytesWritten = 2 + fnum * 8; // Number of bytes that are actually going to be written - if(f == nullptr) + if(!file) + { return bytesWritten; + } + std::ostream & f = *file; // Write number of history entries mpt::IO::WriteIntLE(f, fnum); @@ -1329,7 +1334,7 @@ static uint32 SaveITEditHistory(const CSoundFile &sndFile, FILE *f) else sndFile.AddToLog("Unable to retrieve current time."); - mptHistory.openTime = (uint32)(difftime(time(nullptr), creationTime) * (double)HISTORY_TIMER_PRECISION); + mptHistory.openTime = (uint32)(difftime(time(nullptr), creationTime) * HISTORY_TIMER_PRECISION); #endif // MODPLUG_TRACKER } @@ -1342,17 +1347,15 @@ static uint32 SaveITEditHistory(const CSoundFile &sndFile, FILE *f) } -bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExport) +bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool compatibilityExport) { + const CModSpecifications &specs = (GetType() == MOD_TYPE_MPT ? ModSpecs::mptm : (compatibilityExport ? ModSpecs::it : ModSpecs::itEx)); uint32 dwChnNamLen; ITFileHeader itHeader; uint64 dwPos = 0; uint32 dwHdrPos = 0, dwExtra = 0; - FILE *f; - - if(filename.empty() || ((f = mpt_fopen(filename, "wb")) == NULL)) return false; // Writing Header MemsetZero(itHeader); @@ -1384,9 +1387,9 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor itHeader.patnum = std::min(Patterns.GetNumPatterns(), specs.patternsMax); // Parapointers - std::vector patpos(itHeader.patnum, 0); - std::vector smppos(itHeader.smpnum, 0); - std::vector inspos(itHeader.insnum, 0); + std::vector patpos(itHeader.patnum); + std::vector smppos(itHeader.smpnum); + std::vector inspos(itHeader.insnum); //VERSION if(GetType() == MOD_TYPE_MPT) @@ -1397,7 +1400,7 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor } else { // IT - MptVersion::VersionNum vVersion = MptVersion::num; + uint32 vVersion = Version::Current().GetRawVersion(); itHeader.cwtv = 0x5000 | (uint16)((vVersion >> 16) & 0x0FFF); // format: txyy (t = tracker ID, x = version major, yy = version minor), e.g. 0x5117 (OpenMPT = 5, 117 = v1.17) itHeader.cmwt = 0x0214; // Common compatible tracker :) // Hack from schism tracker: @@ -1505,21 +1508,12 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor mpt::IO::Write(f, itHeader); Order().WriteAsByte(f, itHeader.ordnum); - for(uint16 i = 0; i < itHeader.insnum; ++i) - { - mpt::IO::WriteIntLE(f, inspos[i]); - } - for(uint16 i = 0; i < itHeader.smpnum; ++i) - { - mpt::IO::WriteIntLE(f, smppos[i]); - } - for(uint16 i = 0; i < itHeader.patnum; ++i) - { - mpt::IO::WriteIntLE(f, patpos[i]); - } + mpt::IO::Write(f, inspos); + mpt::IO::Write(f, smppos); + mpt::IO::Write(f, patpos); // Writing edit history information - SaveITEditHistory(*this, f); + SaveITEditHistory(*this, &f); // Writing midi cfg if(itHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) @@ -1558,7 +1552,7 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor // Writing mix plugins info if(!compatibilityExport) { - SaveMixPlugins(f, false); + SaveMixPlugins(&f, false); } // Writing song message @@ -1779,24 +1773,25 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor } } - fseek(f, dwPatPos, SEEK_SET); + mpt::IO::SeekAbsolute(f, dwPatPos); patinfo[0] = writeSize; mpt::IO::Write(f, patinfo); - fseek(f, static_cast(dwPos), SEEK_SET); + mpt::IO::SeekAbsolute(f, dwPos); } // Writing Sample Data for(SAMPLEINDEX smp = 1; smp <= itHeader.smpnum; smp++) { + const ModSample &sample = Samples[smp]; #ifdef MODPLUG_TRACKER uint32 type = GetType() == MOD_TYPE_IT ? 1 : 4; if(compatibilityExport) type = 2; - bool compress = ((((Samples[smp].GetNumChannels() > 1) ? TrackerSettings::Instance().MiscITCompressionStereo : TrackerSettings::Instance().MiscITCompressionMono) & type) != 0); + bool compress = ((((sample.GetNumChannels() > 1) ? TrackerSettings::Instance().MiscITCompressionStereo : TrackerSettings::Instance().MiscITCompressionMono) & type) != 0); #else bool compress = false; #endif // MODPLUG_TRACKER // Old MPT, DUMB and probably other libraries will only consider the IT2.15 compression flag if the header version also indicates IT2.15. // MilkyTracker <= 0.90.85 will only assume IT2.15 compression with cmwt == 0x215, ignoring the delta flag completely. - itss.ConvertToIT(Samples[smp], GetType(), compress, itHeader.cmwt >= 0x215, GetType() == MOD_TYPE_MPT); + itss.ConvertToIT(sample, GetType(), compress, itHeader.cmwt >= 0x215, GetType() == MOD_TYPE_MPT); const bool isExternal = itss.cvt == ITSample::cvtExternalSample; mpt::String::Write(itss.name, m_szNames[smp]); @@ -1810,22 +1805,22 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor itss.length = 0; } SmpLength smpLength = itss.length; // Possibly truncated to 2^32 samples - fseek(f, smppos[smp - 1], SEEK_SET); + mpt::IO::SeekAbsolute(f, smppos[smp - 1]); mpt::IO::Write(f, itss); if(dwPos > uint32_max) { continue; } // TODO this actually wraps around at 2 GB, so we either need to use the 64-bit seek API or warn earlier! - fseek(f, static_cast(dwPos), SEEK_SET); + mpt::IO::SeekAbsolute(f, dwPos); if(!isExternal) { - if(Samples[smp].nLength != smpLength) + if(sample.nLength > smpLength) { // Sample length does not fit into IT header! AddToLog(mpt::format("Truncating sample %1: Length exceeds exceeds 4 gigasamples.")(smp)); } - dwPos += itss.GetSampleFormat().WriteSample(f, Samples[smp], smpLength); + dwPos += itss.GetSampleFormat().WriteSample(f, sample, smpLength); } else { #ifdef MPT_EXTERNAL_SAMPLES @@ -1837,6 +1832,8 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor dwPos += intBytes + strSize; mpt::IO::WriteRaw(f, filenameU8.data(), strSize); } +#else + MPT_UNREFERENCED_PARAMETER(filename); #endif // MPT_EXTERNAL_SAMPLES } } @@ -1852,23 +1849,13 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor } // Updating offsets - fseek(f, dwHdrPos, SEEK_SET); - for(uint16 i = 0; i < itHeader.insnum; ++i) - { - mpt::IO::WriteIntLE(f, inspos[i]); - } - for(uint16 i = 0; i < itHeader.smpnum; ++i) - { - mpt::IO::WriteIntLE(f, smppos[i]); - } - for(uint16 i = 0; i < itHeader.patnum; ++i) - { - mpt::IO::WriteIntLE(f, patpos[i]); - } + mpt::IO::SeekAbsolute(f, dwHdrPos); + mpt::IO::Write(f, inspos); + mpt::IO::Write(f, smppos); + mpt::IO::Write(f, patpos); if(GetType() == MOD_TYPE_IT) { - fclose(f); return true; } @@ -1877,17 +1864,12 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor bool success = true; - fseek(f, 0, SEEK_END); - { - mpt::FILE_ostream fout(f); + mpt::IO::SeekEnd(f); - const uint32 MPTStartPos = (uint32)fout.tellp(); + const mpt::IO::Offset MPTStartPos = mpt::IO::TellWrite(f); - // catch standard library truncating files - MPT_ASSERT_ALWAYS(MPTStartPos > 0); - - srlztn::SsbWrite ssb(fout); - ssb.BeginWrite("mptm", MptVersion::num); + srlztn::SsbWrite ssb(f); + ssb.BeginWrite("mptm", Version::Current().GetRawVersion()); if(GetTuneSpecificTunings().GetNumTunings() > 0) ssb.WriteItem(GetTuneSpecificTunings(), "0", &WriteTuningCollection); @@ -1903,21 +1885,18 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor if(ssb.GetStatus() & srlztn::SNT_FAILURE) { - AddToLog(LogError, MPT_USTRING("Error occurred in writing MPTM extensions.")); + AddToLog(LogError, U_("Error occurred in writing MPTM extensions.")); } //Last 4 bytes should tell where the hack mpt things begin. - if(!fout.good()) + if(!f.good()) { - fout.clear(); + f.clear(); success = false; } - mpt::IO::WriteIntLE(fout, MPTStartPos); + mpt::IO::WriteIntLE(f, static_cast(MPTStartPos)); - fout.seekp(0, std::ios_base::end); - } - fclose(f); - f = nullptr; + mpt::IO::SeekEnd(f); //END : MPT SPECIFIC @@ -1932,100 +1911,95 @@ bool CSoundFile::SaveIT(const mpt::PathString &filename, bool compatibilityExpor #ifndef MODPLUG_NO_FILESAVE -uint32 CSoundFile::SaveMixPlugins(FILE *f, bool bUpdate) +uint32 CSoundFile::SaveMixPlugins(std::ostream *file, bool updatePlugData) { #ifndef NO_PLUGINS - uint32 chinfo[MAX_BASECHANNELS]; - char id[4]; - uint32 nPluginSize; - uint32 nTotalSize = 0; - uint32 nChInfo = 0; + uint32 totalSize = 0; for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++) { const SNDMIXPLUGIN &plugin = m_MixPlugins[i]; if(plugin.IsValidPlugin()) { - nPluginSize = sizeof(SNDMIXPLUGININFO) + 4; // plugininfo+4 (datalen) - if((plugin.pMixPlugin) && (bUpdate)) + uint32 chunkSize = sizeof(SNDMIXPLUGININFO) + 4; // plugininfo+4 (datalen) + if(plugin.pMixPlugin && updatePlugData) { plugin.pMixPlugin->SaveAllParameters(); } - nPluginSize += plugin.pluginData.size(); - uint32 MPTxPlugDataSize = 4 + sizeof(float32) + // 4 for ID and size of dryRatio - 4 + sizeof(int32); // Default Program - // for each extra entity, add 4 for ID, plus 4 for size of entity, plus size of entity + const uint32 extraDataSize = + 4 + sizeof(float32) + // 4 for ID and size of dryRatio + 4 + sizeof(int32); // Default Program + // For each extra entity, add 4 for ID, plus 4 for size of entity, plus size of entity - nPluginSize += MPTxPlugDataSize + 4; //+4 is for size itself: sizeof(uint32) is 4 - if(f) + chunkSize += extraDataSize + 4; // +4 is for size field itself + + const uint32 plugDataSize = std::min(mpt::saturate_cast(plugin.pluginData.size()), uint32_max - chunkSize); + chunkSize += plugDataSize; + + if(file) { - // write plugin ID - id[0] = 'F'; - id[1] = i < 100 ? 'X' : '0' + i / 100; - id[2] = '0' + (i / 10) % 10u; - id[3] = '0' + (i % 10u); + std::ostream &f = *file; + // Chunk ID (= plugin ID) + char id[4] = { 'F', 'X', '0', '0' }; + if(i >= 100) id[1] = '0' + (i / 100u); + id[2] += (i / 10u) % 10u; + id[3] += (i % 10u); mpt::IO::WriteRaw(f, id, 4); - // write plugin size: - mpt::IO::WriteIntLE(f, nPluginSize); + // Write chunk size, plugin info and plugin data chunk + mpt::IO::WriteIntLE(f, chunkSize); mpt::IO::Write(f, m_MixPlugins[i].Info); - uint32 dataSize = mpt::saturate_cast(m_MixPlugins[i].pluginData.size()); - mpt::IO::WriteIntLE(f, dataSize); - if(dataSize) + mpt::IO::WriteIntLE(f, plugDataSize); + if(plugDataSize) { - mpt::IO::WriteRaw(f, m_MixPlugins[i].pluginData.data(), dataSize); + mpt::IO::WriteRaw(f, m_MixPlugins[i].pluginData.data(), plugDataSize); } - mpt::IO::WriteIntLE(f, MPTxPlugDataSize); + mpt::IO::WriteIntLE(f, extraDataSize); // Dry/Wet ratio - memcpy(id, "DWRT", 4); - mpt::IO::WriteRaw(f, id, 4); + mpt::IO::WriteRaw(f, "DWRT", 4); // DWRT chunk does not include a size, so better make sure we always write 4 bytes here. STATIC_ASSERT(sizeof(IEEE754binary32LE) == 4); mpt::IO::Write(f, IEEE754binary32LE(m_MixPlugins[i].fDryRatio)); // Default program - memcpy(id, "PROG", 4); - mpt::IO::WriteRaw(f, id, 4); + mpt::IO::WriteRaw(f, "PROG", 4); // PROG chunk does not include a size, so better make sure we always write 4 bytes here. STATIC_ASSERT(sizeof(m_MixPlugins[i].defaultProgram) == sizeof(int32)); mpt::IO::WriteIntLE(f, m_MixPlugins[i].defaultProgram); // Please, if you add any more chunks here, don't repeat history (see above) and *do* add a size field for your chunk, mmmkay? } - nTotalSize += nPluginSize + 8; + totalSize += chunkSize + 8; } } + std::vector chinfo(GetNumChannels()); + uint32 numChInfo = 0; for(CHANNELINDEX j = 0; j < GetNumChannels(); j++) { - if(j < MAX_BASECHANNELS) + if((chinfo[j] = ChnSettings[j].nMixPlugin) != 0) { - if((chinfo[j] = ChnSettings[j].nMixPlugin) != 0) - { - nChInfo = j + 1; - } + numChInfo = j + 1; } } - if(nChInfo) + if(numChInfo) { - if(f) + if(file) { - memcpy(id, "CHFX", 4); - mpt::IO::WriteRaw(f, id, 4); - mpt::IO::WriteIntLE(f, nChInfo * 4); - for(uint32 i = 0; i < nChInfo; ++i) - { - mpt::IO::WriteIntLE(f, chinfo[i]); - } + std::ostream &f = *file; + mpt::IO::WriteRaw(f, "CHFX", 4); + mpt::IO::WriteIntLE(f, numChInfo * 4); + chinfo.resize(numChInfo); + mpt::IO::Write(f, chinfo); } - nTotalSize += nChInfo * 4 + 8; + totalSize += numChInfo * 4 + 8; } - return nTotalSize; + return totalSize; #else - MPT_UNREFERENCED_PARAMETER(f); - MPT_UNREFERENCED_PARAMETER(bUpdate); + MPT_UNREFERENCED_PARAMETER(file); + MPT_UNREFERENCED_PARAMETER(updatePlugData); return 0; #endif // NO_PLUGINS } @@ -2033,8 +2007,9 @@ uint32 CSoundFile::SaveMixPlugins(FILE *f, bool bUpdate) #endif // MODPLUG_NO_FILESAVE -void CSoundFile::LoadMixPlugins(FileReader &file) +bool CSoundFile::LoadMixPlugins(FileReader &file) { + bool isBeRoTracker = false; while(file.CanRead(9)) { char code[4]; @@ -2047,7 +2022,7 @@ void CSoundFile::LoadMixPlugins(FileReader &file) || !file.CanRead(chunkSize)) { file.SkipBack(8); - return; + return isBeRoTracker; } FileReader chunk = file.ReadChunk(chunkSize); @@ -2061,9 +2036,9 @@ void CSoundFile::LoadMixPlugins(FileReader &file) #ifndef NO_PLUGINS } // Plugin Data FX00, ... FX99, F100, ... F255 -#define ISNUMERIC(x) (code[(x)] >= '0' && code[(x)] <= '9') - else if(code[0] == 'F' && (code[1] == 'X' || ISNUMERIC(1)) && ISNUMERIC(2) && ISNUMERIC(3)) -#undef ISNUMERIC +#define MPT_ISDIGIT(x) (code[(x)] >= '0' && code[(x)] <= '9') + else if(code[0] == 'F' && (code[1] == 'X' || MPT_ISDIGIT(1)) && MPT_ISDIGIT(2) && MPT_ISDIGIT(3)) +#undef MPT_ISDIGIT { PLUGINDEX plug = (code[2] - '0') * 10 + (code[3] - '0'); //calculate plug-in number. if(code[1] != 'X') plug += (code[1] - '0') * 100; @@ -2075,10 +2050,11 @@ void CSoundFile::LoadMixPlugins(FileReader &file) #endif // NO_PLUGINS } else if(!memcmp(code, "MODU", 4)) { - m_madeWithTracker = MPT_USTRING("BeRoTracker"); - m_dwLastSavedWithVersion = 0; // Reset MPT detection for old files that have a similar fingerprint + isBeRoTracker = true; + m_dwLastSavedWithVersion = Version(); // Reset MPT detection for old files that have a similar fingerprint } } + return isBeRoTracker; } @@ -2141,11 +2117,11 @@ void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin) #ifndef MODPLUG_NO_FILESAVE -void CSoundFile::SaveExtendedSongProperties(FILE* f) const +void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const { const CModSpecifications &specs = GetModSpecifications(); // Extra song data - Yet Another Hack. - mpt::IO::WriteIntLE(f, MAGIC4BE('M','P','T','S')); + mpt::IO::WriteIntLE(f, MagicBE("MPTS")); #define WRITEMODULARHEADER(code, fsize) \ { \ @@ -2163,26 +2139,29 @@ void CSoundFile::SaveExtendedSongProperties(FILE* f) const if(m_nDefaultTempo.GetInt() > 255) { uint32 tempo = m_nDefaultTempo.GetInt(); - WRITEMODULAR(MAGIC4BE('D','T','.','.'), tempo); + WRITEMODULAR(MagicBE("DT.."), tempo); } if(m_nDefaultTempo.GetFract() != 0 && specs.hasFractionalTempo) { uint32 tempo = m_nDefaultTempo.GetFract(); - WRITEMODULAR(MAGIC4LE('D','T','F','R'), tempo); + WRITEMODULAR(MagicLE("DTFR"), tempo); } - WRITEMODULAR(MAGIC4BE('R','P','B','.'), m_nDefaultRowsPerBeat); - WRITEMODULAR(MAGIC4BE('R','P','M','.'), m_nDefaultRowsPerMeasure); + if(m_nDefaultRowsPerBeat > 255 || m_nDefaultRowsPerMeasure > 255 || GetType() == MOD_TYPE_XM) + { + WRITEMODULAR(MagicBE("RPB."), m_nDefaultRowsPerBeat); + WRITEMODULAR(MagicBE("RPM."), m_nDefaultRowsPerMeasure); + } if(GetType() != MOD_TYPE_XM) { - WRITEMODULAR(MAGIC4BE('C','.','.','.'), m_nChannels); + WRITEMODULAR(MagicBE("C..."), m_nChannels); } if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && GetNumChannels() > 64) { // IT header has only room for 64 channels. Save the settings that do not fit to the header here as an extension. - WRITEMODULARHEADER(MAGIC4BE('C','h','n','S'), (GetNumChannels() - 64) * 2); + WRITEMODULARHEADER(MagicBE("ChnS"), (GetNumChannels() - 64) * 2); for(CHANNELINDEX chn = 64; chn < GetNumChannels(); chn++) { uint8 panvol[2]; @@ -2195,36 +2174,36 @@ void CSoundFile::SaveExtendedSongProperties(FILE* f) const } { - WRITEMODULARHEADER(MAGIC4BE('T','M','.','.'), 1); + WRITEMODULARHEADER(MagicBE("TM.."), 1); uint8 mode = static_cast(m_nTempoMode); mpt::IO::WriteIntLE(f, mode); } const int32 tmpMixLevels = static_cast(m_nMixLevels); - WRITEMODULAR(MAGIC4BE('P','M','M','.'), tmpMixLevels); + WRITEMODULAR(MagicBE("PMM."), tmpMixLevels); if(m_dwCreatedWithVersion) { - WRITEMODULAR(MAGIC4BE('C','W','V','.'), m_dwCreatedWithVersion); + WRITEMODULAR(MagicBE("CWV."), m_dwCreatedWithVersion.GetRawVersion()); } - WRITEMODULAR(MAGIC4BE('L','S','W','V'), MptVersion::num); - WRITEMODULAR(MAGIC4BE('S','P','A','.'), m_nSamplePreAmp); - WRITEMODULAR(MAGIC4BE('V','S','T','V'), m_nVSTiVolume); + WRITEMODULAR(MagicBE("LSWV"), Version::Current().GetRawVersion()); + WRITEMODULAR(MagicBE("SPA."), m_nSamplePreAmp); + WRITEMODULAR(MagicBE("VSTV"), m_nVSTiVolume); if(GetType() == MOD_TYPE_XM && m_nDefaultGlobalVolume != MAX_GLOBAL_VOLUME) { - WRITEMODULAR(MAGIC4BE('D','G','V','.'), m_nDefaultGlobalVolume); + WRITEMODULAR(MagicBE("DGV."), m_nDefaultGlobalVolume); } if(GetType() != MOD_TYPE_XM && Order().GetRestartPos() != 0) { - WRITEMODULAR(MAGIC4BE('R','P','.','.'), Order().GetRestartPos()); + WRITEMODULAR(MagicBE("RP.."), Order().GetRestartPos()); } if(m_nResampling != SRCMODE_DEFAULT && specs.hasDefaultResampling) { - WRITEMODULAR(MAGIC4LE('R','S','M','P'), static_cast(m_nResampling)); + WRITEMODULAR(MagicLE("RSMP"), static_cast(m_nResampling)); } // Sample cues @@ -2238,11 +2217,11 @@ void CSoundFile::SaveExtendedSongProperties(FILE* f) const // Write one chunk for every sample. // Rationale: chunks are limited to 65536 bytes, which can easily be reached // with the amount of samples that OpenMPT supports. - WRITEMODULARHEADER(MAGIC4LE('C','U','E','S'), 2 + CountOf(sample.cues) * 4); + WRITEMODULARHEADER(MagicLE("CUES"), 2 + CountOf(sample.cues) * 4); mpt::IO::WriteIntLE(f, smp); - for(std::size_t i = 0; i < CountOf(sample.cues); i++) + for(auto cue : sample.cues) { - mpt::IO::WriteIntLE(f, sample.cues[i]); + mpt::IO::WriteIntLE(f, cue); } } } @@ -2255,7 +2234,7 @@ void CSoundFile::SaveExtendedSongProperties(FILE* f) const TempoSwing::Serialize(oStrm, m_tempoSwing); std::string data = oStrm.str(); uint16 length = mpt::saturate_cast(data.size()); - WRITEMODULARHEADER(MAGIC4LE('S','W','N','G'), length); + WRITEMODULARHEADER(MagicLE("SWNG"), length); mpt::IO::WriteRaw(f, data.data(), length); } @@ -2273,7 +2252,7 @@ void CSoundFile::SaveExtendedSongProperties(FILE* f) const } } uint16 numBytes = static_cast(maxBit / 8u); - WRITEMODULARHEADER(MAGIC4BE('M','S','F','.'), numBytes); + WRITEMODULARHEADER(MagicBE("MSF."), numBytes); mpt::IO::WriteRaw(f, bits, numBytes); } @@ -2281,7 +2260,7 @@ void CSoundFile::SaveExtendedSongProperties(FILE* f) const { std::string songArtistU8 = mpt::ToCharset(mpt::CharsetUTF8, m_songArtist); uint16 length = mpt::saturate_cast(songArtistU8.length()); - WRITEMODULARHEADER(MAGIC4LE('A','U','T','H'), length); + WRITEMODULARHEADER(MagicLE("AUTH"), length); mpt::IO::WriteRaw(f, songArtistU8.c_str(), length); } @@ -2295,8 +2274,8 @@ void CSoundFile::SaveExtendedSongProperties(FILE* f) const AddToLog("Too many MIDI Mapping directives to save; data won't be written."); } else { - WRITEMODULARHEADER(MAGIC4BE('M','I','M','A'), static_cast(objectsize)); - GetMIDIMapper().Serialize(f); + WRITEMODULARHEADER(MagicBE("MIMA"), static_cast(objectsize)); + GetMIDIMapper().Serialize(&f); } } #endif @@ -2344,7 +2323,7 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel const uint16 size = file.ReadUint16LE(); // Start of MPTM extensions, non-ASCII ID or truncated field - if(code == MAGIC4LE('2','2','8',4)) + if(code == MagicLE("228\x04")) { file.SkipBack(6); break; @@ -2357,35 +2336,35 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel switch (code) // interpret field code { - case MAGIC4BE('D','T','.','.'): { uint32 tempo; ReadField(chunk, size, tempo); m_nDefaultTempo.Set(tempo, m_nDefaultTempo.GetFract()); break; } - case MAGIC4LE('D','T','F','R'): { uint32 tempoFract; ReadField(chunk, size, tempoFract); m_nDefaultTempo.Set(m_nDefaultTempo.GetInt(), tempoFract); break; } - case MAGIC4BE('R','P','B','.'): ReadField(chunk, size, m_nDefaultRowsPerBeat); break; - case MAGIC4BE('R','P','M','.'): ReadField(chunk, size, m_nDefaultRowsPerMeasure); break; + case MagicBE("DT.."): { uint32 tempo; ReadField(chunk, size, tempo); m_nDefaultTempo.Set(tempo, m_nDefaultTempo.GetFract()); break; } + case MagicLE("DTFR"): { uint32 tempoFract; ReadField(chunk, size, tempoFract); m_nDefaultTempo.Set(m_nDefaultTempo.GetInt(), tempoFract); break; } + case MagicBE("RPB."): ReadField(chunk, size, m_nDefaultRowsPerBeat); break; + case MagicBE("RPM."): ReadField(chunk, size, m_nDefaultRowsPerMeasure); break; // FIXME: If there are only PC events on the last few channels in an MPTM MO3, they won't be imported! - case MAGIC4BE('C','.','.','.'): if(!ignoreChannelCount) { CHANNELINDEX chn = 0; ReadField(chunk, size, chn); m_nChannels = Clamp(chn, m_nChannels, MAX_BASECHANNELS); } break; - case MAGIC4BE('T','M','.','.'): ReadFieldCast(chunk, size, m_nTempoMode); break; - case MAGIC4BE('P','M','M','.'): ReadFieldCast(chunk, size, m_nMixLevels); break; - case MAGIC4BE('C','W','V','.'): ReadField(chunk, size, m_dwCreatedWithVersion); break; - case MAGIC4BE('L','S','W','V'): { uint32 ver; ReadField(chunk, size, ver); if(ver != 0) { m_dwLastSavedWithVersion = ver; } break; } - case MAGIC4BE('S','P','A','.'): ReadField(chunk, size, m_nSamplePreAmp); break; - case MAGIC4BE('V','S','T','V'): ReadField(chunk, size, m_nVSTiVolume); break; - case MAGIC4BE('D','G','V','.'): ReadField(chunk, size, m_nDefaultGlobalVolume); break; - case MAGIC4BE('R','P','.','.'): if(GetType() != MOD_TYPE_XM) { ORDERINDEX restartPos; ReadField(chunk, size, restartPos); Order().SetRestartPos(restartPos); } break; - case MAGIC4LE('R','S','M','P'): + case MagicBE("C..."): if(!ignoreChannelCount) { CHANNELINDEX chn = 0; ReadField(chunk, size, chn); m_nChannels = Clamp(chn, m_nChannels, MAX_BASECHANNELS); } break; + case MagicBE("TM.."): ReadFieldCast(chunk, size, m_nTempoMode); break; + case MagicBE("PMM."): ReadFieldCast(chunk, size, m_nMixLevels); break; + case MagicBE("CWV."): { uint32 ver = 0; ReadField(chunk, size, ver); m_dwCreatedWithVersion = Version(ver); break; } + case MagicBE("LSWV"): { uint32 ver = 0; ReadField(chunk, size, ver); if(ver != 0) { m_dwLastSavedWithVersion = Version(ver); } break; } + case MagicBE("SPA."): ReadField(chunk, size, m_nSamplePreAmp); break; + case MagicBE("VSTV"): ReadField(chunk, size, m_nVSTiVolume); break; + case MagicBE("DGV."): ReadField(chunk, size, m_nDefaultGlobalVolume); break; + case MagicBE("RP.."): if(GetType() != MOD_TYPE_XM) { ORDERINDEX restartPos; ReadField(chunk, size, restartPos); Order().SetRestartPos(restartPos); } break; + case MagicLE("RSMP"): ReadFieldCast(chunk, size, m_nResampling); - if(!IsKnownResamplingMode(m_nResampling)) m_nResampling = SRCMODE_DEFAULT; + if(!Resampling::IsKnownMode(m_nResampling)) m_nResampling = SRCMODE_DEFAULT; break; #ifdef MODPLUG_TRACKER - case MAGIC4BE('M','I','M','A'): GetMIDIMapper().Deserialize(chunk); break; + case MagicBE("MIMA"): GetMIDIMapper().Deserialize(chunk); break; #endif - case MAGIC4LE('A','U','T','H'): + case MagicLE("AUTH"): { std::string artist; chunk.ReadString(artist, chunk.GetLength()); m_songArtist = mpt::ToUnicode(mpt::CharsetUTF8, artist); } break; - case MAGIC4BE('C','h','n','S'): + case MagicBE("ChnS"): // Channel settings for channels 65+ if(size <= (MAX_BASECHANNELS - 64) * 2 && (size % 2u) == 0) { @@ -2409,7 +2388,7 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel } break; - case MAGIC4LE('C','U','E','S'): + case MagicLE("CUES"): // Sample cues if(size > 2) { @@ -2417,15 +2396,15 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel if(smp > 0 && smp <= GetNumSamples()) { ModSample &sample = Samples[smp]; - for(std::size_t i = 0; i < CountOf(sample.cues); i++) + for(auto &cue : sample.cues) { - sample.cues[i] = chunk.ReadUint32LE(); + cue = chunk.ReadUint32LE(); } } } break; - case MAGIC4LE('S','W','N','G'): + case MagicLE("SWNG"): // Tempo Swing Factors if(size > 2) { @@ -2434,7 +2413,7 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel } break; - case MAGIC4BE('M','S','F','.'): + case MagicBE("MSF."): // Playback compatibility flags { size_t bit = 0; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp index 5c4dafac8..053a26ae0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp @@ -71,7 +71,7 @@ MPT_BINARY_STRUCT(ITPHeader, 8) static bool ValidateHeader(const ITPHeader &hdr) { - if(hdr.magic != MAGIC4BE('.','i','t','p')) + if(hdr.magic != MagicBE(".itp")) { return false; } @@ -105,7 +105,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderITP(MemoryFileReader file, co } -bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) +bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) { #if !defined(MPT_EXTERNAL_SAMPLES) && !defined(MPT_FUZZ_TRACKER) // Doesn't really make sense to support this format when there's no support for external files... @@ -339,10 +339,10 @@ bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) FileReader instrFile = GetFileReader(f); if(!ReadInstrumentFromFile(ins + 1, instrFile, true)) { - AddToLog(LogWarning, MPT_USTRING("Unable to open instrument: ") + instrPaths[ins].ToUnicode()); + AddToLog(LogWarning, U_("Unable to open instrument: ") + instrPaths[ins].ToUnicode()); } #else - AddToLog(LogWarning, mpt::format(MPT_USTRING("Loading external instrument %1 ('%2') failed: External instruments are not supported."))(ins, instrPaths[ins].ToUnicode())); + AddToLog(LogWarning, mpt::format(U_("Loading external instrument %1 ('%2') failed: External instruments are not supported."))(ins, instrPaths[ins].ToUnicode())); #endif // MPT_EXTERNAL_SAMPLES } @@ -350,17 +350,17 @@ bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) uint32 code = file.ReadUint32LE(); // Embed instruments' header [v1.01] - if(version >= 0x00000101 && (songFlags & ITP_ITPEMBEDIH) && code == MAGIC4BE('E', 'B', 'I', 'H')) + if(version >= 0x00000101 && (songFlags & ITP_ITPEMBEDIH) && code == MagicBE("EBIH")) { code = file.ReadUint32LE(); INSTRUMENTINDEX ins = 1; while(ins <= GetNumInstruments() && file.CanRead(4)) { - if(code == MAGIC4BE('M', 'P', 'T', 'S')) + if(code == MagicBE("MPTS")) { break; - } else if(code == MAGIC4BE('S', 'E', 'P', '@') || code == MAGIC4BE('M', 'P', 'T', 'X')) + } else if(code == MagicBE("SEP@") || code == MagicBE("MPTX")) { // jump code - switch to next instrument ins++; @@ -374,7 +374,7 @@ bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) } // Song extensions - if(code == MAGIC4BE('M', 'P', 'T', 'S')) + if(code == MagicBE("MPTS")) { file.SkipBack(4); LoadExtendedSongProperties(file, true); @@ -389,7 +389,10 @@ bool CSoundFile::ReadITProject(FileReader &file, ModLoadingFlags loadFlags) m_MidiCfg.Reset(); } - m_madeWithTracker = MPT_USTRING("OpenMPT ") + MptVersion::ToUString(m_dwLastSavedWithVersion); + m_modFormat.formatName = U_("Impulse Tracker Project"); + m_modFormat.type = U_("itp"); + m_modFormat.madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); + m_modFormat.charset = mpt::CharsetWindows1252; return true; #endif // MPT_EXTERNAL_SAMPLES diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp index 55caa27fa..6401aa334 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp @@ -12,8 +12,6 @@ #include "Loaders.h" #include "ChunkReader.h" -#include - OPENMPT_NAMESPACE_BEGIN // MDL file header @@ -32,17 +30,17 @@ struct MDLChunk // 16-Bit chunk identifiers enum ChunkIdentifiers { - idInfo = MAGIC2LE('I','N'), - idMessage = MAGIC2LE('M','E'), - idPats = MAGIC2LE('P','A'), - idPatNames = MAGIC2LE('P','N'), - idTracks = MAGIC2LE('T','R'), - idInstrs = MAGIC2LE('I','I'), - idVolEnvs = MAGIC2LE('V','E'), - idPanEnvs = MAGIC2LE('P','E'), - idFreqEnvs = MAGIC2LE('F','E'), - idSampleInfo = MAGIC2LE('I','S'), - ifSampleData = MAGIC2LE('S','A'), + idInfo = MagicLE("IN"), + idMessage = MagicLE("ME"), + idPats = MagicLE("PA"), + idPatNames = MagicLE("PN"), + idTracks = MagicLE("TR"), + idInstrs = MagicLE("II"), + idVolEnvs = MagicLE("VE"), + idPanEnvs = MagicLE("PE"), + idFreqEnvs = MagicLE("FE"), + idSampleInfo = MagicLE("IS"), + ifSampleData = MagicLE("SA"), }; uint16le id; @@ -98,17 +96,6 @@ struct MDLSampleHeader MPT_BINARY_STRUCT(MDLSampleHeader, 14) -// Part of the sample header that's common between v0 and v1. -struct MDLSampleInfoCommon -{ - uint8le sampleIndex; - char name[32]; - char filename[8]; -}; - -MPT_BINARY_STRUCT(MDLSampleInfoCommon, 41) - - struct MDLEnvelope { uint8 envNum; @@ -167,7 +154,7 @@ enum }; -static const uint8 MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE }; +static const VibratoType MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE }; static const ModCommand::COMMAND MDLEffTrans[] = { @@ -479,11 +466,14 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITSCxStopsSample); // Gate effect in underbeat.mdl - m_madeWithTracker = MPT_USTRING("Digitrakker ") + ( - (fileHeader.version == 0x11) ? MPT_USTRING("3") // really could be 2.99b - close enough - : (fileHeader.version == 0x10) ? MPT_USTRING("2.3") - : (fileHeader.version == 0x00) ? MPT_USTRING("2.0 - 2.2b") // there was no 1.x release - : MPT_USTRING("")); + m_modFormat.formatName = U_("Digitrakker"); + m_modFormat.type = U_("mdl"); + m_modFormat.madeWithTracker = U_("Digitrakker ") + ( + (fileHeader.version == 0x11) ? U_("3") // really could be 2.99b - close enough + : (fileHeader.version == 0x10) ? U_("2.3") + : (fileHeader.version == 0x00) ? U_("2.0 - 2.2b") // there was no 1.x release + : U_("")); + m_modFormat.charset = mpt::CharsetCP437; mpt::String::Read(m_songName, info.title); { @@ -526,24 +516,18 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) uint8 numSamples = chunk.ReadUint8(); for(uint8 smp = 0; smp < numSamples; smp++) { - MDLSampleInfoCommon header; - if(!chunk.ReadStruct(header) || header.sampleIndex == 0) - continue; - #if 1 - STATIC_ASSERT(MPT_MAX_UNSIGNED_VALUE(header.sampleIndex) < MAX_SAMPLES); - #else - MPT_MAYBE_CONSTANT_IF(header.sampleIndex >= MAX_SAMPLES) - continue; - #endif + const SAMPLEINDEX sampleIndex = chunk.ReadUint8(); + if(sampleIndex == 0 || sampleIndex >= MAX_SAMPLES || !chunk.CanRead(32 + 8 + 2 + 12 + 2)) + break; - if(header.sampleIndex > GetNumSamples()) - m_nSamples = header.sampleIndex; + if(sampleIndex > GetNumSamples()) + m_nSamples = sampleIndex; - ModSample &sample = Samples[header.sampleIndex]; + ModSample &sample = Samples[sampleIndex]; sample.Initialize(); - mpt::String::Read(m_szNames[header.sampleIndex], header.name); - mpt::String::Read(sample.filename, header.filename); + chunk.ReadString(m_szNames[sampleIndex], 32); + chunk.ReadString(sample.filename, 8); uint32 c4speed; if(fileHeader.version < 0x10) @@ -559,10 +543,9 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) sample.uFlags.set(CHN_LOOP); sample.nLoopEnd += sample.nLoopStart; } + uint8 volume = chunk.ReadUint8(); if(fileHeader.version < 0x10) - sample.nVolume = chunk.ReadUint8(); - else - chunk.Skip(1); + sample.nVolume = volume; uint8 flags = chunk.ReadUint8(); if(flags & 0x01) @@ -619,7 +602,14 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) if(sampleHeader.smpNum == 0) continue; #if 1 - STATIC_ASSERT(MPT_MAX_UNSIGNED_VALUE(sampleHeader.smpNum) < MAX_SAMPLES); + #if MPT_GCC_BEFORE(6,1,0) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wtype-limits" + #endif + STATIC_ASSERT((mpt::limits::max)() < MAX_SAMPLES); + #if MPT_GCC_BEFORE(6,1,0) + #pragma GCC diagnostic pop + #endif #else MPT_MAYBE_CONSTANT_IF(sampleHeader.smpNum >= MAX_SAMPLES) continue; @@ -824,30 +814,4 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) } -///////////////////////////////////////////////////////////////////////// -// MDL Sample Unpacking - -// MDL Huffman ReadBits compression -uint8 MDLReadBits(uint32 &bitbuf, int32 &bitnum, const uint8 *(&ibuf), size_t &bytesLeft, int8 n) -{ - if(bitnum < n) - { - if(bytesLeft) - { - bitbuf |= (((uint32)(*ibuf++)) << bitnum); - bitnum += 8; - bytesLeft--; - } else - { - throw std::range_error("Truncated MDL sample block"); - } - } - - uint8 v = static_cast(bitbuf & ((1 << n) - 1)); - bitbuf >>= n; - bitnum -= n; - return v; -} - - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp index 466a84980..64d1ec257 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp @@ -1,7 +1,7 @@ /* * Load_med.cpp * ------------ - * Purpose: OctaMed MED module loader + * Purpose: OctaMED / MED Soundstudio module loader * Notes : (currently none) * Authors: Olivier Lapicque * OpenMPT Devs @@ -11,7 +11,7 @@ #include "stdafx.h" #include "Loaders.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" OPENMPT_NAMESPACE_BEGIN @@ -180,10 +180,10 @@ struct MMD2SONGHEADER MPT_BINARY_STRUCT(MMD2SONGHEADER, 788) -// For MMD0 the note information is held in 3 bytes, byte0, byte1, byte2. For reference we +// For MMD0 the note information is held in 3 bytes, byte0, byte1, byte2. For reference we // number the bits in each byte 0..7, where 0 is the low bit. // The note is held as bits 5..0 of byte0 -// The instrument is encoded in 6 bits, bits 7 and 6 of byte0 and bits 7,6,5,4 of byte1 +// The instrument is encoded in 6 bits, bits 7 and 6 of byte0 and bits 7,6,5,4 of byte1 // The command number is bits 3,2,1,0 of byte1, command data is in byte2: // For command 0, byte2 represents the second data byte, otherwise byte2 // represents the first data byte. @@ -241,7 +241,7 @@ MPT_BINARY_STRUCT(MMD2PLAYSEQ, 1066) // A command table contains commands that effect a particular play sequence -// entry. The only commands read in are STOP or POSJUMP, all others are ignored +// entry. The only commands read in are STOP or POSJUMP, all others are ignored // POSJUMP is presumed to have extra bytes containing a uint16 for the position struct MMDCOMMAND { @@ -527,7 +527,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, co } -bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) +bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); MEDMODULEHEADER pmmh; @@ -569,7 +569,19 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) InitializeChannels(); // Setup channel pan positions and volume SetupMODPanning(true); - m_madeWithTracker = mpt::format(MPT_USTRING("OctaMED (MMD%1)"))(mpt::ToUnicode(mpt::CharsetISO8859_1, std::string(1, version))); + + const MPT_UCHAR_TYPE *madeWithTracker = UL_(""); + switch(version) + { + case '0': madeWithTracker = m_nChannels > 4 ? UL_("OctaMED v2.10") : UL_("MED v2"); break; + case '1': madeWithTracker = UL_("OctaMED v4"); break; + case '2': madeWithTracker = UL_("OctaMED v5"); break; + case '3': madeWithTracker = UL_("OctaMED Soundstudio"); break; + } + m_modFormat.formatName = mpt::format(U_("OctaMED (MMD%1)"))(version - '0'); + m_modFormat.type = U_("med"); + m_modFormat.madeWithTracker = madeWithTracker; + m_modFormat.charset = mpt::CharsetISO8859_1; m_nSamplePreAmp = 32; dwBlockArr = pmmh.blockarr; @@ -618,6 +630,7 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) for (uint32 iSHdr=0; iSHdrsample[iSHdr].rep * 2u; sample.nLoopEnd = sample.nLoopStart + (pmsh->sample[iSHdr].replen * 2u); sample.nVolume = (pmsh->sample[iSHdr].svol << 2); @@ -709,7 +722,7 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) annolen = std::min(annolen, MED_MAX_COMMENT_LENGTH); //Thanks to Luigi Auriemma for pointing out an overflow risk if ((annotxt) && (annolen) && (annolen <= dwMemLength) && (annotxt <= dwMemLength - annolen) ) { - m_songMessage.Read(lpStream + annotxt, annolen - 1, SongMessage::leAutodetect); + m_songMessage.Read(mpt::byte_cast(lpStream) + annotxt, annolen - 1, SongMessage::leAutodetect); } // Song Name uint32 songname = pmex->songname; @@ -771,8 +784,8 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) pdwTable = const_unaligned_ptr_be(lpStream + dwSmplArr); for (uint32 iSmp=0; iSmp= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength)) continue; + const uint32 dwPos = pdwTable[iSmp]; + if ((dwPos >= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength) || !(loadFlags & loadSampleData)) continue; const MMDSAMPLEHEADER *psdh = (const MMDSAMPLEHEADER *)(lpStream + dwPos); uint32 len = psdh->length; #ifdef MED_LOG @@ -780,7 +793,7 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) #endif if(dwPos + len + 6 > dwMemLength) len = 0; uint32 stype = psdh->type; - const char *psdata = (const char *)(lpStream + dwPos + 6); + FileReader chunk(mpt::byte_cast(mpt::as_span(lpStream + dwPos + 6, dwMemLength - dwPos - 6))); SampleIO sampleIO( SampleIO::_8bit, @@ -790,7 +803,7 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) if (stype & 0x80) { - psdata += (stype & 0x20) ? 14 : 6; + chunk.Skip((stype & 0x20) ? 14 : 6); } else { if(stype & 0x10) @@ -805,11 +818,7 @@ bool CSoundFile::ReadMed(FileReader &file, ModLoadingFlags loadFlags) } } Samples[iSmp + 1].nLength = len; - if(loadFlags & loadSampleData) - { - FileReader chunk(mpt::byte_cast(mpt::as_span(psdata, dwMemLength - dwPos - 6))); - sampleIO.ReadSample(Samples[iSmp + 1], chunk); - } + sampleIO.ReadSample(Samples[iSmp + 1], chunk); } // Reading patterns (blocks) if(!(loadFlags & loadPatternData)) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp index d94fb8cfc..013e741a7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp @@ -355,76 +355,45 @@ typedef uint32 tick_t; struct TrackState { FileReader track; - tick_t nextEvent; - uint8 command; - bool finished; - - TrackState() - : nextEvent(0) - , command(0) - , finished(false) - { } + tick_t nextEvent = 0; + uint8 command = 0; + bool finished = false; }; struct ModChannelState { - static const uint8 NOMIDI = 0xFF; // No MIDI channel assigned. + enum : uint8 { NOMIDI = 0xFF }; // No MIDI channel assigned. - tick_t age; // At which MIDI tick the channel was triggered - int32 porta; // Current portamento position in extra-fine slide units (1/64th of a semitone) - uint8 vol; // MIDI note volume (0...127) - uint8 pan; // MIDI channel panning (0...256) - uint8 midiCh; // MIDI channel that was last played on this channel - ModCommand::NOTE note; // MIDI note that was last played on this channel - bool sustained : 1; // If true, the note was already released by a note-off event, but sustain pedal CC is still active - - ModChannelState() - : age(0) - , porta(0) - , vol(100) - , pan(128) - , midiCh(NOMIDI) - , note(NOTE_NONE) - , sustained(false) - { } + tick_t age = 0; // At which MIDI tick the channel was triggered + int32 porta = 0; // Current portamento position in extra-fine slide units (1/64th of a semitone) + uint8 vol = 100; // MIDI note volume (0...127) + uint8 pan = 128; // MIDI channel panning (0...256) + uint8 midiCh = NOMIDI; // MIDI channel that was last played on this channel + ModCommand::NOTE note = NOTE_NONE; // MIDI note that was last played on this channel + bool sustained = false; // If true, the note was already released by a note-off event, but sustain pedal CC is still active }; struct MidiChannelState { - int32 pitchbendMod; // Pre-computed pitchbend in extra-fine slide units (1/64th of a semitone) - int16 pitchbend; // 0...16383 - uint16 bank; // 0...16383 - uint8 program; // 0...127 - // -- Controllers ------------- function ---------- CC# --- range ---- init (midi) --- - uint8 pan; // Channel Panning 10 [0-255] 128 (64) - uint8 expression; // Channel Expression 11 0-128 128 (127) - uint8 volume; // Channel Volume 7 0-128 80 (100) - uint16 rpn; // Currently selected RPN 100/101 n/a - uint8 pitchBendRange; // Pitch Bend Range 2 - int8 transpose; // Channel transpose 0 - bool monoMode : 1; // Mono/Poly operation 126/127 n/a Poly - bool sustain : 1; // Sustain pedal 64 on/off off + int32 pitchbendMod = 0; // Pre-computed pitchbend in extra-fine slide units (1/64th of a semitone) + int16 pitchbend = MIDIEvents::pitchBendCentre; // 0...16383 + uint16 bank = 0; // 0...16383 + uint8 program = 0; // 0...127 + // -- Controllers ---------------- function ---------- CC# --- range ---- init (midi) --- + uint8 pan = 128; // Channel Panning 10 [0-255] 128 (64) + uint8 expression = 128; // Channel Expression 11 0-128 128 (127) + uint8 volume = 80; // Channel Volume 7 0-128 80 (100) + uint16 rpn = 0x3FFF; // Currently selected RPN 100/101 n/a + uint8 pitchBendRange = 2; // Pitch Bend Range 2 + int8 transpose = 0; // Channel transpose 0 + bool monoMode = false; // Mono/Poly operation 126/127 n/a Poly + bool sustain = false; // Sustain pedal 64 on/off off - CHANNELINDEX noteOn[128]; // Value != CHANNELINDEX_INVALID: Note is active and mapped to mod channel in value + std::array noteOn; // Value != CHANNELINDEX_INVALID: Note is active and mapped to mod channel in value MidiChannelState() - : pitchbendMod(0) - , pitchbend(MIDIEvents::pitchBendCentre) - , bank(0) - , program(0) - , pan(128) - , expression(128) - , volume(80) - , rpn(0x3FFF) - , pitchBendRange(2) - , transpose(0) - , monoMode(false) - , sustain(false) { - for(auto ¬e : noteOn) - { - note = CHANNELINDEX_INVALID; - } + noteOn.fill(CHANNELINDEX_INVALID); } void SetPitchbend(uint16 value) @@ -450,11 +419,11 @@ struct MidiChannelState { switch(rpn) { - case 0: // Pitch Bend Range + case 0: // Pitch Bend Range pitchBendRange = std::max(value, uint8(1)); SetPitchbend(pitchbend); break; - case 2: // Coarse Tune + case 2: // Coarse Tune transpose = static_cast(value) - 64; break; } @@ -464,9 +433,9 @@ struct MidiChannelState { switch(rpn) { - case 0: // Pitch Bend Range + case 0: // Pitch Bend Range return pitchBendRange; - case 2: // Coarse Tune + case 2: // Coarse Tune return transpose; } return 0; @@ -585,11 +554,27 @@ static void EnterMIDIVolume(ModCommand &m, ModChannelState &modChn, const MidiCh } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMID(MemoryFileReader file, const uint64 *pfilesize) +{ + MPT_UNREFERENCED_PARAMETER(pfilesize); + char magic[4]; + file.ReadArray(magic); + if(!memcmp(magic, "MThd", 4)) + return ProbeSuccess; + + if(!memcmp(magic, "RIFF", 4) && file.Skip(4) && file.ReadMagic("RMID")) + return ProbeSuccess; + + return ProbeFailure; +} + + bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); // Microsoft MIDI files + bool isRIFF = false; if(file.ReadMagic("RIFF")) { file.Skip(4); @@ -610,6 +595,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) file.Skip(length); } else { + isRIFF = true; break; } } while(file.CanRead(8)); @@ -647,8 +633,12 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) const ORDERINDEX MPT_MIDI_IMPORT_MAX_ORDERS = MAX_ORDERS; #endif - m_songArtist = MPT_USTRING("MIDI Conversion"); - m_madeWithTracker = MPT_USTRING("Standard MIDI File"); + m_songArtist = U_("MIDI Conversion"); + m_modFormat.formatName = U_("Standard MIDI File"); + m_modFormat.type = isRIFF ? UL_("rmi") : UL_("mid"); + m_modFormat.madeWithTracker = U_("Standard MIDI File"); + m_modFormat.charset = mpt::CharsetISO8859_1; + SetMixLevels(mixLevels1_17RC3); m_nTempoMode = tempoModeModern; m_SongFlags = SONG_LINEARSLIDES; @@ -764,11 +754,11 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) switch(data1) { - case 1: // Text - case 2: // Copyright + case 1: // Text + case 2: // Copyright m_songMessage.Read(chunk, len, SongMessage::leAutodetect); break; - case 3: // Track Name + case 3: // Track Name if(len > 0) { std::string s; @@ -780,11 +770,11 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) m_songName = s; } break; - case 4: // Instrument - case 5: // Lyric + case 4: // Instrument + case 5: // Lyric break; - case 6: // Marker - case 7: // Cue point + case 6: // Marker + case 7: // Cue point { std::string s; chunk.ReadString(s, len); @@ -796,10 +786,10 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) } } break; - case 8: // Patch name - case 9: // Port name + case 8: // Patch name + case 9: // Port name break; - case 0x2F: // End Of Track + case 0x2F: // End Of Track tracks[t].finished = true; break; case 0x51: // Tempo @@ -816,7 +806,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) } else if(newTempo != tempo) { patRow[tempoChannel].command = CMD_TEMPO; - patRow[tempoChannel].param = mpt::saturate_cast(std::max(32.0, Util::Round(newTempo.ToDouble()))); + patRow[tempoChannel].param = mpt::saturate_round(std::max(32.0, newTempo.ToDouble())); } tempo = newTempo; } @@ -827,16 +817,20 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) } } else { + uint8 command = tracks[t].command; if(data1 & 0x80) { - // Command byte (if not present, repeat previous command byte) - tracks[t].command = data1; + // Command byte (if not present, use running status for channel messages) + command = data1; if(data1 < 0xF0) + { + tracks[t].command = data1; data1 = track.ReadUint8(); + } } - uint8 midiCh = tracks[t].command & 0x0F; + uint8 midiCh = command & 0x0F; - switch(tracks[t].command & 0xF0) + switch(command & 0xF0) { case 0x80: // Note Off case 0x90: // Note On @@ -844,7 +838,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) data1 &= 0x7F; ModCommand::NOTE note = static_cast(Clamp(data1 + NOTE_MIN, NOTE_MIN, NOTE_MAX)); uint8 data2 = track.ReadUint8(); - if(data2 > 0 && (tracks[t].command & 0xF0) == 0x90) + if(data2 > 0 && (command & 0xF0) == 0x90) { // Note On CHANNELINDEX chn = FindUnusedChannel(midiCh, note, modChnStatus, midiChnStatus[midiCh].monoMode, patRow); @@ -1227,11 +1221,11 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { channels.push_back(i); if(modChnStatus[i].midiCh != ModChannelState::NOMIDI) - sprintf(ChnSettings[i].szName, "MIDI Ch %u", 1 + modChnStatus[i].midiCh); + mpt::String::WriteAutoBuf(ChnSettings[i].szName) = mpt::format("MIDI Ch %1")(1 + modChnStatus[i].midiCh); else if(i == tempoChannel) - strcpy(ChnSettings[i].szName, "Tempo"); + mpt::String::WriteAutoBuf(ChnSettings[i].szName) = "Tempo"; else if(i == globalVolChannel) - strcpy(ChnSettings[i].szName, "Global Volume"); + mpt::String::WriteAutoBuf(ChnSettings[i].szName) = "Global Volume"; } } if(channels.empty()) @@ -1251,130 +1245,87 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) GetpModDoc()->m_ShowSavedialog = true; } - std::shared_ptr pCachedBank, pEmbeddedBank; - mpt::PathString cachedBankFile; + std::unique_ptr cachedBank, embeddedBank; if(CDLSBank::IsDLSBank(file.GetFileName())) { // Soundfont embedded in MIDI file - pEmbeddedBank = std::make_shared(); - pEmbeddedBank->Open(file.GetFileName()); + embeddedBank = std::make_unique(); + embeddedBank->Open(file.GetFileName()); } else { // Soundfont with same name as MIDI file - for(const auto &ext : { MPT_PATHSTRING(".sf2"), MPT_PATHSTRING(".sbk"), MPT_PATHSTRING(".dls") }) + for(const auto &ext : { P_(".sf2"), P_(".sbk"), P_(".dls") }) { mpt::PathString filename = file.GetFileName().ReplaceExt(ext); if(filename.IsFile()) { - pEmbeddedBank = std::make_shared(); - if(pEmbeddedBank->Open(filename)) + embeddedBank = std::make_unique(); + if(embeddedBank->Open(filename)) break; } } } ChangeModTypeTo(MOD_TYPE_MPT); - MIDILIBSTRUCT &midiLib = CTrackApp::GetMidiLibrary(); - // Scan Instruments - for (INSTRUMENTINDEX nIns = 1; nIns <= m_nInstruments; nIns++) if (Instruments[nIns]) + const MidiLibrary &midiLib = CTrackApp::GetMidiLibrary(); + mpt::PathString cachedBankName; + // Load Instruments + for (INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) if (Instruments[ins]) { - mpt::PathString pszMidiMapName; - ModInstrument *pIns = Instruments[nIns]; - uint32 nMidiCode; - bool embedded = false; + ModInstrument *pIns = Instruments[ins]; + uint32 midiCode = 0; + if(pIns->nMidiChannel == MIDI_DRUMCHANNEL) + midiCode = 0x80 | (pIns->nMidiDrumKey & 0x7F); + else if(pIns->nMidiProgram) + midiCode = (pIns->nMidiProgram - 1) & 0x7F; - if (pIns->nMidiChannel == MIDI_DRUMCHANNEL) - nMidiCode = 0x80 | (pIns->nMidiDrumKey & 0x7F); - else - nMidiCode = pIns->nMidiProgram & 0x7F; - pszMidiMapName = midiLib.MidiMap[nMidiCode]; - if (pEmbeddedBank) + if(embeddedBank && embeddedBank->FindAndExtract(*this, ins, midiCode >= 0x80)) { - uint32 nDlsIns = 0, nDrumRgn = 0; - uint32 nProgram = (pIns->nMidiProgram != 0) ? pIns->nMidiProgram - 1 : 0; - uint32 dwKey = (nMidiCode < 128) ? 0xFF : (nMidiCode & 0x7F); - if ((pEmbeddedBank->FindInstrument( (nMidiCode >= 128), - ((pIns->wMidiBank - 1) & 0x3FFF), - nProgram, dwKey, &nDlsIns)) - || (pEmbeddedBank->FindInstrument( (nMidiCode >= 128), 0xFFFF, - (nMidiCode >= 128) ? 0xFF : nProgram, - dwKey, &nDlsIns))) - { - if (dwKey < 0x80) nDrumRgn = pEmbeddedBank->GetRegionFromKey(nDlsIns, dwKey); - if (pEmbeddedBank->ExtractInstrument(*this, nIns, nDlsIns, nDrumRgn)) - { - pIns = Instruments[nIns]; // Reset pIns because ExtractInstrument may delete the previous value. - if ((dwKey >= 24) && (dwKey < 24 + MPT_ARRAY_COUNT(szMidiPercussionNames))) - { - mpt::String::CopyN(pIns->name, szMidiPercussionNames[dwKey - 24]); - } - embedded = true; - } - else - pIns = Instruments[nIns]; // Reset pIns because ExtractInstrument may delete the previous value. - } + continue; } - if((!pszMidiMapName.empty()) && (!embedded)) - { - // Load From DLS Bank - if (CDLSBank::IsDLSBank(pszMidiMapName)) - { - std::shared_ptr pDLSBank = nullptr; - if ((pCachedBank) && (!mpt::PathString::CompareNoCase(cachedBankFile, pszMidiMapName))) + const mpt::PathString &midiMapName = midiLib[midiCode]; + if(!midiMapName.empty()) + { + // Load from DLS/SF2 Bank + if(CDLSBank::IsDLSBank(midiMapName)) + { + CDLSBank *dlsBank = nullptr; + if(cachedBank != nullptr && !mpt::PathString::CompareNoCase(cachedBankName, midiMapName)) { - pDLSBank = pCachedBank; + dlsBank = cachedBank.get(); } else { - pCachedBank = std::make_shared(); - cachedBankFile = pszMidiMapName; - if (pCachedBank->Open(pszMidiMapName)) pDLSBank = pCachedBank; + cachedBank = std::make_unique(); + cachedBankName = midiMapName; + if(cachedBank->Open(midiMapName)) dlsBank = cachedBank.get(); } - if (pDLSBank) + if(dlsBank) { - uint32 nDlsIns = 0, nDrumRgn = 0; - uint32 nProgram = (pIns->nMidiProgram != 0) ? pIns->nMidiProgram - 1 : 0; - uint32 dwKey = (nMidiCode < 128) ? 0xFF : (nMidiCode & 0x7F); - if ((pDLSBank->FindInstrument( (nMidiCode >= 128), - ((pIns->wMidiBank - 1) & 0x3FFF), - nProgram, dwKey, &nDlsIns)) - || (pDLSBank->FindInstrument( (nMidiCode >= 128), 0xFFFF, - (nMidiCode >= 128) ? 0xFF : nProgram, - dwKey, &nDlsIns))) - { - if (dwKey < 0x80) nDrumRgn = pDLSBank->GetRegionFromKey(nDlsIns, dwKey); - pDLSBank->ExtractInstrument(*this, nIns, nDlsIns, nDrumRgn); - pIns = Instruments[nIns]; // Reset pIns because ExtractInstrument may delete the previous value. - if ((dwKey >= 24) && (dwKey < 24 + MPT_ARRAY_COUNT(szMidiPercussionNames))) - { - mpt::String::CopyN(pIns->name, szMidiPercussionNames[dwKey - 24]); - } - } + dlsBank->FindAndExtract(*this, ins, midiCode >= 0x80); } } else { // Load from Instrument or Sample file InputFile f; - - if(f.Open(pszMidiMapName)) + if(f.Open(midiMapName)) { FileReader insFile = GetFileReader(f); - if(insFile.IsValid()) + if(ReadInstrumentFromFile(ins, insFile, false)) { - ReadInstrumentFromFile(nIns, insFile, false); - mpt::PathString szName = pszMidiMapName.GetFullFileName(); - pIns = Instruments[nIns]; - if (!pIns->filename[0]) mpt::String::Copy(pIns->filename, szName.ToLocale().c_str()); - if (!pIns->name[0]) + mpt::PathString filename = midiMapName.GetFullFileName(); + pIns = Instruments[ins]; + if(!pIns->filename[0]) mpt::String::Copy(pIns->filename, filename.ToLocale().c_str()); + if(!pIns->name[0]) { - if (nMidiCode < 128) + if(midiCode < 0x80) { - mpt::String::CopyN(pIns->name, szMidiProgramNames[nMidiCode]); + mpt::String::CopyN(pIns->name, szMidiProgramNames[midiCode]); } else { - uint32 nKey = nMidiCode & 0x7F; - if (nKey >= 24) - mpt::String::CopyN(pIns->name, szMidiPercussionNames[nKey - 24]); + uint32 key = midiCode & 0x7F; + if((key >= 24) && (key < 24 + mpt::size(szMidiPercussionNames))) + mpt::String::CopyN(pIns->name, szMidiPercussionNames[key - 24]); } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp index c896ea552..7b849b1cc 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp @@ -217,11 +217,11 @@ struct MO3Instrument mptIns.nPan = panning; mptIns.dwFlags.set(INS_SETPANNING); } - mptIns.nNNA = nna; + mptIns.nNNA = static_cast(nna.get()); mptIns.nPPS = pps; mptIns.nPPC = ppc; - mptIns.nDCT = dct; - mptIns.nDNA = dca; + mptIns.nDCT = static_cast(dct.get()); + mptIns.nDNA = static_cast(dca.get()); mptIns.nVolSwing = static_cast(std::min(volSwing, 100)); mptIns.nPanSwing = static_cast(std::min(panSwing, 256) / 4u); mptIns.SetCutoff(cutoff & 0x7F, (cutoff & 0x80) != 0); @@ -277,7 +277,7 @@ struct MO3Sample if(frequencyIsHertz) mptSmp.nC5Speed = static_cast(freqFinetune); else - mptSmp.nC5Speed = Util::Round(8363.0 * std::pow(2.0, (freqFinetune + 1408) / 1536.0)); + mptSmp.nC5Speed = mpt::saturate_round(8363.0 * std::pow(2.0, (freqFinetune + 1408) / 1536.0)); } else { mptSmp.nFineTune = static_cast(freqFinetune); @@ -298,7 +298,7 @@ struct MO3Sample if(flags & smpSustain) mptSmp.uFlags.set(CHN_SUSTAINLOOP); if(flags & smpSustainPingPong) mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); - mptSmp.nVibType = AutoVibratoIT2XM[vibType & 7]; + mptSmp.nVibType = static_cast(AutoVibratoIT2XM[vibType & 7]); mptSmp.nVibSweep = vibSweep; mptSmp.nVibDepth = vibDepth; mptSmp.nVibRate = vibRate; @@ -461,8 +461,8 @@ struct MO3Delta8BitParams { typedef int8 sample_t; typedef uint8 unsigned_t; - static const int shift = 7; - static const uint8 dhInit = 4; + enum : int { shift = 7 }; + enum : uint8 { dhInit = 4 }; static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 &/*dh*/, unsigned_t &val) { @@ -479,8 +479,8 @@ struct MO3Delta16BitParams { typedef int16 sample_t; typedef uint16 unsigned_t; - static const int shift = 15; - static const uint8 dhInit = 8; + enum : int { shift = 15 }; + enum : uint8 { dhInit = 8 }; static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 &dh, unsigned_t &val) { @@ -800,17 +800,34 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultSpeed = fileHeader.defaultSpeed ? fileHeader.defaultSpeed : 6; m_nDefaultTempo.Set(fileHeader.defaultTempo ? fileHeader.defaultTempo : 125, 0); - m_ContainerType = MOD_CONTAINERTYPE_MO3; + mpt::ustring originalFormatType; + mpt::ustring originalFormatName; if(fileHeader.flags & MO3FileHeader::isIT) + { SetType(MOD_TYPE_IT); - else if(fileHeader.flags & MO3FileHeader::isS3M) + originalFormatType = U_("it"); + originalFormatName = U_("Impulse Tracker"); + } else if(fileHeader.flags & MO3FileHeader::isS3M) + { SetType(MOD_TYPE_S3M); - else if(fileHeader.flags & MO3FileHeader::isMOD) + originalFormatType = U_("s3m"); + originalFormatName = U_("ScreamTracker 3"); + } else if(fileHeader.flags & MO3FileHeader::isMOD) + { SetType(MOD_TYPE_MOD); - else if(fileHeader.flags & MO3FileHeader::isMTM) + originalFormatType = U_("mod"); + originalFormatName = U_("Generic MOD"); + } else if(fileHeader.flags & MO3FileHeader::isMTM) + { SetType(MOD_TYPE_MTM); - else + originalFormatType = U_("mtm"); + originalFormatName = U_("MultiTracker"); + } else + { SetType(MOD_TYPE_XM); + originalFormatType = U_("xm"); + originalFormatName = U_("FastTracker 2"); + } if(fileHeader.flags & MO3FileHeader::linearSlides) m_SongFlags.set(SONG_LINEARSLIDES); @@ -871,16 +888,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) for(uint32 i = 0; i < 16; i++) { if(fileHeader.sfxMacros[i]) - sprintf(m_MidiCfg.szMidiSFXExt[i], "F0F0%02Xz", fileHeader.sfxMacros[i] - 1); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiSFXExt[i]) = mpt::format("F0F0%1z")(mpt::fmt::HEX0<2>(fileHeader.sfxMacros[i] - 1)); else - strcpy(m_MidiCfg.szMidiSFXExt[i], ""); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiSFXExt[i]) = ""; } for(uint32 i = 0; i < 128; i++) { if(fileHeader.fixedMacros[i][1]) - sprintf(m_MidiCfg.szMidiZXXExt[i], "F0F0%02X%02X", fileHeader.fixedMacros[i][1] - 1, fileHeader.fixedMacros[i][0].get()); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i]) = mpt::format("F0F0%1%2")(mpt::fmt::HEX0<2>(fileHeader.fixedMacros[i][1] - 1), mpt::fmt::HEX0<2>(fileHeader.fixedMacros[i][0].get())); else - strcpy(m_MidiCfg.szMidiZXXExt[i], ""); + mpt::String::WriteAutoBuf(m_MidiCfg.szMidiZXXExt[i]) = ""; } } @@ -1305,9 +1322,9 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) LimitMax(sample.nLength, smpFrom.nLength); sample.uFlags.set(CHN_16BIT, smpFrom.uFlags[CHN_16BIT]); sample.uFlags.set(CHN_STEREO, smpFrom.uFlags[CHN_STEREO]); - if(smpFrom.pSample != nullptr && sample.AllocateSample()) + if(smpFrom.HasSampleData() && sample.AllocateSample()) { - memcpy(sample.pSample, smpFrom.pSample, sample.GetSampleSizeInBytes()); + memcpy(sample.sampleb(), smpFrom.sampleb(), sample.GetSampleSizeInBytes()); } } else if(smpHeader.compressedSize > 0) { @@ -1317,23 +1334,36 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) FileReader sampleData = file.ReadChunk(smpHeader.compressedSize); const uint8 numChannels = sample.GetNumChannels(); + if(compression == MO3Sample::smpDeltaCompression || compression == MO3Sample::smpDeltaPrediction) + { + // In the best case, MO3 compression represents each sample point as two bits. + // As a result, if we have a file length of n, we know that the sample can be at most n*4 sample points long. + size_t maxLength = smpHeader.compressedSize; + uint8 maxSamplesPerByte = 4 / numChannels; + if(Util::MaxValueOfType(maxLength) / maxSamplesPerByte >= maxLength) + maxLength *= maxSamplesPerByte; + else + maxLength = Util::MaxValueOfType(maxLength); + LimitMax(sample.nLength, mpt::saturate_cast(maxLength)); + } + if(compression == MO3Sample::smpDeltaCompression) { if(sample.AllocateSample()) { if(smpHeader.flags & MO3Sample::smp16Bit) - UnpackMO3DeltaSample(sampleData, sample.pSample16, sample.nLength, numChannels); + UnpackMO3DeltaSample(sampleData, sample.sample16(), sample.nLength, numChannels); else - UnpackMO3DeltaSample(sampleData, sample.pSample8, sample.nLength, numChannels); + UnpackMO3DeltaSample(sampleData, sample.sample8(), sample.nLength, numChannels); } } else if(compression == MO3Sample::smpDeltaPrediction) { if(sample.AllocateSample()) { if(smpHeader.flags & MO3Sample::smp16Bit) - UnpackMO3DeltaPredictionSample(sampleData, sample.pSample16, sample.nLength, numChannels); + UnpackMO3DeltaPredictionSample(sampleData, sample.sample16(), sample.nLength, numChannels); else - UnpackMO3DeltaPredictionSample(sampleData, sample.pSample8, sample.nLength, numChannels); + UnpackMO3DeltaPredictionSample(sampleData, sample.sample8(), sample.nLength, numChannels); } } else if(compression == MO3Sample::smpCompressionOgg || compression == MO3Sample::smpSharedOgg) { @@ -1358,12 +1388,12 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) mpegData = sampleData.ReadChunk(sampleData.BytesLeft()); } - if(ReadMP3Sample(smp, mpegData, true) || ReadMediaFoundationSample(smp, mpegData, true)) + if(ReadMP3Sample(smp, mpegData, true, true) || ReadMediaFoundationSample(smp, mpegData, true)) { if(smpHeader.encoderDelay > 0 && smpHeader.encoderDelay < sample.GetSampleSizeInBytes()) { SmpLength delay = smpHeader.encoderDelay / sample.GetBytesPerSample(); - memmove(sample.pSample8, sample.pSample8 + smpHeader.encoderDelay, sample.GetSampleSizeInBytes() - smpHeader.encoderDelay); + memmove(sample.sampleb(), sample.sampleb() + smpHeader.encoderDelay, sample.GetSampleSizeInBytes() - smpHeader.encoderDelay); sample.nLength -= delay; } LimitMax(sample.nLength, smpHeader.length); @@ -1404,7 +1434,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) // We do not handle multiple muxed logical streams as they do not exist in practice in mo3. // We assume sequence numbers are consecutive at the end of the headers. // Corrupted pages get dropped as required by Ogg spec. We cannot do any further sane parsing on them anyway. - // We do not match up multiple muxed stream properly as this wold need parsing of actual packet data to determine or guess the codec. + // We do not match up multiple muxed stream properly as this would need parsing of actual packet data to determine or guess the codec. // Ogg Vorbis files may contain at least an additional Ogg Skeleton stream. It is not clear whether these actually exist in MO3. // We do not validate packet structure or logical bitstream structure (i.e. sequence numbers and granule positions). @@ -1528,13 +1558,13 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) if(headStreamSerials.size() > 1) { - AddToLog(LogWarning, mpt::format(MPT_USTRING("Sample %1: Ogg Vorbis data with shared header and multiple logical bitstreams in header chunk found. This may be handled incorrectly."))(smp)); + AddToLog(LogWarning, mpt::format(U_("Sample %1: Ogg Vorbis data with shared header and multiple logical bitstreams in header chunk found. This may be handled incorrectly."))(smp)); } else if(dataStreamSerials.size() > 1) { - AddToLog(LogWarning, mpt::format(MPT_USTRING("Sample %1: Ogg Vorbis sample with shared header and multiple logical bitstreams found. This may be handled incorrectly."))(smp)); + AddToLog(LogWarning, mpt::format(U_("Sample %1: Ogg Vorbis sample with shared header and multiple logical bitstreams found. This may be handled incorrectly."))(smp)); } else if((dataStreamSerials.size() == 1) && (headStreamSerials.size() == 1) && (dataStreamSerials[0] != headStreamSerials[0])) { - AddToLog(LogInformation, mpt::format(MPT_USTRING("Sample %1: Ogg Vorbis data with shared header and different logical bitstream serials found."))(smp)); + AddToLog(LogInformation, mpt::format(U_("Sample %1: Ogg Vorbis data with shared header and different logical bitstream serials found."))(smp)); } std::string mergedStreamData = mergedStream.str(); @@ -1542,7 +1572,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) sampleChunk.chunk.Rewind(); FileReader::PinnedRawDataView sampleChunkView = sampleChunk.chunk.GetPinnedRawDataView(); - mergedData.insert(mergedData.end(), sampleChunkView.begin(), sampleChunkView.end()); + mergedData.insert(mergedData.end(), mpt::byte_cast(sampleChunkView.begin()), mpt::byte_cast(sampleChunkView.end())); #endif @@ -1576,7 +1606,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) }; OggVorbis_File vf; MemsetZero(vf); - if(ov_open_callbacks(&sampleData, &vf, NULL, 0, callbacks) == 0) + if(ov_open_callbacks(&sampleData, &vf, nullptr, 0, callbacks) == 0) { if(ov_streams(&vf) == 1) { // we do not support chained vorbis samples @@ -1590,7 +1620,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) int current_section = 0; long decodedSamples = 0; bool eof = false; - while(!eof && offset < sample.nLength && sample.pSample != nullptr) + while(!eof && offset < sample.nLength && sample.HasSampleData()) { float **output = nullptr; long ret = ov_read_float(&vf, &output, 1024, ¤t_section); @@ -1610,10 +1640,10 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { if(sample.uFlags[CHN_16BIT]) { - CopyChannelToInterleaved >(sample.pSample16 + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + CopyChannelToInterleaved >(sample.sample16() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); } else { - CopyChannelToInterleaved >(sample.pSample8 + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + CopyChannelToInterleaved >(sample.sample8() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); } } } @@ -1626,7 +1656,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) } } else { - AddToLog(LogWarning, mpt::format(MPT_USTRING("Sample %1: Unsupported Ogg Vorbis chained stream found."))(smp)); + AddToLog(LogWarning, mpt::format(U_("Sample %1: Unsupported Ogg Vorbis chained stream found."))(smp)); unsupportedSamples = true; } ov_clear(&vf); @@ -1650,7 +1680,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) if(sharedHeader) { FileReader::PinnedRawDataView headChunkView = headerChunk.GetPinnedRawDataView(initialRead); - vorb = stb_vorbis_open_pushdata(headChunkView.data(), mpt::saturate_cast(headChunkView.size()), &consumed, &error, nullptr); + vorb = stb_vorbis_open_pushdata(mpt::byte_cast(headChunkView.data()), mpt::saturate_cast(headChunkView.size()), &consumed, &error, nullptr); headerChunk.Skip(consumed); } FileReader::PinnedRawDataView sampleDataView = sampleData.GetPinnedRawDataView(); @@ -1658,7 +1688,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) std::size_t dataLeft = sampleDataView.size(); if(!sharedHeader) { - vorb = stb_vorbis_open_pushdata(data, mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); + vorb = stb_vorbis_open_pushdata(mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); sampleData.Skip(consumed); data += consumed; dataLeft -= consumed; @@ -1670,11 +1700,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) sample.AllocateSample(); SmpLength offset = 0; while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0)) - && offset < sample.nLength && sample.pSample != nullptr) + && offset < sample.nLength && sample.HasSampleData()) { int channels = 0, decodedSamples = 0; float **output; - consumed = stb_vorbis_decode_frame_pushdata(vorb, data, mpt::saturate_cast(dataLeft), &channels, &output, &decodedSamples); + consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &channels, &output, &decodedSamples); sampleData.Skip(consumed); data += consumed; dataLeft -= consumed; @@ -1684,9 +1714,9 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) for(int chn = 0; chn < channels; chn++) { if(sample.uFlags[CHN_16BIT]) - CopyChannelToInterleaved >(sample.pSample16 + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + CopyChannelToInterleaved >(sample.sample16() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); else - CopyChannelToInterleaved >(sample.pSample8 + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + CopyChannelToInterleaved >(sample.sample8() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); } } offset += decodedSamples; @@ -1711,7 +1741,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) // Transfer XM instrument vibrato to samples for(INSTRUMENTINDEX ins = 0; ins < m_nInstruments; ins++) { - PropagateXMAutoVibrato(ins + 1, instrVibrato[ins].type, instrVibrato[ins].sweep, instrVibrato[ins].depth, instrVibrato[ins].rate); + PropagateXMAutoVibrato(ins + 1, static_cast(instrVibrato[ins].type.get()), instrVibrato[ins].sweep, instrVibrato[ins].depth, instrVibrato[ins].rate); } } @@ -1742,6 +1772,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) } } + mpt::ustring madeWithTracker; uint16 cwtv = 0; uint16 cmwt = 0; MPT_UNUSED_VARIABLE(cmwt); @@ -1752,7 +1783,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) FileReader chunk = musicChunk.ReadChunk(len); switch(id) { - case MAGIC4LE('V','E','R','S'): + case MagicLE("VERS"): // Tracker magic bytes (depending on format) switch(m_nType) { @@ -1768,24 +1799,24 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) cwtv = chunk.ReadUint16LE(); break; case MOD_TYPE_XM: - chunk.ReadString(m_madeWithTracker, mpt::CharsetCP437, std::min(FileReader::off_t(32), chunk.GetLength())); + chunk.ReadString(madeWithTracker, mpt::CharsetCP437, std::min(FileReader::off_t(32), chunk.GetLength())); break; case MOD_TYPE_MTM: { uint8 mtmVersion = chunk.ReadUint8(); - m_madeWithTracker = mpt::format(MPT_USTRING("MultiTracker %1.%2"))(mtmVersion >> 4, mtmVersion & 0x0F); + madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(mtmVersion >> 4, mtmVersion & 0x0F); } break; default: break; } break; - case MAGIC4LE('M', 'I', 'D', 'I'): + case MagicLE("MIDI"): // Full MIDI config chunk.ReadStruct(m_MidiCfg); m_MidiCfg.Sanitize(); break; - case MAGIC4LE('O', 'M', 'P', 'T'): + case MagicLE("OMPT"): // Read pattern names: "PNAM" if(chunk.ReadMagic("PNAM")) { @@ -1821,7 +1852,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) if(m_dwLastSavedWithVersion) { - m_madeWithTracker = MPT_USTRING("OpenMPT ") + MptVersion::ToUString(m_dwLastSavedWithVersion); + madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); } break; } @@ -1835,14 +1866,52 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) m_MidiCfg.ClearZxxMacros(); } - if(m_madeWithTracker.empty()) - m_madeWithTracker = mpt::format(MPT_USTRING("MO3 v%1"))(version); + if(fileHeader.flags & MO3FileHeader::modplugMode) + { + // Apply some old ModPlug (mis-)behaviour + for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) + { + if(ModInstrument *ins = Instruments[i]) + { + // Fix pitch / filter envelope being shortened by one tick + if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType()); + // Fix excessive pan swing range + if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + ins->nPanSwing = (ins->nPanSwing + 3) / 4u; + } + } + if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) + { + m_playBehaviour.reset(kITOffset); + m_playBehaviour.reset(kFT2OffsetOutOfRange); + } + if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 23, 00, 00)) + m_playBehaviour.reset(kFT2Periods); + if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + m_playBehaviour.reset(kITInstrWithNoteOff); + } + + if(madeWithTracker.empty()) + madeWithTracker = mpt::format(U_("MO3 v%1"))(version); else - m_madeWithTracker = mpt::format(MPT_USTRING("MO3 v%1 (%2)"))(version, m_madeWithTracker); + madeWithTracker = mpt::format(U_("MO3 v%1 (%2)"))(version, madeWithTracker); + + m_modFormat.formatName = mpt::format(U_("Un4seen MO3 v%1"))(version); + m_modFormat.type = U_("mo3"); + m_modFormat.originalType = std::move(originalFormatType); + m_modFormat.originalFormatName = std::move(originalFormatName); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + if(m_dwLastSavedWithVersion) + m_modFormat.charset = mpt::CharsetWindows1252; + else if(GetType() == MOD_TYPE_MOD) + m_modFormat.charset = mpt::CharsetISO8859_1; + else + m_modFormat.charset = mpt::CharsetCP437; if(unsupportedSamples) { - AddToLog(LogWarning, MPT_USTRING("Some compressed samples could not be loaded because they use an unsupported codec.")); + AddToLog(LogWarning, U_("Some compressed samples could not be loaded because they use an unsupported codec.")); } return true; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp index 831d00da0..47567dae1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp @@ -58,8 +58,6 @@ void CSoundFile::ConvertModCommand(ModCommand &m) case 'H' - 55: m.command = CMD_GLOBALVOLSLIDE; break; case 'K' - 55: m.command = CMD_KEYOFF; break; case 'L' - 55: m.command = CMD_SETENVPOSITION; break; - case 'M' - 55: m.command = CMD_CHANNELVOLUME; break; // Wat. Luckily, MPT never allowed this to be entered in patterns... - case 'N' - 55: m.command = CMD_CHANNELVOLSLIDE; break; // Ditto. case 'P' - 55: m.command = CMD_PANNINGSLIDE; break; case 'R' - 55: m.command = CMD_RETRIG; break; case 'T' - 55: m.command = CMD_TREMOR; break; @@ -104,7 +102,7 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co case CMD_TREMOLO: command = 0x07; break; case CMD_PANNING8: command = 0x08; - if(m_nType & MOD_TYPE_S3M) + if(GetType() & MOD_TYPE_S3M) { if(param <= 0x80) { @@ -279,7 +277,7 @@ struct MODSampleHeader // Convert OpenMPT's internal sample header to a MOD sample header. SmpLength ConvertToMOD(const ModSample &mptSmp) { - SmpLength writeLength = mptSmp.pSample != nullptr ? mptSmp.nLength : 0; + SmpLength writeLength = mptSmp.HasSampleData() ? mptSmp.nLength : 0; // If the sample size is odd, we have to add a padding byte, as all sample sizes in MODs are even. if((writeLength % 2u) != 0) { @@ -322,14 +320,14 @@ struct MODSampleHeader } // Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore - static const uint32 INVALID_BYTE_THRESHOLD = 40; + enum : uint32 { INVALID_BYTE_THRESHOLD = 40 }; // This threshold is used for files where the file magic only gives a // fragile result which alone would lead to too many false positives. // In particular, the files from Inconexia demo by Iguana // (https://www.pouet.net/prod.php?which=830) which have 3 \0 bytes in // the file magic tend to cause misdetection of random files. - static const uint32 INVALID_BYTE_FRAGILE_THRESHOLD = 1; + enum : uint32 { INVALID_BYTE_FRAGILE_THRESHOLD = 1 }; // Retrieve the internal sample format flags for this sample. static SampleIO GetSampleFormat() @@ -383,10 +381,10 @@ struct AMInstrument switch(waveform) { default: - case 0: sample.pSample8[i] = ModSinusTable[i * 2]; break; // Sine - case 1: sample.pSample8[i] = static_cast(-128 + i * 8); break; // Saw - case 2: sample.pSample8[i] = i < 16 ? -128 : 127; break; // Square - case 3: sample.pSample8[i] = mpt::random(rng); break; // Noise + case 0: sample.sample8()[i] = ModSinusTable[i * 2]; break; // Sine + case 1: sample.sample8()[i] = static_cast(-128 + i * 8); break; // Saw + case 2: sample.sample8()[i] = i < 16 ? -128 : 127; break; // Square + case 3: sample.sample8()[i] = mpt::random(rng); break; // Noise } } } @@ -457,10 +455,10 @@ struct PT36IffChunk // IFF chunk names enum ChunkIdentifiers { - idVERS = MAGIC4BE('V','E','R','S'), - idINFO = MAGIC4BE('I','N','F','O'), - idCMNT = MAGIC4BE('C','M','N','T'), - idPTDT = MAGIC4BE('P','T','D','T'), + idVERS = MagicBE("VERS"), + idINFO = MagicBE("INFO"), + idCMNT = MagicBE("CMNT"), + idPTDT = MagicBE("PTDT"), }; uint32be signature; // IFF chunk name @@ -587,7 +585,7 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN file.Seek(patternStartOffset); } -#ifdef _DEBUG +#ifdef MPT_BUILD_DEBUG // Check if the "hidden" patterns in the order list are actually real, i.e. if they are saved in the file. // OpenMPT did this check in the past, but no other tracker appears to do this. // Interestingly, (broken) variants of the ProTracker modules @@ -631,11 +629,11 @@ void CSoundFile::ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m) { // Read Period uint16 period = (((static_cast(data[0]) & 0x0F) << 8) | data[1]); - m.note = NOTE_NONE; + size_t note = NOTE_NONE; if(period > 0 && period != 0xFFF) { - m.note = 6 * 12 + 35 + NOTE_MIN; - for(int i = 0; i < 6 * 12; i++) + note = mpt::size(ProTrackerPeriodTable) + 23 + NOTE_MIN; + for(size_t i = 0; i < mpt::size(ProTrackerPeriodTable); i++) { if(period >= ProTrackerPeriodTable[i]) { @@ -645,15 +643,16 @@ void CSoundFile::ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m) uint16 p2 = ProTrackerPeriodTable[i]; if(p1 - period < (period - p2)) { - m.note = static_cast(i + 35 + NOTE_MIN); + note = i + 23 + NOTE_MIN; break; } } - m.note = static_cast(i + 36 + NOTE_MIN); + note = i + 24 + NOTE_MIN; break; } } } + m.note = static_cast(note); // Read Instrument m.instr = (data[2] >> 4) | (data[0] & 0x10); // Read Effect @@ -682,62 +681,62 @@ static bool CheckMODMagic(const char magic[4], MODMagicResult &result) || IsMagic(magic, "NSMS") // kingdomofpleasure.mod by bee hunter || IsMagic(magic, "LARD")) // judgement_day_gvine.mod by 4-mat { - result.madeWithTracker = MPT_ULITERAL("Generic ProTracker or compatible"); + result.madeWithTracker = UL_("Generic ProTracker or compatible"); result.numChannels = 4; } else if(IsMagic(magic, "M&K!") // "His Master's Noise" musicdisk || IsMagic(magic, "FEST") // "His Master's Noise" musicdisk || IsMagic(magic, "N.T.")) { - result.madeWithTracker = MPT_ULITERAL("NoiseTracker"); + result.madeWithTracker = UL_("NoiseTracker"); result.isNoiseTracker = true; result.numChannels = 4; } else if(IsMagic(magic, "OKTA") || IsMagic(magic, "OCTA")) { // Oktalyzer - result.madeWithTracker = MPT_ULITERAL("Oktalyzer"); + result.madeWithTracker = UL_("Oktalyzer"); result.numChannels = 8; } else if(IsMagic(magic, "CD81") || IsMagic(magic, "CD61")) { // Octalyser on Atari STe/Falcon - result.madeWithTracker = MPT_ULITERAL("Octalyser (Atari)"); + result.madeWithTracker = UL_("Octalyser (Atari)"); result.numChannels = magic[2] - '0'; } else if(IsMagic(magic, "M\0\0\0") || IsMagic(magic, "8\0\0\0")) { // Inconexia demo by Iguana, delta samples (https://www.pouet.net/prod.php?which=830) - result.madeWithTracker = MPT_ULITERAL("Inconexia demo (delta samples)"); + result.madeWithTracker = UL_("Inconexia demo (delta samples)"); result.invalidByteThreshold = MODSampleHeader::INVALID_BYTE_FRAGILE_THRESHOLD; result.numChannels = (magic[0] == '8') ? 8 : 4; } else if(!memcmp(magic, "FA0", 3) && magic[3] >= '4' && magic[3] <= '8') { // Digital Tracker on Atari Falcon - result.madeWithTracker = MPT_ULITERAL("Digital Tracker"); + result.madeWithTracker = UL_("Digital Tracker"); result.numChannels = magic[3] - '0'; } else if((!memcmp(magic, "FLT", 3) || !memcmp(magic, "EXO", 3)) && magic[3] >= '4' && magic[3] <= '9') { // FLTx / EXOx - Startrekker by Exolon / Fairlight - result.madeWithTracker = MPT_ULITERAL("Startrekker"); + result.madeWithTracker = UL_("Startrekker"); result.isStartrekker = true; result.setMODVBlankTiming = true; result.numChannels = magic[3] - '0'; } else if(magic[0] >= '1' && magic[0] <= '9' && !memcmp(magic + 1, "CHN", 3)) { // xCHN - Many trackers - result.madeWithTracker = MPT_ULITERAL("Generic MOD-compatible Tracker"); + result.madeWithTracker = UL_("Generic MOD-compatible Tracker"); result.isGenericMultiChannel = true; result.numChannels = magic[0] - '0'; } else if(magic[0] >= '1' && magic[0] <= '9' && magic[1]>='0' && magic[1] <= '9' && (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2))) { // xxCN / xxCH - Many trackers - result.madeWithTracker = MPT_ULITERAL("Generic MOD-compatible Tracker"); + result.madeWithTracker = UL_("Generic MOD-compatible Tracker"); result.isGenericMultiChannel = true; result.numChannels = (magic[0] - '0') * 10 + magic[1] - '0'; } else if(!memcmp(magic, "TDZ", 3) && magic[3] >= '4' && magic[3] <= '9') { // TDZx - TakeTracker - result.madeWithTracker = MPT_ULITERAL("TakeTracker"); + result.madeWithTracker = UL_("TakeTracker"); result.numChannels = magic[3] - '0'; } else { @@ -780,7 +779,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMOD(MemoryFileReader file, co } -bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) +bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) { char magic[4]; if(!file.Seek(1080) || !file.ReadArray(magic)) @@ -804,14 +803,13 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) } m_nChannels = modMagicResult.numChannels; - if(modMagicResult.madeWithTracker) - { - m_madeWithTracker = modMagicResult.madeWithTracker; - } + bool isNoiseTracker = modMagicResult.isNoiseTracker; bool isStartrekker = modMagicResult.isStartrekker; bool isGenericMultiChannel = modMagicResult.isGenericMultiChannel; bool isInconexia = IsMagic(magic, "M\0\0\0") || IsMagic(magic, "8\0\0\0"); + // A loop length of zero will freeze ProTracker, so assume that modules having such a value were not meant to be played on Amiga. Fixes LHS_MI.MOD + bool hasRepLen0 = false; if(modMagicResult.setMODVBlankTiming) { m_playBehaviour.set(kMODVBlankTiming); @@ -845,6 +843,10 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) { isNoiseTracker = false; } + if(sampleHeader.length && !sampleHeader.loopLength) + { + hasRepLen0 = true; + } } // If there is too much binary garbage in the sample headers, reject the file. if(invalidBytes > modMagicResult.invalidByteThreshold) @@ -879,7 +881,7 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) if(isMdKd && GetNumChannels() == 8) { // M.K. with 8 channels = Grave Composer - m_madeWithTracker = MPT_USTRING("Mod's Grave"); + modMagicResult.madeWithTracker = UL_("Mod's Grave"); } if(isFLT8) @@ -1086,7 +1088,7 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) } } - if(onlyAmigaNotes && (IsMagic(magic, "M.K.") || IsMagic(magic, "M!K!") || IsMagic(magic, "PATT"))) + if(onlyAmigaNotes && !hasRepLen0 && (IsMagic(magic, "M.K.") || IsMagic(magic, "M!K!") || IsMagic(magic, "PATT"))) { // M.K. files that don't exceed the Amiga note limit (fixes mod.mothergoose) m_SongFlags.set(SONG_AMIGALIMITS); @@ -1108,7 +1110,7 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) } } else if(!onlyAmigaNotes && fileHeader.restartPos == 0x7F && isMdKd && fileHeader.restartPos + 1u >= realOrders) { - m_madeWithTracker = MPT_USTRING("ScreamTracker"); + modMagicResult.madeWithTracker = UL_("ScreamTracker"); } if(onlyAmigaNotes && !isGenericMultiChannel && filterTransitions < 7) @@ -1167,7 +1169,7 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) if(!filename.empty()) { // Find instrument definition file - const mpt::PathString exts[] = { MPT_PATHSTRING(".nt"), MPT_PATHSTRING(".NT"), MPT_PATHSTRING(".as"), MPT_PATHSTRING(".AS") }; + const mpt::PathString exts[] = { P_(".nt"), P_(".NT"), P_(".as"), P_(".AS") }; for(const auto &ext : exts) { mpt::PathString infoName = filename + ext; @@ -1175,11 +1177,11 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) if(infoName.IsFile() && amFile.Open(infoName) && (amData = GetFileReader(amFile)).IsValid() && amData.ReadArray(stMagic)) { if(!memcmp(stMagic, "ST1.2 ModuleINFO", 16)) - m_madeWithTracker = MPT_USTRING("Startrekker 1.2"); + modMagicResult.madeWithTracker = UL_("Startrekker 1.2"); else if(!memcmp(stMagic, "ST1.3 ModuleINFO", 16)) - m_madeWithTracker = MPT_USTRING("Startrekker 1.3"); + modMagicResult.madeWithTracker = UL_("Startrekker 1.3"); else if(!memcmp(stMagic, "AudioSculpture10", 16)) - m_madeWithTracker = MPT_USTRING("AudioSculpture 1.0"); + modMagicResult.madeWithTracker = UL_("AudioSculpture 1.0"); else continue; @@ -1233,7 +1235,7 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) // modules are checked. if(isMdKd && hasTempoCommands && !definitelyCIA) { - const double songTime = GetSongTime(); + const double songTime = GetLength(eNoAdjust).front().duration; if(songTime >= 600.0) { m_playBehaviour.set(kMODVBlankTiming); @@ -1244,11 +1246,17 @@ bool CSoundFile::ReadMod(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kMODVBlankTiming); } else { - m_madeWithTracker = MPT_USTRING("ProTracker (VBlank)"); + modMagicResult.madeWithTracker = UL_("ProTracker (VBlank)"); } } } + std::transform(std::begin(magic), std::end(magic), std::begin(magic), [](unsigned char c) -> unsigned char { return (c < ' ') ? ' ' : c; }); + m_modFormat.formatName = mpt::format(U_("ProTracker MOD (%1)"))(mpt::ToUnicode(mpt::CharsetASCII, std::string(std::begin(magic), std::end(magic)))); + m_modFormat.type = U_("mod"); + if(modMagicResult.madeWithTracker) m_modFormat.madeWithTracker = modMagicResult.madeWithTracker; + m_modFormat.charset = mpt::CharsetISO8859_1; + return true; } @@ -1746,31 +1754,37 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) } } + const MPT_UCHAR_TYPE *madeWithTracker = UL_(""); switch(minVersion) { case UST1_00: - m_madeWithTracker = MPT_USTRING("Ultimate Soundtracker 1.0-1.21"); + madeWithTracker = UL_("Ultimate Soundtracker 1.0-1.21"); break; case UST1_80: - m_madeWithTracker = MPT_USTRING("Ultimate Soundtracker 1.8-2.0"); + madeWithTracker = UL_("Ultimate Soundtracker 1.8-2.0"); break; case ST2_00_Exterminator: - m_madeWithTracker = MPT_USTRING("SoundTracker 2.0 / D.O.C. SoundTracker II"); + madeWithTracker = UL_("SoundTracker 2.0 / D.O.C. SoundTracker II"); break; case ST_III: - m_madeWithTracker = MPT_USTRING("Defjam Soundtracker III / Alpha Flight SoundTracker IV / D.O.C. SoundTracker IV / VI"); + madeWithTracker = UL_("Defjam Soundtracker III / Alpha Flight SoundTracker IV / D.O.C. SoundTracker IV / VI"); break; case ST_IX: - m_madeWithTracker = MPT_USTRING("D.O.C. SoundTracker IX"); + madeWithTracker = UL_("D.O.C. SoundTracker IX"); break; case MST1_00: - m_madeWithTracker = MPT_USTRING("Master Soundtracker 1.0"); + madeWithTracker = UL_("Master Soundtracker 1.0"); break; case ST2_00: - m_madeWithTracker = MPT_USTRING("SoundTracker 2.0 / 2.1 / 2.2"); + madeWithTracker = UL_("SoundTracker 2.0 / 2.1 / 2.2"); break; } + m_modFormat.formatName = U_("Soundtracker"); + m_modFormat.type = U_("stk"); + m_modFormat.madeWithTracker = madeWithTracker; + m_modFormat.charset = mpt::CharsetISO8859_1; + // Reading samples if(loadFlags & loadSampleData) { @@ -1856,10 +1870,16 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) if(IsMagic(magic, "MTN\0")) { - m_madeWithTracker = MPT_USTRING("SoundTracker 2.6"); + m_modFormat.formatName = U_("MnemoTroN SoundTracker"); + m_modFormat.type = U_("st26"); + m_modFormat.madeWithTracker = U_("SoundTracker 2.6"); + m_modFormat.charset = mpt::CharsetISO8859_1; } else if(IsMagic(magic, "IT10")) { - m_madeWithTracker = MPT_USTRING("Ice Tracker 1.0 / 1.1"); + m_modFormat.formatName = U_("Ice Tracker"); + m_modFormat.type = U_("ice"); + m_modFormat.madeWithTracker = U_("Ice Tracker 1.0 / 1.1"); + m_modFormat.charset = mpt::CharsetISO8859_1; } else { return false; @@ -2002,9 +2022,9 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) struct PT36Header { - char magicFORM[4]; // "FORM" - uint8be dummy1[4]; - char magicMODL[4]; // "MODL" + char magicFORM[4]; // "FORM" + uint32be size; + char magicMODL[4]; // "MODL" }; MPT_BINARY_STRUCT(PT36Header, 12) @@ -2106,14 +2126,14 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) break; case PT36IffChunk::idPTDT: - ok = ReadMod(chunk, loadFlags); + ok = ReadMOD(chunk, loadFlags); break; } } while(file.ReadStruct(iffHead)); if(version.empty()) { - version = MPT_USTRING("3.6"); + version = U_("3.6"); } // both an info chunk and a module are required @@ -2133,7 +2153,6 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) && IsInRange(info.dateMinute, 0, 59) && IsInRange(info.dateSecond, 0, 59)) { FileHistory mptHistory; - MemsetZero(mptHistory); mptHistory.loadDate.tm_year = info.dateYear; mptHistory.loadDate.tm_mon = info.dateMonth - 1; mptHistory.loadDate.tm_mday = info.dateDay; @@ -2157,7 +2176,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) } } - m_madeWithTracker = MPT_USTRING("ProTracker ") + version; + m_modFormat.madeWithTracker = U_("ProTracker ") + version; } m_SongFlags.set(SONG_PT_MODE); m_playBehaviour.set(kMODIgnorePanning); @@ -2170,18 +2189,18 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) #ifndef MODPLUG_NO_FILESAVE -bool CSoundFile::SaveMod(const mpt::PathString &filename) const +bool CSoundFile::SaveMod(std::ostream &f) const { - FILE *f; - - if(m_nChannels == 0 || filename.empty()) return false; - if((f = mpt_fopen(filename, "wb")) == nullptr) return false; + if(m_nChannels == 0) + { + return false; + } // Write song title { char name[20]; mpt::String::Write(name, m_songName); - fwrite(name, 20, 1, f); + mpt::IO::Write(f, name); } std::vector sampleLength(32, 0); @@ -2216,7 +2235,7 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const MODSampleHeader sampleHeader; mpt::String::Write(sampleHeader.name, m_szNames[sampleSource[smp]]); sampleLength[smp] = sampleHeader.ConvertToMOD(sampleSource[smp] <= GetNumSamples() ? GetSample(sampleSource[smp]) : ModSample(MOD_TYPE_MOD)); - fwrite(&sampleHeader, sizeof(sampleHeader), 1, f); + mpt::IO::Write(f, sampleHeader); } // Write order list @@ -2242,7 +2261,7 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const } } fileHeader.numOrders = writtenOrders; - fwrite(&fileHeader, sizeof(fileHeader), 1, f); + mpt::IO::Write(f, fileHeader); // Write magic bytes char modMagic[4]; @@ -2264,7 +2283,7 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const modMagic[0] += static_cast(writeChannels / 10u); modMagic[1] += static_cast(writeChannels % 10u); } - fwrite(modMagic, 4, 1, f); + mpt::IO::Write(f, modMagic); // Write patterns std::vector events; @@ -2274,7 +2293,7 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const { // Invent empty pattern events.assign(writeChannels * 64 * 4, 0); - fwrite(events.data(), events.size(), 1, f); + mpt::IO::Write(f, events); continue; } @@ -2284,7 +2303,7 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const { // Invent empty row events.assign(writeChannels * 4, 0); - fwrite(events.data(), events.size(), 1, f); + mpt::IO::Write(f, events); continue; } PatternRow rowBase = Patterns[pat].GetRow(row); @@ -2306,9 +2325,9 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const uint16 period = 0; // Convert note to period - if(m.note >= 36 + NOTE_MIN && m.note < CountOf(ProTrackerPeriodTable) + 36 + NOTE_MIN) + if(m.note >= 24 + NOTE_MIN && m.note < mpt::size(ProTrackerPeriodTable) + 24 + NOTE_MIN) { - period = ProTrackerPeriodTable[m.note - 36 - NOTE_MIN]; + period = ProTrackerPeriodTable[m.note - 24 - NOTE_MIN]; } uint8 instr = (m.instr <= 31) ? m.instr : 0; @@ -2318,7 +2337,7 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const events[eventByte++] = ((instr & 0x0F) << 4) | (command & 0x0F); events[eventByte++] = param; } - fwrite(events.data(), eventByte, 1, f); + mpt::IO::WriteRaw(f, mpt::as_span(events.data(), eventByte)); } } @@ -2341,28 +2360,28 @@ bool CSoundFile::SaveMod(const mpt::PathString &filename) const } const ModSample &sample = Samples[sampleSource[smp]]; - const long sampleStart = ftell(f); + const mpt::IO::Offset sampleStart = mpt::IO::TellWrite(f); const size_t writtenBytes = MODSampleHeader::GetSampleFormat().WriteSample(f, sample, sampleLength[smp]); - const int8 silence[] = { 0, 0 }; + const int8 silence = 0; // Write padding byte if the sample size is odd. if((writtenBytes % 2u) != 0) { - fwrite(silence, 1, 1, f); + mpt::IO::Write(f, silence); } if(!sample.uFlags[CHN_LOOP] && writtenBytes >= 2) { // First two bytes of oneshot samples have to be 0 due to PT's one-shot loop - const long sampleEnd = ftell(f); - fseek(f, sampleStart, SEEK_SET); - fwrite(&silence, 2, 1, f); - fseek(f, sampleEnd, SEEK_SET); + const mpt::IO::Offset sampleEnd = mpt::IO::TellWrite(f); + mpt::IO::SeekAbsolute(f, sampleStart); + mpt::IO::Write(f, silence); + mpt::IO::Write(f, silence); + mpt::IO::SeekAbsolute(f, sampleEnd); } } - fclose(f); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp index 2d0dca5d2..2a98b2493 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp @@ -454,7 +454,12 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_MT2); InitializeChannels(); - mpt::String::Read(m_madeWithTracker, mpt::CharsetWindows1252, fileHeader.trackerName); + + m_modFormat.formatName = mpt::format(U_("MadTracker %1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); + m_modFormat.type = U_("mt2"); + mpt::String::Read(m_modFormat.madeWithTracker, mpt::CharsetWindows1252, fileHeader.trackerName); + m_modFormat.charset = mpt::CharsetWindows1252; + mpt::String::Read(m_songName, fileHeader.songName); m_nChannels = fileHeader.numChannels; m_nDefaultSpeed = Clamp(fileHeader.ticksPerLine, 1, 31); @@ -578,7 +583,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) switch(id) { - case MAGIC4LE('B','P','M','+'): + case MagicLE("BPM+"): if(!hasLegacyTempo) { m_nTempoMode = tempoModeModern; @@ -590,13 +595,13 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } break; - case MAGIC4LE('T','F','X','M'): + case MagicLE("TFXM"): break; - case MAGIC4LE('T','R','K','O'): + case MagicLE("TRKO"): break; - case MAGIC4LE('T','R','K','S'): + case MagicLE("TRKS"): m_nSamplePreAmp = chunk.ReadUint16LE() / 256u; // 131072 is 0dB... I think (that's how MTIOModule_MT2.cpp reads) m_nVSTiVolume = m_nSamplePreAmp / 2u; for(CHANNELINDEX c = 0; c < GetNumChannels(); c++) @@ -611,7 +616,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } break; - case MAGIC4LE('T','R','K','L'): + case MagicLE("TRKL"): for(CHANNELINDEX i = 0; i < m_nChannels && chunk.CanRead(1); i++) { std::string name; @@ -620,7 +625,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } break; - case MAGIC4LE('P','A','T','N'): + case MagicLE("PATN"): for(PATTERNINDEX i = 0; i < fileHeader.numPatterns && chunk.CanRead(1) && Patterns.IsValidIndex(i); i++) { std::string name; @@ -629,15 +634,15 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } break; - case MAGIC4LE('M','S','G','\0'): + case MagicLE("MSG\0"): chunk.Skip(1); // Show message on startup m_songMessage.Read(chunk, chunk.BytesLeft(), SongMessage::leCRLF); break; - case MAGIC4LE('P','I','C','T'): + case MagicLE("PICT"): break; - case MAGIC4LE('S','U','M','\0'): + case MagicLE("SUM\0"): { uint8 summaryMask[6]; chunk.ReadArray(summaryMask); @@ -650,14 +655,14 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } break; - case MAGIC4LE('T','M','A','P'): + case MagicLE("TMAP"): break; - case MAGIC4LE('M','I','D','I'): + case MagicLE("MIDI"): break; - case MAGIC4LE('T','R','E','Q'): + case MagicLE("TREQ"): break; - case MAGIC4LE('V','S','T','2'): + case MagicLE("VST2"): numVST = chunk.ReadUint32LE(); #ifndef NO_VST if(!(loadFlags & loadPluginData)) @@ -682,7 +687,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) // Remove ".dll" from library name mixPlug.Info.szLibraryName[len - 4] = '\0'; } - mixPlug.Info.dwPluginId1 = kEffectMagic; + mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; mixPlug.Info.dwPluginId2 = vstHeader.fxID; if(vstHeader.track >= m_nChannels) { @@ -918,9 +923,9 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) if(fileHeader.version >= 0x0202) envMask = instrChunk.ReadUint32LE(); mptIns->nFadeOut = insHeader.fadeout; - const uint8 NNA[4] = { NNA_NOTECUT, NNA_CONTINUE, NNA_NOTEOFF, NNA_NOTEFADE }; - const uint8 DCT[4] = { DCT_NONE, DCT_NOTE, DCT_SAMPLE, DCT_INSTRUMENT }; - const uint8 DNA[4] = { DNA_NOTECUT, DNA_NOTEFADE /* actually continue, but IT doesn't have that */, DNA_NOTEOFF, DNA_NOTEFADE }; + const NewNoteAction NNA[4] = { NNA_NOTECUT, NNA_CONTINUE, NNA_NOTEOFF, NNA_NOTEFADE }; + const DuplicateCheckType DCT[4] = { DCT_NONE, DCT_NOTE, DCT_SAMPLE, DCT_INSTRUMENT }; + const DuplicateNoteAction DNA[4] = { DNA_NOTECUT, DNA_NOTEFADE /* actually continue, but IT doesn't have that */, DNA_NOTEOFF, DNA_NOTEFADE }; mptIns->nNNA = NNA[insHeader.nna & 3]; mptIns->nDCT = DCT[(insHeader.nna >> 8) & 3]; mptIns->nDNA = DNA[(insHeader.nna >> 12) & 3]; @@ -1092,7 +1097,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) if(sample > 0 && sample <= m_nSamples) { ModSample &mptSmp = Samples[sample]; - mptSmp.nVibType = insHeader.vibtype; + mptSmp.nVibType = static_cast(insHeader.vibtype & 3); // In fact, MT2 only implements sine vibrato mptSmp.nVibSweep = insHeader.vibsweep; mptSmp.nVibDepth = insHeader.vibdepth; mptSmp.nVibRate = insHeader.vibrate; @@ -1151,7 +1156,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } SetSamplePath(i + 1, mpt::PathString::FromLocaleSilent(filename)); #elif !defined(LIBOPENMPT_BUILD_TEST) - AddToLog(LogWarning, mpt::format(MPT_USTRING("Loading external sample %1 ('%2') failed: External samples are not supported."))(i, mpt::ToUnicode(GetCharsetFile(), filename))); + AddToLog(LogWarning, mpt::format(U_("Loading external sample %1 ('%2') failed: External samples are not supported."))(i, mpt::ToUnicode(GetCharsetFile(), filename))); #endif // MPT_EXTERNAL_SAMPLES } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp index b4cc3e0f1..10140ba3f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp @@ -137,7 +137,11 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) mpt::String::Read(m_songName, fileHeader.songName); m_nSamples = fileHeader.numSamples; m_nChannels = fileHeader.numChannels; - m_madeWithTracker = mpt::format(MPT_USTRING("MultiTracker %1.%2"))(fileHeader.version >> 4, fileHeader.version & 0x0F); + + m_modFormat.formatName = U_("MultiTracker"); + m_modFormat.type = U_("mtm"); + m_modFormat.madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(fileHeader.version >> 4, fileHeader.version & 0x0F); + m_modFormat.charset = mpt::CharsetCP437; // Reading instruments for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp index eed4f9a2f..e2be1a04a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp @@ -19,14 +19,14 @@ struct OktIffChunk // IFF chunk names enum ChunkIdentifiers { - idCMOD = MAGIC4BE('C','M','O','D'), - idSAMP = MAGIC4BE('S','A','M','P'), - idSPEE = MAGIC4BE('S','P','E','E'), - idSLEN = MAGIC4BE('S','L','E','N'), - idPLEN = MAGIC4BE('P','L','E','N'), - idPATT = MAGIC4BE('P','A','T','T'), - idPBOD = MAGIC4BE('P','B','O','D'), - idSBOD = MAGIC4BE('S','B','O','D'), + idCMOD = MagicBE("CMOD"), + idSAMP = MagicBE("SAMP"), + idSPEE = MagicBE("SPEE"), + idSLEN = MagicBE("SLEN"), + idPLEN = MagicBE("PLEN"), + idPATT = MagicBE("PATT"), + idPBOD = MagicBE("PBOD"), + idSBOD = MagicBE("SBOD"), }; uint32be signature; // IFF chunk name @@ -60,28 +60,21 @@ static void ReadOKTSamples(FileReader &chunk, std::vector &sample7bit, CSo OktSample oktSmp; chunk.ReadStruct(oktSmp); - oktSmp.length = oktSmp.length; - oktSmp.loopStart = oktSmp.loopStart * 2; - oktSmp.loopLength = oktSmp.loopLength * 2; - oktSmp.volume = oktSmp.volume; - oktSmp.type = oktSmp.type; - mptSmp.Initialize(); mpt::String::Read(sndFile.m_szNames[smp], oktSmp.name); mptSmp.nC5Speed = 8287; mptSmp.nGlobalVol = 64; - mptSmp.nVolume = MIN(oktSmp.volume, 64) * 4; + mptSmp.nVolume = std::min(oktSmp.volume, 64u) * 4u; mptSmp.nLength = oktSmp.length & ~1; // round down - // parse loops - if (oktSmp.loopLength > 2 && static_cast(oktSmp.loopStart) + static_cast(oktSmp.loopLength) <= mptSmp.nLength) + // Parse loops + const SmpLength loopStart = oktSmp.loopStart * 2; + const SmpLength loopLength = oktSmp.loopLength * 2; + if(loopLength > 2 && loopStart + loopLength <= mptSmp.nLength) { - mptSmp.nSustainStart = oktSmp.loopStart; - mptSmp.nSustainEnd = oktSmp.loopStart + oktSmp.loopLength; - if (mptSmp.nSustainStart < mptSmp.nLength && mptSmp.nSustainEnd <= mptSmp.nLength) - mptSmp.uFlags |= CHN_SUSTAINLOOP; - else - mptSmp.nSustainStart = mptSmp.nSustainEnd = 0; + mptSmp.uFlags.set(CHN_SUSTAINLOOP); + mptSmp.nSustainStart = loopStart; + mptSmp.nSustainEnd = loopStart + loopLength; } sample7bit[smp - 1] = (oktSmp.type == 0 || oktSmp.type == 2); } @@ -89,16 +82,18 @@ static void ReadOKTSamples(FileReader &chunk, std::vector &sample7bit, CSo // Parse a pattern block -static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX nPat, CSoundFile &sndFile) +static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndFile) { if(!chunk.CanRead(2)) { + // Invent empty pattern + sndFile.Patterns.Insert(pat, 64); return; } ROWINDEX rows = Clamp(static_cast(chunk.ReadUint16BE()), ROWINDEX(1), MAX_PATTERN_ROWS); - if(!sndFile.Patterns.Insert(nPat, rows)) + if(!sndFile.Patterns.Insert(pat, rows)) { return; } @@ -107,36 +102,37 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX nPat, CSoundFile &snd for(ROWINDEX row = 0; row < rows; row++) { - ModCommand *m = sndFile.Patterns[nPat].GetRow(row); - for(CHANNELINDEX chn = 0; chn < chns; chn++, m++) + ModCommand *rowCmd = sndFile.Patterns[pat].GetRow(row); + for(CHANNELINDEX chn = 0; chn < chns; chn++) { + ModCommand &m = rowCmd[chn]; uint8 note = chunk.ReadUint8(); uint8 instr = chunk.ReadUint8(); uint8 effect = chunk.ReadUint8(); - m->param = chunk.ReadUint8(); + m.param = chunk.ReadUint8(); if(note > 0 && note <= 36) { - m->note = note + 48; - m->instr = instr + 1; + m.note = note + (NOTE_MIDDLEC - 13); + m.instr = instr + 1; } else { - m->instr = 0; + m.instr = 0; } switch(effect) { case 0: // Nothing - m->param = 0; + m.param = 0; break; case 1: // 1 Portamento Down (Period) - m->command = CMD_PORTAMENTOUP; - m->param &= 0x0F; + m.command = CMD_PORTAMENTOUP; + m.param &= 0x0F; break; case 2: // 2 Portamento Up (Period) - m->command = CMD_PORTAMENTODOWN; - m->param &= 0x0F; + m.command = CMD_PORTAMENTODOWN; + m.param &= 0x0F; break; #if 0 @@ -144,111 +140,110 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX nPat, CSoundFile &snd For now I'm going to leave these unimplemented. */ case 10: // A Arpeggio 1 (down, orig, up) case 11: // B Arpeggio 2 (orig, up, orig, down) - if (m->param) - m->command = CMD_ARPEGGIO; + if (m.param) + m.command = CMD_ARPEGGIO; break; #endif // This one is close enough to "standard" arpeggio -- I think! case 12: // C Arpeggio 3 (up, up, orig) - if (m->param) - m->command = CMD_ARPEGGIO; + if (m.param) + m.command = CMD_ARPEGGIO; break; case 13: // D Slide Down (Notes) - if (m->param) + if (m.param) { - m->command = CMD_NOTESLIDEDOWN; - m->param = 0x10 | MIN(0x0F, m->param); + m.command = CMD_NOTESLIDEDOWN; + m.param = 0x10 | MIN(0x0F, m.param); } break; case 30: // U Slide Up (Notes) - if (m->param) + if (m.param) { - m->command = CMD_NOTESLIDEUP; - m->param = 0x10 | MIN(0x0F, m->param); + m.command = CMD_NOTESLIDEUP; + m.param = 0x10 | MIN(0x0F, m.param); } break; - /* We don't have fine note slide, but this is supposed to happen once - per row. Sliding every 5 (non-note) ticks kind of works (at least at - speed 6), but implementing fine slides would of course be better. */ + // We don't have fine note slide, but this is supposed to happen once + // per row. Sliding every 5 (non-note) ticks kind of works (at least at + // speed 6), but implementing fine slides would of course be better. case 21: // L Slide Down Once (Notes) - if (m->param) + if (m.param) { - m->command = CMD_NOTESLIDEDOWN; - m->param = 0x50 | MIN(0x0F, m->param); + m.command = CMD_NOTESLIDEDOWN; + m.param = 0x50 | MIN(0x0F, m.param); } break; case 17: // H Slide Up Once (Notes) - if (m->param) + if (m.param) { - m->command = CMD_NOTESLIDEUP; - m->param = 0x50 | MIN(0x0F, m->param); + m.command = CMD_NOTESLIDEUP; + m.param = 0x50 | MIN(0x0F, m.param); } break; case 15: // F Set Filter <>00:ON - // Not implemented, but let's import it anyway... - m->command = CMD_MODCMDEX; - m->param = !!m->param; + m.command = CMD_MODCMDEX; + m.param = !!m.param; break; case 25: // P Pos Jump - m->command = CMD_POSITIONJUMP; + m.command = CMD_POSITIONJUMP; break; case 27: // R Release sample (apparently not listed in the help!) - m->Clear(); - m->note = NOTE_KEYOFF; + m.Clear(); + m.note = NOTE_KEYOFF; break; case 28: // S Speed - m->command = CMD_SPEED; // or tempo? + m.command = CMD_SPEED; // or tempo? break; case 31: // V Volume - m->command = CMD_VOLUMESLIDE; - switch (m->param >> 4) + m.command = CMD_VOLUMESLIDE; + switch (m.param >> 4) { case 4: - if (m->param != 0x40) + if (m.param != 0x40) { - m->param &= 0x0F; // D0x + m.param &= 0x0F; // D0x break; } // 0x40 is set volume -- fall through MPT_FALLTHROUGH; case 0: case 1: case 2: case 3: - m->volcmd = VOLCMD_VOLUME; - m->vol = m->param; - m->command = CMD_NONE; - m->param = 0; + m.volcmd = VOLCMD_VOLUME; + m.vol = m.param; + m.command = CMD_NONE; + m.param = 0; break; case 5: - m->param = (m->param & 0x0F) << 4; // Dx0 + m.param = (m.param & 0x0F) << 4; // Dx0 break; case 6: - m->param = 0xF0 | MIN(m->param & 0x0F, 0x0E); // DFx + m.param = 0xF0 | MIN(m.param & 0x0F, 0x0E); // DFx break; case 7: - m->param = (MIN(m->param & 0x0F, 0x0E) << 4) | 0x0F; // DxF + m.param = (MIN(m.param & 0x0F, 0x0E) << 4) | 0x0F; // DxF break; default: // Junk. - m->command = CMD_NONE; - m->param = 0; + m.command = CMD_NONE; + m.param = 0; break; } break; #if 0 case 24: // O Old Volume (???) - m->command = CMD_VOLUMESLIDE; - m->param = 0; + m.command = CMD_VOLUMESLIDE; + m.param = 0; break; #endif default: - m->command = m->param = 0; + m.command = m.param = 0; break; } } @@ -275,7 +270,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, co { return ProbeFailure; } - if((iffHead.signature & 0x7f7f7f7fu) != iffHead.signature) // ASCII? + if((iffHead.signature & 0x80808080u) != 0) // ASCII? { return ProbeFailure; } @@ -300,6 +295,10 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_OKT); + m_modFormat.formatName = U_("Oktalyzer"); + m_modFormat.type = U_("okt"); + m_modFormat.charset = mpt::CharsetISO8859_1; + // Go through IFF chunks... while(file.CanRead(sizeof(OktIffChunk))) { @@ -326,7 +325,7 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 4; chn++) { - uint8 ch1 = chunk.ReadUint8(), ch2 = chunk.ReadUint8(); + const uint8 ch1 = chunk.ReadUint8(), ch2 = chunk.ReadUint8(); if(ch1 || ch2) { ChnSettings[m_nChannels].Reset(); @@ -413,10 +412,7 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) Patterns.ResizeArray(static_cast(patternChunks.size())); for(PATTERNINDEX pat = 0; pat < patternChunks.size(); pat++) { - if(patternChunks[pat].GetLength() > 0) - ReadOKTPattern(patternChunks[pat], pat, *this); - else - Patterns.Insert(pat, 64); // Invent empty pattern + ReadOKTPattern(patternChunks[pat], pat, *this); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp index 963a0eb64..0eef50654 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp @@ -148,7 +148,11 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_PLM); InitializeChannels(); m_SongFlags = SONG_ITOLDEFFECTS; - m_madeWithTracker = MPT_USTRING("Disorder Tracker 2"); + + m_modFormat.formatName = U_("Disorder Tracker 2"); + m_modFormat.type = U_("plm"); + m_modFormat.charset = mpt::CharsetCP437; + // Some PLMs use ASCIIZ, some space-padding strings...weird. Oh, and the file browser stops at 0 bytes in the name, the main GUI doesn't. mpt::String::Read(m_songName, fileHeader.songName); m_nChannels = fileHeader.numChannels + 1; // Additional channel for writing pattern breaks @@ -263,7 +267,7 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) file.ReadStruct(patHeader); if(!patHeader.numRows) continue; - STATIC_ASSERT(ORDERINDEX_MAX >= (MPT_MAX_UNSIGNED_VALUE(ord.x) + 255) / rowsPerPat); + STATIC_ASSERT(ORDERINDEX_MAX >= ((mpt::limits::max)() + 255) / rowsPerPat); ORDERINDEX curOrd = static_cast(ord.x / rowsPerPat); ROWINDEX curRow = static_cast(ord.x % rowsPerPat); const CHANNELINDEX numChannels = std::min(patHeader.numChannels, fileHeader.numChannels - ord.y); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp index e284f7fdd..3f96802a7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp @@ -40,16 +40,16 @@ struct PSMChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idTITL = MAGIC4LE('T','I','T','L'), - idSDFT = MAGIC4LE('S','D','F','T'), - idPBOD = MAGIC4LE('P','B','O','D'), - idSONG = MAGIC4LE('S','O','N','G'), - idDATE = MAGIC4LE('D','A','T','E'), - idOPLH = MAGIC4LE('O','P','L','H'), - idPPAN = MAGIC4LE('P','P','A','N'), - idPATT = MAGIC4LE('P','A','T','T'), - idDSAM = MAGIC4LE('D','S','A','M'), - idDSMP = MAGIC4LE('D','S','M','P'), + idTITL = MagicLE("TITL"), + idSDFT = MagicLE("SDFT"), + idPBOD = MagicLE("PBOD"), + idSONG = MagicLE("SONG"), + idDATE = MagicLE("DATE"), + idOPLH = MagicLE("OPLH"), + idPPAN = MagicLE("PPAN"), + idPATT = MagicLE("PATT"), + idDSAM = MagicLE("DSAM"), + idDSMP = MagicLE("DSMP"), }; uint32le id; @@ -163,9 +163,9 @@ struct PSMSubSong // For internal use (pattern conversion) { std::vector channelPanning, channelVolume; std::vector channelSurround; - uint8 defaultTempo, defaultSpeed; + ORDERINDEX startOrder = ORDERINDEX_INVALID, endOrder = ORDERINDEX_INVALID, restartPos = 0; + uint8 defaultTempo = 125, defaultSpeed = 6; char songName[10]; - ORDERINDEX startOrder, endOrder, restartPos; PSMSubSong() { @@ -173,10 +173,6 @@ struct PSMSubSong // For internal use (pattern conversion) channelVolume.assign(MAX_BASECHANNELS, 64); channelSurround.assign(MAX_BASECHANNELS, false); MemsetZero(songName); - defaultTempo = 125; - defaultSpeed = 6; - startOrder = endOrder = ORDERINDEX_INVALID; - restartPos = 0; } }; @@ -211,12 +207,19 @@ static PATTERNINDEX ReadPSMPatternIndex(FileReader &file, bool &sinariaFormat) static bool ValidateHeader(const PSMFileHeader &fileHeader) { - if(std::memcmp(fileHeader.formatID, "PSM ", 4) - || std::memcmp(fileHeader.fileInfoID, "FILE", 4)) + if(!std::memcmp(fileHeader.formatID, "PSM ", 4) + && !std::memcmp(fileHeader.fileInfoID, "FILE", 4)) { - return false; + return true; } - return true; +#ifdef MPT_PSM_DECRYPT + if(!std::memcmp(fileHeader.formatID, "QUP$", 4) + && !std::memcmp(fileHeader.fileInfoID, "OSWQ", 4)) + { + return true; + } +#endif + return false; } @@ -631,7 +634,9 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) ChnSettings[chn].dwFlags.set(CHN_SURROUND, subsongs[0].channelSurround[chn]); } - m_madeWithTracker = sinariaFormat ? MPT_USTRING("Epic MegaGames MASI (New Version / Sinaria)") : MPT_USTRING("Epic MegaGames MASI (New Version)"); + m_modFormat.formatName = sinariaFormat ? U_("Epic MegaGames MASI (New Version / Sinaria)") : U_("Epic MegaGames MASI (New Version)"); + m_modFormat.type = U_("psm"); + m_modFormat.charset = mpt::CharsetCP437; if(!(loadFlags & loadPatternData) || m_nChannels == 0) { @@ -1122,7 +1127,11 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) // Seems to be valid! InitializeGlobals(MOD_TYPE_PSM); - m_madeWithTracker = MPT_USTRING("Epic MegaGames MASI (Old Version)"); + + m_modFormat.formatName = U_("Epic MegaGames MASI (Old Version)"); + m_modFormat.type = U_("psm"); + m_modFormat.charset = mpt::CharsetCP437; + m_nChannels = Clamp(CHANNELINDEX(fileHeader.numChannelsPlay), CHANNELINDEX(fileHeader.numChannelsReal), MAX_BASECHANNELS); m_nSamplePreAmp = fileHeader.masterVolume; if(m_nSamplePreAmp == 255) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp index 8af92eaa0..3e2d77602 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp @@ -170,7 +170,11 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) mpt::String::Read(m_songName, fileHeader.songname); - m_madeWithTracker = mpt::format(MPT_USTRING("PolyTracker %1.%2"))(fileHeader.versionHi.get(), mpt::ufmt::hex0<2>(fileHeader.versionLo.get())); + m_modFormat.formatName = U_("PolyTracker"); + m_modFormat.type = U_("ptm"); + m_modFormat.madeWithTracker = mpt::format(U_("PolyTracker %1.%2"))(fileHeader.versionHi.get(), mpt::ufmt::hex0<2>(fileHeader.versionLo.get())); + m_modFormat.charset = mpt::CharsetCP437; + m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = fileHeader.numChannels; m_nSamples = std::min(fileHeader.numSamples, MAX_SAMPLES - 1); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp index 07c329965..f5faa90b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "Loaders.h" #include "S3MTools.h" +#include "ITTools.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #ifdef MODPLUG_TRACKER @@ -61,6 +62,7 @@ void CSoundFile::S3MConvert(ModCommand &m, bool fromIT) } } +#ifndef MODPLUG_NO_FILESAVE void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool compatibilityExport) const { @@ -154,6 +156,7 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co command &= ~0x40; } +#endif // MODPLUG_NO_FILESAVE // Pattern decoding flags enum S3MPattern @@ -234,9 +237,11 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) // ST3 ignored Zxx commands, so if we find that a file was made with ST3, we should erase all MIDI macros. bool keepMidiMacros = false; - mpt::ustring trackerStr; + mpt::ustring madeWithTracker; + bool formatTrackerStr = false; bool nonCompatTracker = false; bool isST3 = false; + bool isSchism = false; switch(fileHeader.cwtv & S3MFileHeader::trackerMask) { case S3MFileHeader::trkScreamTracker: @@ -244,59 +249,77 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) { // MPT 1.16 and older versions of OpenMPT - Simply keep default (filter) MIDI macros m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker / OpenMPT"); + madeWithTracker = U_("ModPlug Tracker / OpenMPT"); keepMidiMacros = true; nonCompatTracker = true; m_playBehaviour.set(kST3LimitPeriod); } else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 0 && fileHeader.usePanningTable == 0) { - m_madeWithTracker = MPT_USTRING("Velvet Studio"); + madeWithTracker = U_("Velvet Studio"); } else { - trackerStr = MPT_USTRING("Scream Tracker"); + madeWithTracker = U_("Scream Tracker"); + formatTrackerStr = true; isST3 = true; } break; case S3MFileHeader::trkImagoOrpheus: - trackerStr = MPT_USTRING("Imago Orpheus"); + madeWithTracker = U_("Imago Orpheus"); + formatTrackerStr = true; nonCompatTracker = true; break; case S3MFileHeader::trkImpulseTracker: if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14) - trackerStr = MPT_USTRING("Impulse Tracker"); - else - m_madeWithTracker = mpt::format(MPT_USTRING("Impulse Tracker 2.14p%1"))(fileHeader.cwtv - S3MFileHeader::trkIT2_14); + { + madeWithTracker = U_("Impulse Tracker"); + formatTrackerStr = true; + } else + { + madeWithTracker = mpt::format(U_("Impulse Tracker 2.14p%1"))(fileHeader.cwtv - S3MFileHeader::trkIT2_14); + } nonCompatTracker = true; m_nMinPeriod = 1; break; case S3MFileHeader::trkSchismTracker: if(fileHeader.cwtv == S3MFileHeader::trkBeRoTrackerOld) { - m_madeWithTracker = MPT_USTRING("BeRoTracker"); + madeWithTracker = U_("BeRoTracker"); m_playBehaviour.set(kST3LimitPeriod); } else { - m_madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv); + madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv); m_nMinPeriod = 1; + isSchism = true; } nonCompatTracker = true; break; case S3MFileHeader::trkOpenMPT: - trackerStr = MPT_USTRING("OpenMPT"); - m_dwLastSavedWithVersion = (fileHeader.cwtv & S3MFileHeader::versionMask) << 16; + madeWithTracker = U_("OpenMPT"); + formatTrackerStr = true; + m_dwLastSavedWithVersion = Version((fileHeader.cwtv & S3MFileHeader::versionMask) << 16); break; case S3MFileHeader::trkBeRoTracker: - m_madeWithTracker = MPT_USTRING("BeRoTracker"); + madeWithTracker = U_("BeRoTracker"); m_playBehaviour.set(kST3LimitPeriod); break; case S3MFileHeader::trkCreamTracker: - m_madeWithTracker = MPT_USTRING("CreamTracker"); + madeWithTracker = U_("CreamTracker"); + break; + default: + if(fileHeader.cwtv == S3MFileHeader::trkCamoto) + madeWithTracker = U_("Camoto"); break; } - if(!trackerStr.empty()) + if(formatTrackerStr) { - m_madeWithTracker = mpt::format(MPT_USTRING("%1 %2.%3"))(trackerStr, (fileHeader.cwtv & 0xF00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF)); + madeWithTracker = mpt::format(U_("%1 %2.%3"))(madeWithTracker, (fileHeader.cwtv & 0xF00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF)); } + + m_modFormat.formatName = U_("ScreamTracker 3"); + m_modFormat.type = U_("s3m"); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::CharsetWindows1252 : mpt::CharsetCP437; + if(nonCompatTracker) { m_playBehaviour.reset(kST3NoMutedChannels); @@ -304,11 +327,11 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kST3PortaSampleChange); m_playBehaviour.reset(kST3VibratoMemory); m_playBehaviour.reset(KST3PortaAfterArpeggio); + m_playBehaviour.reset(kST3OffsetWithoutInstrument); } if((fileHeader.cwtv & S3MFileHeader::trackerMask) > S3MFileHeader::trkScreamTracker) { - // 2xyy - Imago Orpheus, 3xyy - IT, 4xyy - Schism, 5xyy - OpenMPT, 6xyy - BeRoTracker if((fileHeader.cwtv & S3MFileHeader::trackerMask) != S3MFileHeader::trkImpulseTracker || fileHeader.cwtv >= S3MFileHeader::trkIT2_14) { // Keep MIDI macros if this is not an old IT version (BABYLON.S3M by Necros has Zxx commands and was saved with IT 2.05) @@ -358,26 +381,36 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; } - // Bit 8 = Stereo (we always use stereo) + // Bit 7 = Stereo (we always use stereo) m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); + // Approximately as loud as in DOSBox and a real SoundBlaster 16 + m_nVSTiVolume = 36; + if(isSchism && fileHeader.cwtv < SchismVersionFromDate<2018, 11, 12>::Version(S3MFileHeader::trkSchismTracker)) + m_nVSTiVolume = 64; // Channel setup m_nChannels = 4; + std::bitset<32> isAdlibChannel; for(CHANNELINDEX i = 0; i < 32; i++) { ChnSettings[i].Reset(); + uint8 ctype = fileHeader.channels[i] & ~0x80; if(fileHeader.channels[i] != 0xFF) { m_nChannels = i + 1; - ChnSettings[i].nPan = (fileHeader.channels[i] & 8) ? 0xCC : 0x33; // 200 : 56 + ChnSettings[i].nPan = (ctype & 8) ? 0xCC : 0x33; // 200 : 56 } if(fileHeader.channels[i] & 0x80) { ChnSettings[i].dwFlags = CHN_MUTE; - // Detect Adlib channels here (except for OpenMPT 1.19 and older, which would write wrong channel types for PCM channels 16-32): - // c = channels[i] ^ 0x80; - // if(c >= 16 && c < 32) adlibChannel = true; + } + if(ctype >= 16 && ctype <= 29) + { + // Adlib channel - except for OpenMPT 1.19 and older, which would write wrong channel types for PCM channels 16-32. + // However, MPT/OpenMPT always wrote the extra panning table, so there is no need to consider this here. + ChnSettings[i].nPan = 128; + isAdlibChannel[i] = true; } } if(m_nChannels < 1) @@ -401,15 +434,13 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) file.ReadArray(pan); for(CHANNELINDEX i = 0; i < 32; i++) { - if((pan[i] & 0x20) != 0) + if((pan[i] & 0x20) != 0 && (!isST3 || !isAdlibChannel[i])) { ChnSettings[i].nPan = (static_cast(pan[i] & 0x0F) * 256 + 8) / 15; } } } - bool hasAdlibPatches = false; - // Reading sample headers m_nSamples = std::min(fileHeader.smpNum, MAX_SAMPLES - 1); for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++) @@ -424,25 +455,16 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) sampleHeader.ConvertToMPT(Samples[smp + 1]); mpt::String::Read(m_szNames[smp + 1], sampleHeader.name); - if(sampleHeader.sampleType >= S3MSampleHeader::typeAdMel) + if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) { - hasAdlibPatches = true; - } - - const uint32 sampleOffset = (sampleHeader.dataPointer[1] << 4) | (sampleHeader.dataPointer[2] << 12) | (sampleHeader.dataPointer[0] << 20); - - if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset)) - { - sampleHeader.GetSampleFormat((fileHeader.formatVersion == S3MFileHeader::oldVersion)).ReadSample(Samples[smp + 1], file); + const uint32 sampleOffset = (sampleHeader.dataPointer[1] << 4) | (sampleHeader.dataPointer[2] << 12) | (sampleHeader.dataPointer[0] << 20); + if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset)) + { + sampleHeader.GetSampleFormat((fileHeader.formatVersion == S3MFileHeader::oldVersion)).ReadSample(Samples[smp + 1], file); + } } } - if(hasAdlibPatches) - { - AddToLog("This track uses Adlib instruments, which are not supported by this version of OpenMPT."); - } - - // Try to find out if Zxx commands are supposed to be panning commands (PixPlay). // Actually I am only aware of one module that uses this panning style, namely "Crawling Despair" by $volkraq // and I have no idea what PixPlay is, so this code is solely based on the sample text of that module. @@ -587,7 +609,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) #ifndef MODPLUG_NO_FILESAVE -bool CSoundFile::SaveS3M(const mpt::PathString &filename) const +bool CSoundFile::SaveS3M(std::ostream &f) const { static const uint8 filler[16] = { @@ -595,9 +617,17 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }; - FILE *f; - if(m_nChannels == 0 || filename.empty()) return false; - if((f = mpt_fopen(filename, "wb")) == nullptr) return false; + if(m_nChannels == 0) + { + return false; + } + + const bool saveMuteStatus = +#ifdef MODPLUG_TRACKER + TrackerSettings::Instance().MiscSaveChannelMuteStatus; +#else + true; +#endif S3MFileHeader fileHeader; MemsetZero(fileHeader); @@ -621,7 +651,7 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const // Samples SAMPLEINDEX writeSamples = static_cast(GetNumInstruments()); - if(fileHeader.smpNum == 0) + if(writeSamples == 0) { writeSamples = GetNumSamples(); } @@ -641,56 +671,27 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const { fileHeader.flags |= S3MFileHeader::amigaLimits; } + if(m_SongFlags[SONG_S3MOLDVIBRATO]) + { + fileHeader.flags |= S3MFileHeader::st2Vibrato; + } // Version info following: ST3.20 = 0x1320 // Most significant nibble = Tracker ID, see S3MFileHeader::S3MTrackerVersions // Following: One nibble = Major version, one byte = Minor version (hex) - fileHeader.cwtv = S3MFileHeader::trkOpenMPT | static_cast((MptVersion::num >> 16) & S3MFileHeader::versionMask); + fileHeader.cwtv = S3MFileHeader::trkOpenMPT | static_cast((Version::Current().GetRawVersion() >> 16) & S3MFileHeader::versionMask); fileHeader.formatVersion = S3MFileHeader::newVersion; memcpy(fileHeader.magic, "SCRM", 4); // Song Variables - fileHeader.globalVol = static_cast(std::min(m_nDefaultGlobalVolume / 4u, 64u)); + fileHeader.globalVol = static_cast(std::min(m_nDefaultGlobalVolume / 4u, 64u)); fileHeader.speed = static_cast(Clamp(m_nDefaultSpeed, 1u, 254u)); fileHeader.tempo = static_cast(Clamp(m_nDefaultTempo.GetInt(), 33u, 255u)); fileHeader.masterVolume = static_cast(Clamp(m_nSamplePreAmp, 16u, 127u) | 0x80); fileHeader.ultraClicks = 8; fileHeader.usePanningTable = S3MFileHeader::idPanning; - // Channel Table - const uint8 midCh = static_cast(std::min(GetNumChannels() / 2, 8)); - for(CHANNELINDEX chn = 0; chn < 32; chn++) - { - if(chn < GetNumChannels()) - { - // ST3 only supports 16 PCM channels, so if channels 17-32 are used, - // they must be mapped to the same "internal channels" as channels 1-16. - // The channel indices determine in which order channels are evaluated in ST3. - // First, the "left" channels (0...7) are evaluated, then the "right" channels (8...15). - // Previously, an alternating LRLR scheme was written, which would lead to a different - // effect processing in ST3 than LLL...RRR, but since OpenMPT doesn't care about the - // channel order and always parses them left to right as they appear in the pattern, - // we should just write in the LLL...RRR manner. - uint8 ch = chn & 0x0F; - if(ch >= midCh) - { - ch += 8 - midCh; - } -#ifdef MODPLUG_TRACKER - if(TrackerSettings::Instance().MiscSaveChannelMuteStatus) -#endif - if(ChnSettings[chn].dwFlags[CHN_MUTE]) - { - ch |= 0x80; - } - fileHeader.channels[chn] = ch; - } else - { - fileHeader.channels[chn] = 0xFF; - } - } - - fwrite(&fileHeader, sizeof(fileHeader), 1, f); + mpt::IO::Write(f, fileHeader); Order().WriteAsByte(f, writeOrders); // Comment about parapointers stolen from Schism Tracker: @@ -702,7 +703,7 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const // The "practical standard order" listed in TECH.DOC is sample headers, patterns, then sample data. // Calculate offset of first sample header... - size_t sampleHeaderOffset = ftell(f) + (writeSamples + writePatterns) * 2 + 32; + mpt::IO::Offset sampleHeaderOffset = mpt::IO::TellWrite(f) + (writeSamples + writePatterns) * 2 + 32; // ...which must be a multiple of 16, because parapointers omit the lowest 4 bits. sampleHeaderOffset = (sampleHeaderOffset + 15) & ~15; @@ -712,45 +713,41 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const STATIC_ASSERT((sizeof(S3MSampleHeader) % 16) == 0); sampleOffsets[smp] = static_cast((sampleHeaderOffset + smp * sizeof(S3MSampleHeader)) / 16); } + mpt::IO::Write(f, sampleOffsets); - if(writeSamples != 0) - { - fwrite(sampleOffsets.data(), 2, writeSamples, f); - } - - size_t patternPointerOffset = ftell(f); - size_t firstPatternOffset = sampleHeaderOffset + writeSamples * sizeof(S3MSampleHeader); + mpt::IO::Offset patternPointerOffset = mpt::IO::TellWrite(f); + mpt::IO::Offset firstPatternOffset = sampleHeaderOffset + writeSamples * sizeof(S3MSampleHeader); std::vector patternOffsets(writePatterns); // Need to calculate the real offsets later. - if(writePatterns != 0) - { - fwrite(patternOffsets.data(), 2, writePatterns, f); - } + mpt::IO::Write(f, patternOffsets); // Write channel panning uint8 chnPan[32]; for(CHANNELINDEX chn = 0; chn < 32; chn++) { if(chn < GetNumChannels()) - chnPan[chn] = static_cast(((ChnSettings[chn].nPan * 15 + 128) / 256)) | 0x20; + chnPan[chn] = static_cast(((ChnSettings[chn].nPan * 15 + 128) / 256) | 0x20); else chnPan[chn] = 0x08; } - fwrite(chnPan, 32, 1, f); + mpt::IO::Write(f, chnPan); // Do we need to fill up the file with some padding bytes for 16-Byte alignment? - size_t curPos = ftell(f); + mpt::IO::Offset curPos = mpt::IO::TellWrite(f); if(curPos < sampleHeaderOffset) { MPT_ASSERT(sampleHeaderOffset - curPos < 16); - fwrite(filler, sampleHeaderOffset - curPos, 1, f); + mpt::IO::WriteRaw(f, filler, static_cast(sampleHeaderOffset - curPos)); } // Don't write sample headers for now, we are lacking the sample offset data. - fseek(f, firstPatternOffset, SEEK_SET); + mpt::IO::SeekAbsolute(f, firstPatternOffset); // Write patterns + enum class S3MChannelType : uint8 { kUnused = 0, kPCM = 1, kAdlib = 2 }; + FlagSet channelType[32] = { S3MChannelType::kUnused }; + bool globalCmdOnMutedChn = false; for(PATTERNINDEX pat = 0; pat < writePatterns; pat++) { if(Patterns.IsPatternEmpty(pat)) @@ -759,10 +756,10 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const continue; } - long patOffset = ftell(f); + mpt::IO::Offset patOffset = mpt::IO::TellWrite(f); if(patOffset > 0xFFFF0) { - AddToLog(LogError, mpt::format(MPT_USTRING("Too much pattern data! Writing patterns failed starting from pattern %1."))(pat)); + AddToLog(LogError, mpt::format(U_("Too much pattern data! Writing patterns failed starting from pattern %1."))(pat)); break; } MPT_ASSERT((patOffset % 16) == 0); @@ -786,7 +783,7 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const const PatternRow rowBase = Patterns[pat].GetRow(row); - CHANNELINDEX writeChannels = MIN(32, GetNumChannels()); + CHANNELINDEX writeChannels = std::min(CHANNELINDEX(32), GetNumChannels()); for(CHANNELINDEX chn = 0; chn < writeChannels; chn++) { const ModCommand &m = rowBase[chn]; @@ -819,6 +816,15 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const note -= (12 + NOTE_MIN); note = (note % 12) + ((note / 12) << 4); } + + if(m.instr > 0 && m.instr <= GetNumSamples()) + { + const ModSample &smp = Samples[m.instr]; + if(smp.uFlags[CHN_ADLIB]) + channelType[chn].set(S3MChannelType::kAdlib); + else if(smp.HasSampleData()) + channelType[chn].set(S3MChannelType::kPCM); + } } if(command == CMD_VOLUME) @@ -843,6 +849,10 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const if(command) { info |= s3mEffectPresent; + if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE] && m.IsGlobalCommand()) + { + globalCmdOnMutedChn = true; + } } } @@ -884,10 +894,14 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const buffer.insert(buffer.end(), 16 - (buffer.size() % 16u), 0); } - fwrite(buffer.data(), buffer.size(), 1, f); + mpt::IO::Write(f, buffer); + } + if(globalCmdOnMutedChn) + { + //AddToLog(LogWarning, U_("Global commands on muted channels are interpreted by only some S3M players.")); } - size_t sampleDataOffset = ftell(f); + mpt::IO::Offset sampleDataOffset = mpt::IO::TellWrite(f); // Write samples std::vector sampleHeader(writeSamples); @@ -898,11 +912,11 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const if(GetNumInstruments() != 0 && Instruments[smp] != nullptr) { // Find some valid sample associated with this instrument. - for(size_t i = 0; i < CountOf(Instruments[smp]->Keyboard); i++) + for(SAMPLEINDEX keySmp : Instruments[smp]->Keyboard) { - if(Instruments[smp]->Keyboard[i] > 0 && Instruments[smp]->Keyboard[i] <= GetNumSamples()) + if(keySmp > 0 && keySmp <= GetNumSamples()) { - realSmp = Instruments[smp]->Keyboard[i]; + realSmp = keySmp; break; } } @@ -913,16 +927,15 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const continue; } - SmpLength smpLength = sampleHeader[smp].ConvertToS3M(Samples[realSmp]); - + const SmpLength smpLength = sampleHeader[smp].ConvertToS3M(Samples[realSmp]); mpt::String::Write(sampleHeader[smp].name, m_szNames[realSmp]); - if(Samples[realSmp].pSample) + if(smpLength != 0) { // Write sample data if(sampleDataOffset > 0xFFFFFF0) { - AddToLog(LogError, mpt::format(MPT_USTRING("Too much sample data! Writing samples failed starting from sample %1."))(realSmp)); + AddToLog(LogError, mpt::format(U_("Too much sample data! Writing samples failed starting from sample %1."))(realSmp)); break; } @@ -935,27 +948,72 @@ bool CSoundFile::SaveS3M(const mpt::PathString &filename) const if((writtenLength % 16u) != 0) { size_t fillSize = 16 - (writtenLength % 16u); - fwrite(filler, fillSize, 1, f); + mpt::IO::WriteRaw(f, filler, fillSize); sampleDataOffset += fillSize; } } } + // Channel Table + uint8 sampleCh = 0, adlibCh = 0; + for(CHANNELINDEX chn = 0; chn < 32; chn++) + { + if(chn < GetNumChannels()) + { + if(channelType[chn][S3MChannelType::kPCM] && channelType[chn][S3MChannelType::kAdlib]) + { + AddToLog(LogWarning, mpt::format(U_("Pattern channel %1 constains both samples and OPL instruments, which is not supported by Scream Tracker 3."))(chn + 1)); + } + // ST3 only supports 16 PCM channels, so if channels 17-32 are used, + // they must be mapped to the same "internal channels" as channels 1-16. + // The channel indices determine in which order channels are evaluated in ST3. + // First, the "left" channels (0...7) are evaluated, then the "right" channels (8...15). + // Previously, an alternating LRLR scheme was written, which would lead to a different + // effect processing in ST3 than LLL...RRR, but since OpenMPT doesn't care about the + // channel order and always parses them left to right as they appear in the pattern, + // we should just write in the LLL...RRR manner. + uint8 ch = sampleCh % 16u; // If there are neither PCM nor AdLib instruments on this channel, just fall back a regular sample-based channel for maximum compatibility. + if(channelType[chn][S3MChannelType::kPCM]) + ch = (sampleCh++) % 16u; + else if(channelType[chn][S3MChannelType::kAdlib]) + ch = 16 + ((adlibCh++) % 9u); + + if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE]) + { + ch |= 0x80; + } + fileHeader.channels[chn] = ch; + } else + { + fileHeader.channels[chn] = 0xFF; + } + } + if(sampleCh > 16) + { + AddToLog(LogWarning, mpt::format(U_("This module has more than 16 (%1) sample channels, which is not supported by Scream Tracker 3."))(sampleCh)); + } + if(adlibCh > 9) + { + AddToLog(LogWarning, mpt::format(U_("This module has more than 9 (%1) OPL channels, which is not supported by Scream Tracker 3."))(adlibCh)); + } + + mpt::IO::SeekAbsolute(f, 0); + mpt::IO::Write(f, fileHeader); + // Now we know where the patterns are. if(writePatterns != 0) { - fseek(f, patternPointerOffset, SEEK_SET); - fwrite(patternOffsets.data(), 2, writePatterns, f); + mpt::IO::SeekAbsolute(f, patternPointerOffset); + mpt::IO::Write(f, patternOffsets); } // And we can finally write the sample headers. if(writeSamples != 0) { - fseek(f, sampleHeaderOffset, SEEK_SET); - fwrite(sampleHeader.data(), sizeof(sampleHeader[0]), writeSamples, f); + mpt::IO::SeekAbsolute(f, sampleHeaderOffset); + mpt::IO::Write(f, sampleHeader); } - fclose(f); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp index f0a643e46..7a4134dc0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp @@ -79,13 +79,13 @@ static uint8 ClampSlideParam(uint8 value, uint8 lowNote, uint8 highNote) uint16 lowPeriod, highPeriod; if(lowNote < highNote && - lowNote >= 36 + NOTE_MIN && - highNote >= 36 + NOTE_MIN && - lowNote < CountOf(ProTrackerPeriodTable) + 36 + NOTE_MIN && - highNote < CountOf(ProTrackerPeriodTable) + 36 + NOTE_MIN) + lowNote >= 24 + NOTE_MIN && + highNote >= 24 + NOTE_MIN && + lowNote < CountOf(ProTrackerPeriodTable) + 24 + NOTE_MIN && + highNote < CountOf(ProTrackerPeriodTable) + 24 + NOTE_MIN) { - lowPeriod = ProTrackerPeriodTable[lowNote - 36 - NOTE_MIN]; - highPeriod = ProTrackerPeriodTable[highNote - 36 - NOTE_MIN]; + lowPeriod = ProTrackerPeriodTable[lowNote - 24 - NOTE_MIN]; + highPeriod = ProTrackerPeriodTable[highNote - 24 - NOTE_MIN]; // with a fixed speed of 6 ticks/row, and excluding the first row, // 1xx/2xx param has a max value of (low-high)/5 to avoid sliding too far @@ -128,7 +128,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSFX(MemoryFileReader file, co { return ProbeWantMoreData; } - if(file.Seek(0x7c) && file.ReadMagic("SO31")) + if(file.Seek(0x7C) && file.ReadMagic("SO31")) { numSamples = 31; } @@ -208,7 +208,6 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) // Setup channel pan positions and volume SetupMODPanning(true); - m_playBehaviour.set(kMODIgnorePanning); file.Skip(4); uint16 speed = file.ReadUint16BE(); @@ -227,11 +226,11 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) sampleHeader.ConvertToMPT(Samples[smp], sampleLen[smp - 1]); // Get rid of weird characters in sample names. - for(uint32 i = 0; i < CountOf(sampleHeader.name); i++) + for(char &c : sampleHeader.name) { - if(sampleHeader.name[i] > 0 && sampleHeader.name[i] < ' ') + if(c > 0 && c < ' ') { - sampleHeader.name[i] = ' '; + c = ' '; invalidChars++; } } @@ -240,6 +239,11 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) mpt::String::Read(m_szNames[smp], sampleHeader.name); } + // Broken conversions of the "Operation Stealth" soundtrack (BOND23 / BOND32) + // There is a converter that shifts all note values except FFFD (empty note) to the left by 1 bit, + // but it should not do that for FFFE (STP) notes - as a consequence, they turn into pattern breaks (FFFC). + const bool fixPatternBreaks = !strcmp(m_szNames[1], "BASSE2.AMI") || !strcmp(m_szNames[1], "PRA1.AMI"); + SFXFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) { @@ -274,6 +278,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) uint8 lastNote[4] = {0}; uint8 slideTo[4] = {0}; uint8 slideRate[4] = {0}; + uint8 version = 0; // Reading patterns if(loadFlags & loadPatternData) @@ -299,17 +304,19 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) { lastNote[chn] = slideRate[chn] = 0; + if(fixPatternBreaks && data[1] == 0xFC) + data[1] = 0xFE; + switch(data[1]) { case 0xFE: // STP (note cut) m.command = CMD_VOLUME; continue; - case 0xFD: // PIC (null) continue; - case 0xFC: // BRK (pattern break) m.command = CMD_PATTERNBREAK; + version = 9; continue; } } @@ -319,17 +326,21 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) { lastNote[chn] = m.note; slideRate[chn] = 0; + if(m.note < NOTE_MIDDLEC - 12) + { + version = std::max(version, uint8(8)); + } } if(m.command || m.param) { switch(m.command) { - case 0x1: // arpeggio + case 0x1: // Arpeggio m.command = CMD_ARPEGGIO; break; - case 0x2: // portamento (like Ultimate Soundtracker) + case 0x2: // Portamento (like Ultimate Soundtracker) if(m.param & 0xF0) { m.command = CMD_PORTAMENTODOWN; @@ -344,8 +355,8 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) } break; - case 0x3: // enable filter/LED - // give precedence to 7xy/8xy slides + case 0x3: // Enable LED filter + // Give precedence to 7xy/8xy slides if(slideRate[chn]) { m.command = m.param = 0; @@ -355,8 +366,8 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) m.param = 0; break; - case 0x4: // disable filter/LED - // give precedence to 7xy/8xy slides + case 0x4: // Disable LED filter + // Give precedence to 7xy/8xy slides if(slideRate[chn]) { m.command = m.param = 0; @@ -366,13 +377,13 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) m.param = 1; break; - case 0x5: // increase volume + case 0x5: // Increase volume if(m.instr) { m.command = CMD_VOLUME; m.param = std::min(ModCommand::PARAM(0x3F), static_cast((Samples[m.instr].nVolume / 4u) + m.param)); - // give precedence to 7xy/8xy slides (and move this to the volume column) + // Give precedence to 7xy/8xy slides (and move this to the volume column) if(slideRate[chn]) { m.volcmd = VOLCMD_VOLUME; @@ -386,7 +397,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) } break; - case 0x6: // decrease volume + case 0x6: // Decrease volume if(m.instr) { m.command = CMD_VOLUME; @@ -395,7 +406,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) else m.param = 0; - // give precedence to 7xy/8xy slides (and move this to the volume column) + // Give precedence to 7xy/8xy slides (and move this to the volume column) if(slideRate[chn]) { m.volcmd = VOLCMD_VOLUME; @@ -409,7 +420,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) } break; - case 0x7: // 7xy: slide down x semitones at speed y + case 0x7: // 7xy: Slide down x semitones at speed y slideTo[chn] = lastNote[chn] - (m.param >> 4); m.command = CMD_PORTAMENTODOWN; @@ -417,7 +428,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) m.param = ClampSlideParam(slideRate[chn], slideTo[chn], lastNote[chn]); break; - case 0x8: // 8xy: slide up x semitones at speed y + case 0x8: // 8xy: Slide up x semitones at speed y slideTo[chn] = lastNote[chn] + (m.param >> 4); m.command = CMD_PORTAMENTOUP; @@ -425,13 +436,16 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) m.param = ClampSlideParam(slideRate[chn], lastNote[chn], slideTo[chn]); break; + case 0x9: // 9xy: Auto slide + version = std::max(version, uint8(8)); + MPT_FALLTHROUGH; default: m.command = CMD_NONE; break; } } - // continue 7xy/8xy slides if needed + // Continue 7xy/8xy slides if needed if(m.command == CMD_NONE && slideRate[chn]) { if(slideTo[chn]) @@ -460,6 +474,10 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) } } + m_modFormat.formatName = m_nSamples == 15 ? mpt::format(U_("SoundFX 1.%1"))(version) : U_("SoundFX 2.0 / MultiMedia Sound"); + m_modFormat.type = m_nSamples == 15 ? UL_("sfx") : UL_("sfx2"); + m_modFormat.charset = mpt::CharsetISO8859_1; + return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp index e7d872bc1..f5c5557ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp @@ -65,7 +65,7 @@ struct STMFileHeader uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :) uint8 verMajor; uint8 verMinor; - uint8 initTempo; // Ticks per row. Keep in mind that effects are only updated on every 16th tick. + uint8 initTempo; // Ticks per row. uint8 numPatterns; // number of patterns uint8 globalVolume; uint8 reserved[13]; @@ -76,24 +76,25 @@ MPT_BINARY_STRUCT(STMFileHeader, 48) static bool ValidateHeader(const STMFileHeader &fileHeader) { - // NOTE: Historically the magic byte check used to be case-insensitive. - // Other libraries (mikmod, xmp, Milkyplay) don't do this. - // ScreamTracker 2 and 3 do not care about the content of the magic bytes at all. - // After reviewing all STM files on ModLand and ModArchive, it was found that the - // case-insensitive comparison is most likely not necessary for any files in the wild. if(fileHeader.filetype != 2 || (fileHeader.dosEof != 0x1A && fileHeader.dosEof != 2) // ST2 ignores this, ST3 doesn't. Broken versions of putup10.stm / putup11.stm have dosEof = 2. || fileHeader.verMajor != 2 || (fileHeader.verMinor != 0 && fileHeader.verMinor != 10 && fileHeader.verMinor != 20 && fileHeader.verMinor != 21) || fileHeader.numPatterns > 64 - || (fileHeader.globalVolume > 64 && fileHeader.globalVolume != 0x58) // 0x58 may be a placeholder value in earlier ST2 versions. - || (std::memcmp(fileHeader.trackername, "!Scream!", 8) - && std::memcmp(fileHeader.trackername, "BMOD2STM", 8) - && std::memcmp(fileHeader.trackername, "WUZAMOD!", 8)) - ) + || (fileHeader.globalVolume > 64 && fileHeader.globalVolume != 0x58)) // 0x58 may be a placeholder value in earlier ST2 versions. { return false; } + // Tracker string can be anything really (ST2 and ST3 won't check it), + // but we do not want to generate too many false positives here, as + // STM already has very few magic bytes anyway. + // Magic bytes that have been found in the wild are !Scream!, BMOD2STM, WUZAMOD! and SWavePro. + for(uint8 c : fileHeader.trackername) + { + if(c < 0x20 || c >= 0x7F) + return false; + } + return true; } @@ -145,8 +146,11 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) mpt::String::Read(m_songName, fileHeader.songname); - // Read STM header - m_madeWithTracker = mpt::format(MPT_USTRING("Scream Tracker %1.%2"))(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor)); + m_modFormat.formatName = U_("Scream Tracker 2"); + m_modFormat.type = U_("stm"); + m_modFormat.madeWithTracker = mpt::format(U_("Scream Tracker %1.%2"))(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor)); + m_modFormat.charset = mpt::CharsetCP437; + m_nSamples = 31; m_nChannels = 4; m_nMinPeriod = 64; @@ -189,7 +193,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) { if(pat == 99 || pat == 255) // 99 is regular, sometimes a single 255 entry can be found too pat = Order.GetInvalidPatIndex(); - else if(pat > 99) + else if(pat > 63) return false; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp index 4389a4f35..afbf18c91 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp @@ -118,7 +118,7 @@ static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, S dest.FreeSample(); dest = src; dest.nLength = len; - dest.pSample = nullptr; + dest.pData.pSample = nullptr; if(!dest.AllocateSample()) { @@ -129,7 +129,7 @@ static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, S if(len != src.nLength) MemsetZero(dest.cues); - std::memcpy(dest.pSample8, src.pSample8 + start, len); + std::memcpy(dest.sampleb(), src.sampleb() + start, len); dest.uFlags.set(CHN_LOOP, loop); if(loop) { @@ -150,7 +150,7 @@ static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList) ModSample newSmp = smp; newSmp.nLength = 0; - newSmp.pSample = nullptr; + newSmp.pData.pSample = nullptr; size_t numLoops = loopList.size(); @@ -184,7 +184,7 @@ static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList) { STPLoopInfo &info = loopList[i]; - memcpy(newSmp.pSample8 + start, smp.pSample8 + info.loopStart, info.loopLength); + memcpy(newSmp.sampleb() + start, smp.sampleb() + info.loopStart, info.loopLength); // update loop info based on position in edited sample info.loopStart = start; @@ -256,6 +256,10 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_STP); + m_modFormat.formatName = mpt::format(U_("Soundtracker Pro II v%1"))(fileHeader.version); + m_modFormat.type = U_("stp"); + m_modFormat.charset = mpt::CharsetISO8859_1; + m_nChannels = 4; m_nSamples = 0; @@ -453,7 +457,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) uint16 ciaTempo = (static_cast(m.command & 0x0F) << 8) | m.param; if(ciaTempo) { - m.param = mpt::saturate_cast(Util::Round(ConvertTempo(ciaTempo).ToDouble())); + m.param = mpt::saturate_round(ConvertTempo(ciaTempo).ToDouble()); m.command = CMD_TEMPO; } else { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp index 46758c4ae..f46234955 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp @@ -16,16 +16,32 @@ OPENMPT_NAMESPACE_BEGIN +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderUAX(MemoryFileReader file, const uint64 *pfilesize) +{ + UMXFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if(!fileHeader.IsValid()) + { + return ProbeFailure; + } + if(!FindUMXNameTableEntryMemory(file, fileHeader, "sound")) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); UMXFileHeader fileHeader; if(!file.ReadStruct(fileHeader) - || memcmp(fileHeader.magic, "\xC1\x83\x2A\x9E", 4) - || fileHeader.nameCount == 0 - || fileHeader.exportCount == 0 - || fileHeader.importCount == 0 - ) + || !fileHeader.IsValid()) { return false; } @@ -72,7 +88,9 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags) // Now we can be pretty sure that we're doing the right thing. InitializeGlobals(); - m_madeWithTracker = mpt::format(MPT_USTRING("Unreal Package v%1"))(fileHeader.packageVersion); + m_modFormat.formatName = mpt::format(U_("Unreal Package v%1"))(fileHeader.packageVersion); + m_modFormat.type = U_("uax"); + m_modFormat.charset = mpt::CharsetWindows1252; for(uint32 i = 0; i < fileHeader.exportCount && file.CanRead(4); i++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp index 036fa82d3..c960cf3ce 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp @@ -377,7 +377,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, co } -bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags) +bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); @@ -402,9 +402,11 @@ bool CSoundFile::ReadUlt(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_ULT); mpt::String::Read(m_songName, fileHeader.songName); - const MPT_UCHAR_TYPE *versions[] = {MPT_ULITERAL("<1.4"), MPT_ULITERAL("1.4"), MPT_ULITERAL("1.5"), MPT_ULITERAL("1.6")}; - m_madeWithTracker = MPT_USTRING("UltraTracker "); - m_madeWithTracker += versions[fileHeader.version - '1']; + const MPT_UCHAR_TYPE *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; + m_modFormat.formatName = U_("UltraTracker"); + m_modFormat.type = U_("ult"); + m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1']; + m_modFormat.charset = mpt::CharsetCP437; m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp index 9e589fb8a..dfeac4f8c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp @@ -24,7 +24,7 @@ OPENMPT_NAMESPACE_BEGIN template -bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t channelIndex, size_t numChannels, SampleConversion conv = SampleConversion()) +static bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t channelIndex, size_t numChannels, SampleConversion conv = SampleConversion()) { MPT_ASSERT(sample.GetNumChannels() == 1); MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); @@ -37,12 +37,29 @@ bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t channelInd } const mpt::byte *inBuf = file.GetRawData(); - CopySample(reinterpret_cast(sample.pSample), sample.nLength, 1, inBuf + offset, file.BytesLeft() - offset, numChannels, conv); + CopySample(reinterpret_cast(sample.samplev()), sample.nLength, 1, inBuf + offset, file.BytesLeft() - offset, numChannels, conv); return true; } -bool CSoundFile::ReadWav(FileReader &file, ModLoadingFlags loadFlags) +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderWAV(MemoryFileReader file, const uint64 *pfilesize) +{ + RIFFHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return ProbeWantMoreData; + } + if((fileHeader.magic != RIFFHeader::idRIFF && fileHeader.magic != RIFFHeader::idLIST) + || (fileHeader.type != RIFFHeader::idWAVE && fileHeader.type != RIFFHeader::idwave)) + { + return ProbeFailure; + } + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags) { WAVReader wavFile(file); @@ -67,6 +84,10 @@ bool CSoundFile::ReadWav(FileReader &file, ModLoadingFlags loadFlags) { return false; } + + m_modFormat.formatName = U_("RIFF WAVE"); + m_modFormat.type = U_("wav"); + m_modFormat.charset = mpt::CharsetWindows1252; const SmpLength sampleLength = wavFile.GetSampleLength(); @@ -74,7 +95,7 @@ bool CSoundFile::ReadWav(FileReader &file, ModLoadingFlags loadFlags) // Calculate sample length in ticks at tempo 125 const uint32 sampleRate = std::max(uint32(1), wavFile.GetSampleRate()); const uint32 sampleTicks = mpt::saturate_cast(((sampleLength * 50) / sampleRate) + 1); - uint32 ticksPerRow = std::max((sampleTicks + 63u) / 63u, 1u); + uint32 ticksPerRow = std::max((sampleTicks + 63u) / 63u, 1u); Order().assign(1, 0); ORDERINDEX numOrders = 1; @@ -116,7 +137,7 @@ bool CSoundFile::ReadWav(FileReader &file, ModLoadingFlags loadFlags) sample.nLength = sampleLength; sample.nC5Speed = wavFile.GetSampleRate(); strcpy(m_szNames[channel + 1], ""); - wavFile.ApplySampleSettings(sample, m_szNames[channel + 1]); + wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[channel + 1]); if(wavFile.GetNumChannels() > 1) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp index b657d5a60..918969070 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp @@ -12,7 +12,6 @@ #include "stdafx.h" #include "Loaders.h" #include "../common/version.h" -#include "../common/misc_util.h" #include "XMTools.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" @@ -43,7 +42,7 @@ static std::vector AllocateXMSamples(CSoundFile &sndFile, SAMPLEIND // If too many sample slots are needed, try to fill some empty slots first. for(SAMPLEINDEX j = 1; j <= sndFile.GetNumSamples(); j++) { - if(sndFile.GetSample(j).pSample != nullptr) + if(sndFile.GetSample(j).HasSampleData()) { continue; } @@ -321,6 +320,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) m_nMixLevels = mixLevelsCompatible; FlagSet madeWith(verUnknown); + mpt::ustring madeWithTracker; if(!memcmp(fileHeader.trackerName, "FastTracker ", 12)) { @@ -341,14 +341,14 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { // ??? madeWith.set(verConfirmed); - m_madeWithTracker = MPT_USTRING("FastTracker Clone"); + madeWithTracker = U_("FastTracker Clone"); } } else { // Something else! madeWith = verUnknown | verConfirmed; - mpt::String::Read(m_madeWithTracker, mpt::CharsetCP437, fileHeader.trackerName); + mpt::String::Read(madeWithTracker, mpt::CharsetCP437, fileHeader.trackerName); if(!memcmp(fileHeader.trackerName, "OpenMPT ", 8)) { @@ -438,12 +438,12 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { // ModPlug Tracker Alpha m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker 1.0 alpha"); + madeWithTracker = U_("ModPlug Tracker 1.0 alpha"); } else if(instrHeader.size == 263) { // ModPlug Tracker Beta (Beta 1 still behaves like Alpha, but Beta 3.3 does it this way) m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, B3); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker 1.0 beta"); + madeWithTracker = U_("ModPlug Tracker 1.0 beta"); } else { // WTF? @@ -640,11 +640,11 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) if(madeWith[verModPlug1_09]) { m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker 1.09"); + madeWithTracker = U_("ModPlug Tracker 1.09"); } else if(madeWith[verNewModPlug]) { m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); - m_madeWithTracker = MPT_USTRING("ModPlug Tracker 1.10 - 1.16"); + madeWithTracker = U_("ModPlug Tracker 1.10 - 1.16"); } } @@ -652,7 +652,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { // Hey, I know this tracker! std::string mptVersion(fileHeader.trackerName + 8, 12); - m_dwLastSavedWithVersion = MptVersion::ToNum(mptVersion); + m_dwLastSavedWithVersion = Version::Parse(mpt::ToUnicode(mpt::CharsetASCII, mptVersion)); madeWith = verOpenMPT | verConfirmed; if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 19)) @@ -661,7 +661,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) m_nMixLevels = mixLevelsCompatibleFT2; } - if(m_dwLastSavedWithVersion != 0 && !madeWith[verOpenMPT]) + if(m_dwLastSavedWithVersion && !madeWith[verOpenMPT]) { m_nMixLevels = mixLevelsOriginal; m_playBehaviour.reset(); @@ -689,32 +689,29 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) } } - if(m_madeWithTracker.empty()) + if(madeWithTracker.empty()) { if(madeWith[verDigiTrakker] && sampleReserved == 0 && (instrType ? instrType : -1) == -1) { - m_madeWithTracker = MPT_USTRING("DigiTrakker"); + madeWithTracker = U_("DigiTrakker"); } else if(madeWith[verFT2Generic]) { - m_madeWithTracker = MPT_USTRING("FastTracker 2 or compatible"); + madeWithTracker = U_("FastTracker 2 or compatible"); } else { - m_madeWithTracker = MPT_USTRING("Unknown"); + madeWithTracker = U_("Unknown"); } } - // Leave if no extra instrument settings are available (end of file reached) - if(file.NoBytesLeft()) return true; - - bool interpretOpenMPTMade = false; // specific for OpenMPT 1.17+ (bMadeWithModPlug is also for MPT 1.16) + bool isOpenMPTMade = false; // specific for OpenMPT 1.17+ if(GetNumInstruments()) { - LoadExtendedInstrumentProperties(file, &interpretOpenMPTMade); + isOpenMPTMade = LoadExtendedInstrumentProperties(file); } - LoadExtendedSongProperties(file, true, &interpretOpenMPTMade); + LoadExtendedSongProperties(file, true, &isOpenMPTMade); - if(interpretOpenMPTMade && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(isOpenMPTMade && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00)) { // Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0 // when saving a file in OpenMPT for the first time. @@ -723,7 +720,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00)) { - m_madeWithTracker = MPT_USTRING("OpenMPT ") + MptVersion::ToUString(m_dwLastSavedWithVersion); + madeWithTracker = U_("OpenMPT ") + m_dwLastSavedWithVersion.ToUString(); } // We no longer allow any --- or +++ items in the order list now. @@ -735,6 +732,11 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) Order().Replace(0xFF, Order.GetInvalidPatIndex()); } + m_modFormat.formatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); + m_modFormat.type = U_("xm"); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::CharsetWindows1252 : mpt::CharsetCP437; + return true; } @@ -745,17 +747,8 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) #define str_pattern ("pattern") -bool CSoundFile::SaveXM(const mpt::PathString &filename, bool compatibilityExport) +bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) { - if(filename.empty()) - { - return false; - } - FILE *f = mpt_fopen(filename, "wb"); - if(!f) - { - return false; - } bool addChannel = false; // avoid odd channel count for FT2 compatibility @@ -765,7 +758,7 @@ bool CSoundFile::SaveXM(const mpt::PathString &filename, bool compatibilityExpor memcpy(fileHeader.signature, "Extended Module: ", 17); mpt::String::Write(fileHeader.songName, m_songName); fileHeader.eof = 0x1A; - const std::string openMptTrackerName = MptVersion::GetOpenMPTVersionStr(); + const std::string openMptTrackerName = mpt::ToCharset(GetCharsetFile(), Version::Current().GetOpenMPTVersionString()); mpt::String::Write(fileHeader.trackerName, openMptTrackerName); // Writing song header @@ -1125,7 +1118,7 @@ bool CSoundFile::SaveXM(const mpt::PathString &filename, bool compatibilityExpor } //Save hacked-on extra info - SaveMixPlugins(f); + SaveMixPlugins(&f); if(GetNumInstruments()) { SaveExtendedInstrumentProperties(writeInstruments, f); @@ -1133,7 +1126,6 @@ bool CSoundFile::SaveXM(const mpt::PathString &filename, bool compatibilityExpor SaveExtendedSongProperties(f); } - fclose(f); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h index 8b68a5ee5..ad3724a2c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/misc_util.h" #include "../common/FileReader.h" #include "Sndfile.h" @@ -16,18 +18,34 @@ OPENMPT_NAMESPACE_BEGIN -// Macros to create magic bytes in little-endian format -#define MAGIC4LE(a, b, c, d) static_cast((static_cast(d) << 24) | (static_cast(c) << 16) | (static_cast(b) << 8) | static_cast(a)) -#define MAGIC2LE(a, b) static_cast((static_cast(b) << 8) | static_cast(a)) -// Macros to create magic bytes in big-endian format -#define MAGIC4BE(a, b, c, d) static_cast((static_cast(a) << 24) | (static_cast(b) << 16) | (static_cast(c) << 8) | static_cast(d)) -#define MAGIC2BE(a, b) static_cast((static_cast(a) << 8) | static_cast(b)) +// Functions to create 4-byte and 2-byte magic byte identifiers in little-endian format +// Use this together with uint32le/uint16le file members. +constexpr uint32 MagicLE(const char(&id)[5]) +{ + return static_cast((static_cast(id[3]) << 24) | (static_cast(id[2]) << 16) | (static_cast(id[1]) << 8) | static_cast(id[0])); +} +constexpr uint16 MagicLE(const char(&id)[3]) +{ + return static_cast((static_cast(id[1]) << 8) | static_cast(id[0])); +} +// Functions to create 4-byte and 2-byte magic byte identifiers in big-endian format +// Use this together with uint32be/uint16be file members. +// Note: Historically, some magic bytes in MPT-specific fields are reversed (due to the use of multi-char literals). +// Such fields turned up reversed in files, so MagicBE is used to keep them readable in the code. +constexpr uint32 MagicBE(const char(&id)[5]) +{ + return static_cast((static_cast(id[0]) << 24) | (static_cast(id[1]) << 16) | (static_cast(id[2]) << 8) | static_cast(id[3])); +} +constexpr uint16 MagicBE(const char(&id)[3]) +{ + return static_cast((static_cast(id[0]) << 8) | static_cast(id[1])); +} // Read 'howMany' order items from an array. -// 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass ORDERINDEX_INVALID. +// 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass uint16_max. template -bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t howMany = arraySize, uint16 stopIndex = ORDERINDEX_INVALID, uint16 ignoreIndex = ORDERINDEX_INVALID) +bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t howMany = arraySize, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { STATIC_ASSERT(mpt::is_binary_safe::value); LimitMax(howMany, arraySize); @@ -47,9 +65,9 @@ bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t // Read 'howMany' order items as integers with defined endianness from a file. -// 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass ORDERINDEX_INVALID. +// 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass uint16_max. template -bool ReadOrderFromFile(ModSequence &order, FileReader &file, size_t howMany, uint16 stopIndex = ORDERINDEX_INVALID, uint16 ignoreIndex = ORDERINDEX_INVALID) +bool ReadOrderFromFile(ModSequence &order, FileReader &file, size_t howMany, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { STATIC_ASSERT(mpt::is_binary_safe::value); if(!file.CanRead(howMany * sizeof(T))) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIEvents.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIEvents.h index 90fe4441c..f00b0d56e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIEvents.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIEvents.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp index a022811a3..a19eb46ce 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp @@ -11,7 +11,7 @@ #include "stdafx.h" #include "../soundlib/MIDIEvents.h" #include "MIDIMacros.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" #include "../common/misc_util.h" #ifdef MODPLUG_TRACKER @@ -21,40 +21,40 @@ OPENMPT_NAMESPACE_BEGIN -parameteredMacroType MIDIMacroConfig::GetParameteredMacroType(uint32 macroIndex) const +ParameteredMacro MIDIMacroConfig::GetParameteredMacroType(uint32 macroIndex) const { const std::string macro = GetSafeMacro(szMidiSFXExt[macroIndex]); - for(uint32 i = 0; i < sfx_max; i++) + for(uint32 i = 0; i < kSFxMax; i++) { - parameteredMacroType sfx = static_cast(i); - if(sfx != sfx_custom) + ParameteredMacro sfx = static_cast(i); + if(sfx != kSFxCustom) { if(macro.compare(CreateParameteredMacro(sfx)) == 0) return sfx; } } // Special macros with additional "parameter": - if (macro.compare(CreateParameteredMacro(sfx_cc, MIDIEvents::MIDICC_start)) >= 0 && macro.compare(CreateParameteredMacro(sfx_cc, MIDIEvents::MIDICC_end)) <= 0 && macro.size() == 5) - return sfx_cc; - if (macro.compare(CreateParameteredMacro(sfx_plug, 0)) >= 0 && macro.compare(CreateParameteredMacro(sfx_plug, 0x17F)) <= 0 && macro.size() == 7) - return sfx_plug; + if (macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_start)) >= 0 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_end)) <= 0 && macro.size() == 5) + return kSFxCC; + if (macro.compare(CreateParameteredMacro(kSFxPlugParam, 0)) >= 0 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0x17F)) <= 0 && macro.size() == 7) + return kSFxPlugParam; - return sfx_custom; // custom / unknown + return kSFxCustom; // custom / unknown } // Retrieve Zxx (Z80-ZFF) type from current macro configuration -fixedMacroType MIDIMacroConfig::GetFixedMacroType() const +FixedMacro MIDIMacroConfig::GetFixedMacroType() const { // Compare with all possible preset patterns - for(uint32 i = 0; i < zxx_max; i++) + for(uint32 i = 0; i < kZxxMax; i++) { - fixedMacroType zxx = static_cast(i); - if(zxx != zxx_custom) + FixedMacro zxx = static_cast(i); + if(zxx != kZxxCustom) { // Prepare macro pattern to compare - char macros[128][MACRO_LENGTH]; + Macro macros[128]; CreateFixedMacro(macros, zxx); bool found = true; @@ -69,45 +69,26 @@ fixedMacroType MIDIMacroConfig::GetFixedMacroType() const if(found) return zxx; } } - return zxx_custom; // Custom setup + return kZxxCustom; // Custom setup } -void MIDIMacroConfig::CreateParameteredMacro(char (¶meteredMacro)[MACRO_LENGTH], parameteredMacroType macroType, int subType) const +void MIDIMacroConfig::CreateParameteredMacro(Macro ¶meteredMacro, ParameteredMacro macroType, int subType) const { switch(macroType) { - case sfx_unused: - strcpy(parameteredMacro, ""); - break; - case sfx_cutoff: - strcpy(parameteredMacro, "F0F000z"); - break; - case sfx_reso: - strcpy(parameteredMacro, "F0F001z"); - break; - case sfx_mode: - strcpy(parameteredMacro, "F0F002z"); - break; - case sfx_drywet: - strcpy(parameteredMacro, "F0F003z"); - break; - case sfx_cc: - sprintf(parameteredMacro, "Bc%02Xz", (subType & 0x7F)); - break; - case sfx_plug: - sprintf(parameteredMacro, "F0F%03Xz", std::min(subType, 0x17F) + 0x80); - break; - case sfx_channelAT: - strcpy(parameteredMacro, "Dcz"); - break; - case sfx_polyAT: - strcpy(parameteredMacro, "Acnz"); - break; - case sfx_pitch: - strcpy(parameteredMacro, "Ec00z"); - break; - case sfx_custom: + case kSFxUnused: mpt::String::WriteAutoBuf(parameteredMacro) = ""; break; + case kSFxCutoff: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F000z"; break; + case kSFxReso: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F001z"; break; + case kSFxFltMode: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F002z"; break; + case kSFxDryWet: mpt::String::WriteAutoBuf(parameteredMacro) = "F0F003z"; break; + case kSFxCC: mpt::String::WriteAutoBuf(parameteredMacro) = mpt::format("Bc%1z")(mpt::fmt::HEX0<2>(subType & 0x7F)); break; + case kSFxPlugParam: mpt::String::WriteAutoBuf(parameteredMacro) = mpt::format("F0F%1z")(mpt::fmt::HEX0<3>(std::min(subType, 0x17F) + 0x80)); break; + case kSFxChannelAT: mpt::String::WriteAutoBuf(parameteredMacro) = "Dcz"; break; + case kSFxPolyAT: mpt::String::WriteAutoBuf(parameteredMacro) = "Acnz"; break; + case kSFxPitch: mpt::String::WriteAutoBuf(parameteredMacro) = "Ec00z"; break; + case kSFxProgChange: mpt::String::WriteAutoBuf(parameteredMacro) = "Ccz"; break; + case kSFxCustom: default: MPT_ASSERT_NOTREACHED(); break; @@ -115,65 +96,47 @@ void MIDIMacroConfig::CreateParameteredMacro(char (¶meteredMacro)[MACRO_LENG } -// Create Zxx (Z80 - ZFF) from one out of five presets -void MIDIMacroConfig::CreateFixedMacro(char (&fixedMacros)[128][MACRO_LENGTH], fixedMacroType macroType) const +// Create Zxx (Z80 - ZFF) from preset +void MIDIMacroConfig::CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const { for(uint32 i = 0; i < 128; i++) { + const char *str = ""; + uint32 param = i; switch(macroType) { - case zxx_unused: - strcpy(fixedMacros[i], ""); + case kZxxUnused: str = ""; break; + case kZxxReso4Bit: + param = i * 8; + if(i < 16) + str = "F0F001%1"; + else + str = ""; break; - - case zxx_reso4Bit: - // Type 1 - Z80 - Z8F controls resonance - if (i < 16) sprintf(fixedMacros[i], "F0F001%02X", i * 8); - else strcpy(fixedMacros[i], ""); + case kZxxReso7Bit: str = "F0F001%1"; break; + case kZxxCutoff: str = "F0F000%1"; break; + case kZxxFltMode: str = "F0F002%1"; break; + case kZxxResoFltMode: + param = (i & 0x0F) * 8; + if(i < 16) + str = "F0F001%1"; + else if(i < 32) + str = "F0F002%1"; + else + str = ""; break; + case kZxxChannelAT: str = "Dc%1"; break; + case kZxxPolyAT: str = "Acn%1"; break; + case kZxxPitch: str = "Ec00%1"; break; + case kZxxProgChange: str = "Cc%1"; break; - case zxx_reso7Bit: - // Type 2 - Z80 - ZFF controls resonance - sprintf(fixedMacros[i], "F0F001%02X", i); - break; - - case zxx_cutoff: - // Type 3 - Z80 - ZFF controls cutoff - sprintf(fixedMacros[i], "F0F000%02X", i); - break; - - case zxx_mode: - // Type 4 - Z80 - ZFF controls filter mode - sprintf(fixedMacros[i], "F0F002%02X", i); - break; - - case zxx_resomode: - // Type 5 - Z80 - Z9F controls resonance + filter mode - if (i < 16) sprintf(fixedMacros[i], "F0F001%02X", i * 8); - else if (i < 32) sprintf(fixedMacros[i], "F0F002%02X", (i - 16) * 8); - else strcpy(fixedMacros[i], ""); - break; - - case zxx_channelAT: - // Type 6 - Z80 - ZFF controls Channel Aftertouch - sprintf(fixedMacros[i], "Dc%02X", i); - break; - - case zxx_polyAT: - // Type 7 - Z80 - ZFF controls Poly Aftertouch - sprintf(fixedMacros[i], "Acn%02X", i); - break; - - case zxx_pitch: - // Type 7 - Z80 - ZFF controls Pitch Bend - sprintf(fixedMacros[i], "Ec00%02X", i); - break; - - case zxx_custom: + case kZxxCustom: default: MPT_ASSERT_NOTREACHED(); - break; + continue; } + + mpt::String::WriteAutoBuf(fixedMacros[i]) = mpt::format(str)(mpt::fmt::HEX0<2>(param)); } } @@ -182,19 +145,9 @@ void MIDIMacroConfig::CreateFixedMacro(char (&fixedMacros)[128][MACRO_LENGTH], f bool MIDIMacroConfig::operator== (const MIDIMacroConfig &other) const { - for(uint32 i = 0; i < CountOf(szMidiGlb); i++) + for(auto left = begin(), right = other.begin(); left != end(); left++, right++) { - if(strncmp(szMidiGlb[i], other.szMidiGlb[i], MACRO_LENGTH)) - return false; - } - for(uint32 i = 0; i < CountOf(szMidiSFXExt); i++) - { - if(strncmp(szMidiSFXExt[i], other.szMidiSFXExt[i], MACRO_LENGTH)) - return false; - } - for(uint32 i = 0; i < CountOf(szMidiZXXExt); i++) - { - if(strncmp(szMidiZXXExt[i], other.szMidiZXXExt[i], MACRO_LENGTH)) + if(strncmp(*left, *right, MACRO_LENGTH)) return false; } return true; @@ -204,15 +157,15 @@ bool MIDIMacroConfig::operator== (const MIDIMacroConfig &other) const // Returns macro description including plugin parameter / MIDI CC information CString MIDIMacroConfig::GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin) const { - const parameteredMacroType macroType = GetParameteredMacroType(macroIndex); + const ParameteredMacro macroType = GetParameteredMacroType(macroIndex); switch(macroType) { - case sfx_plug: + case kSFxPlugParam: { const int param = MacroToPlugParam(macroIndex); CString formattedName; - formattedName.Format(_T("Param %u"), param); + formattedName.Format(_T("Param %d"), param); #ifndef NO_PLUGINS if(plugin != nullptr) { @@ -231,10 +184,10 @@ CString MIDIMacroConfig::GetParameteredMacroName(uint32 macroIndex, IMixPlugin * return formattedName; } - case sfx_cc: + case kSFxCC: { CString formattedCC; - formattedCC.Format(_T("MIDI CC %u"), MacroToMidiCC(macroIndex)); + formattedCC.Format(_T("MIDI CC %d"), MacroToMidiCC(macroIndex)); return formattedCC; } @@ -245,63 +198,44 @@ CString MIDIMacroConfig::GetParameteredMacroName(uint32 macroIndex, IMixPlugin * // Returns generic macro description. -CString MIDIMacroConfig::GetParameteredMacroName(parameteredMacroType macroType) const +CString MIDIMacroConfig::GetParameteredMacroName(ParameteredMacro macroType) const { switch(macroType) { - case sfx_unused: - return _T("Unused"); - case sfx_cutoff: - return _T("Set Filter Cutoff"); - case sfx_reso: - return _T("Set Filter Resonance"); - case sfx_mode: - return _T("Set Filter Mode"); - case sfx_drywet: - return _T("Set Plugin Dry/Wet Ratio"); - case sfx_plug: - return _T("Control Plugin Parameter..."); - case sfx_cc: - return _T("MIDI CC..."); - case sfx_channelAT: - return _T("Channel Aftertouch"); - case sfx_polyAT: - return _T("Polyphonic Aftertouch"); - case sfx_pitch: - return _T("Pitch Bend"); - case sfx_custom: - default: - return _T("Custom"); + case kSFxUnused: return _T("Unused"); + case kSFxCutoff: return _T("Set Filter Cutoff"); + case kSFxReso: return _T("Set Filter Resonance"); + case kSFxFltMode: return _T("Set Filter Mode"); + case kSFxDryWet: return _T("Set Plugin Dry/Wet Ratio"); + case kSFxPlugParam: return _T("Control Plugin Parameter..."); + case kSFxCC: return _T("MIDI CC..."); + case kSFxChannelAT: return _T("Channel Aftertouch"); + case kSFxPolyAT: return _T("Polyphonic Aftertouch"); + case kSFxPitch: return _T("Pitch Bend"); + case kSFxProgChange: return _T("MIDI Program Change"); + case kSFxCustom: + default: return _T("Custom"); } } // Returns generic macro description. -CString MIDIMacroConfig::GetFixedMacroName(fixedMacroType macroType) const +CString MIDIMacroConfig::GetFixedMacroName(FixedMacro macroType) const { switch(macroType) { - case zxx_unused: - return _T("Unused"); - case zxx_reso4Bit: - return _T("Z80 - Z8F controls Resonant Filter Resonance"); - case zxx_reso7Bit: - return _T("Z80 - ZFF controls Resonant Filter Resonance"); - case zxx_cutoff: - return _T("Z80 - ZFF controls Resonant Filter Cutoff"); - case zxx_mode: - return _T("Z80 - ZFF controls Resonant Filter Mode"); - case zxx_resomode: - return _T("Z80 - Z9F controls Resonance + Filter Mode"); - case zxx_channelAT: - return _T("Z80 - ZFF controls Channel Aftertouch"); - case zxx_polyAT: - return _T("Z80 - ZFF controls Polyphonic Aftertouch"); - case zxx_pitch: - return _T("Z80 - ZFF controls Pitch Bend"); - case zxx_custom: - default: - return _T("Custom"); + case kZxxUnused: return _T("Unused"); + case kZxxReso4Bit: return _T("Z80 - Z8F controls Resonant Filter Resonance"); + case kZxxReso7Bit: return _T("Z80 - ZFF controls Resonant Filter Resonance"); + case kZxxCutoff: return _T("Z80 - ZFF controls Resonant Filter Cutoff"); + case kZxxFltMode: return _T("Z80 - ZFF controls Resonant Filter Mode"); + case kZxxResoFltMode: return _T("Z80 - Z9F controls Resonance + Filter Mode"); + case kZxxChannelAT: return _T("Z80 - ZFF controls Channel Aftertouch"); + case kZxxPolyAT: return _T("Z80 - ZFF controls Polyphonic Aftertouch"); + case kZxxPitch: return _T("Z80 - ZFF controls Pitch Bend"); + case kZxxProgChange: return _T("Z80 - ZFF controls MIDI Program Change"); + case kZxxCustom: + default: return _T("Custom"); } } @@ -345,12 +279,11 @@ int MIDIMacroConfig::FindMacroForParam(PlugParamIndex param) const { for(int macroIndex = 0; macroIndex < NUM_MACROS; macroIndex++) { - if(GetParameteredMacroType(macroIndex) == sfx_plug && MacroToPlugParam(macroIndex) == param) + if(GetParameteredMacroType(macroIndex) == kSFxPlugParam && MacroToPlugParam(macroIndex) == param) { return macroIndex; } } - return -1; } @@ -397,9 +330,9 @@ void MIDIMacroConfig::Reset() strcpy(szMidiGlb[MIDIOUT_NOTEOFF], "9c n 0"); strcpy(szMidiGlb[MIDIOUT_PROGRAM], "Cc p"); // SF0: Z00-Z7F controls cutoff - CreateParameteredMacro(0, sfx_cutoff); + CreateParameteredMacro(0, kSFxCutoff); // Z80-Z8F controls resonance - CreateFixedMacro(zxx_reso4Bit); + CreateFixedMacro(kZxxReso4Bit); } @@ -414,35 +347,27 @@ void MIDIMacroConfig::ClearZxxMacros() // Sanitize all macro config strings. void MIDIMacroConfig::Sanitize() { - for(uint32 i = 0; i < CountOf(szMidiGlb); i++) + for(auto ¯o : *this) { - mpt::String::FixNullString(szMidiGlb[i]); - } - for(uint32 i = 0; i < CountOf(szMidiSFXExt); i++) - { - mpt::String::FixNullString(szMidiSFXExt[i]); - } - for(uint32 i = 0; i < CountOf(szMidiZXXExt); i++) - { - mpt::String::FixNullString(szMidiZXXExt[i]); + mpt::String::FixNullString(macro); } } // Helper function for UpgradeMacros() -void MIDIMacroConfig::UpgradeMacroString(char *macro) const +void MIDIMacroConfig::UpgradeMacroString(Macro ¯o) const { - for(uint32 i = 0; i < MACRO_LENGTH; i++) + for(auto &c : macro) { - if(macro[i] >= 'a' && macro[i] <= 'f') // both A-F and a-f were treated as hex constants + if(c >= 'a' && c <= 'f') // Both A-F and a-f were treated as hex constants { - macro[i] = macro[i] - 'a' + 'A'; - } else if(macro[i] == 'K' || macro[i] == 'k') // channel was K or k + c = c - 'a' + 'A'; + } else if(c == 'K' || c == 'k') // Channel was K or k { - macro[i] = 'c'; - } else if(macro[i] == 'X' || macro[i] == 'x' || macro[i] == 'Y' || macro[i] == 'y') // those were pointless + c = 'c'; + } else if(c == 'X' || c == 'x' || c == 'Y' || c == 'y') // Those were pointless { - macro[i] = 'z'; + c = 'z'; } } } @@ -451,24 +376,20 @@ void MIDIMacroConfig::UpgradeMacroString(char *macro) const // Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings. void MIDIMacroConfig::UpgradeMacros() { - for(uint32 i = 0; i < CountOf(szMidiSFXExt); i++) + for(auto ¯o : *this) { - UpgradeMacroString(szMidiSFXExt[i]); - } - for(uint32 i = 0; i < CountOf(szMidiZXXExt); i++) - { - UpgradeMacroString(szMidiZXXExt[i]); + UpgradeMacroString(macro); } } // Normalize by removing blanks and other unwanted characters from macro strings for internal usage. -std::string MIDIMacroConfig::GetSafeMacro(const char *macro) const +std::string MIDIMacroConfig::GetSafeMacro(const Macro ¯o) const { std::string sanitizedMacro = macro; std::string::size_type pos; - while((pos = sanitizedMacro.find_first_not_of("0123456789ABCDEFabpcnuvxyz")) != std::string::npos) + while((pos = sanitizedMacro.find_first_not_of("0123456789ABCDEFabchmnopsuvxyz")) != std::string::npos) { sanitizedMacro.erase(pos, 1); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h index 6af3c4c13..3681bd458 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN enum @@ -27,39 +29,41 @@ OPENMPT_NAMESPACE_END OPENMPT_NAMESPACE_BEGIN // Parametered macro presets -enum parameteredMacroType +enum ParameteredMacro { - sfx_unused = 0, - sfx_cutoff, // Type 1 - Z00 - Z7F controls resonant filter cutoff - sfx_reso, // Type 2 - Z00 - Z7F controls resonant filter resonance - sfx_mode, // Type 3 - Z00 - Z7F controls resonant filter mode (lowpass / highpass) - sfx_drywet, // Type 4 - Z00 - Z7F controls plugin Dry / Wet ratio - sfx_plug, // Type 5 - Z00 - Z7F controls a plugin parameter - sfx_cc, // Type 6 - Z00 - Z7F controls MIDI CC - sfx_channelAT, // Type 7 - Z00 - Z7F controls Channel Aftertouch - sfx_polyAT, // Type 8 - Z00 - Z7F controls Poly Aftertouch - sfx_pitch, // Type 9 - Z00 - Z7F controls Pitch Bend - sfx_custom, + kSFxUnused = 0, + kSFxCutoff, // Z00 - Z7F controls resonant filter cutoff + kSFxReso, // Z00 - Z7F controls resonant filter resonance + kSFxFltMode, // Z00 - Z7F controls resonant filter mode (lowpass / highpass) + kSFxDryWet, // Z00 - Z7F controls plugin Dry / Wet ratio + kSFxPlugParam, // Z00 - Z7F controls a plugin parameter + kSFxCC, // Z00 - Z7F controls MIDI CC + kSFxChannelAT, // Z00 - Z7F controls Channel Aftertouch + kSFxPolyAT, // Z00 - Z7F controls Poly Aftertouch + kSFxPitch, // Z00 - Z7F controls Pitch Bend + kSFxProgChange, // Z00 - Z7F controls MIDI Program Change + kSFxCustom, - sfx_max + kSFxMax }; // Fixed macro presets -enum fixedMacroType +enum FixedMacro { - zxx_unused = 0, - zxx_reso4Bit, // Type 1 - Z80 - Z8F controls resonant filter resonance - zxx_reso7Bit, // Type 2 - Z80 - ZFF controls resonant filter resonance - zxx_cutoff, // Type 3 - Z80 - ZFF controls resonant filter cutoff - zxx_mode, // Type 4 - Z80 - ZFF controls resonant filter mode (lowpass / highpass) - zxx_resomode, // Type 5 - Z80 - Z9F controls resonance + filter mode - zxx_channelAT, // Type 6 - Z80 - ZFF controls Channel Aftertouch - zxx_polyAT, // Type 7 - Z80 - ZFF controls Poly Aftertouch - zxx_pitch, // Type 8 - Z80 - ZFF controls Pitch Bend - zxx_custom, + kZxxUnused = 0, + kZxxReso4Bit, // Z80 - Z8F controls resonant filter resonance + kZxxReso7Bit, // Z80 - ZFF controls resonant filter resonance + kZxxCutoff, // Z80 - ZFF controls resonant filter cutoff + kZxxFltMode, // Z80 - ZFF controls resonant filter mode (lowpass / highpass) + kZxxResoFltMode, // Z80 - Z9F controls resonance + filter mode + kZxxChannelAT, // Z80 - ZFF controls Channel Aftertouch + kZxxPolyAT, // Z80 - ZFF controls Poly Aftertouch + kZxxPitch, // Z80 - ZFF controls Pitch Bend + kZxxProgChange, // Z80 - ZFF controls MIDI Program Change + kZxxCustom, - zxx_max + kZxxMax }; @@ -80,10 +84,16 @@ enum struct MIDIMacroConfigData { + typedef char Macro[MACRO_LENGTH]; // encoding is ASCII - char szMidiGlb[9][MACRO_LENGTH]; // Global MIDI macros - char szMidiSFXExt[16][MACRO_LENGTH]; // Parametric MIDI macros - char szMidiZXXExt[128][MACRO_LENGTH]; // Fixed MIDI macros + Macro szMidiGlb[9]; // Global MIDI macros + Macro szMidiSFXExt[16]; // Parametric MIDI macros + Macro szMidiZXXExt[128]; // Fixed MIDI macros + + Macro *begin() { return std::begin(szMidiGlb); } + const Macro *begin() const { return std::begin(szMidiGlb); } + Macro *end() { return std::end(szMidiZXXExt); } + const Macro *end() const { return std::end(szMidiZXXExt); } }; MPT_BINARY_STRUCT(MIDIMacroConfigData, 4896) // this is directly written to files, so the size must be correct! @@ -93,34 +103,34 @@ class MIDIMacroConfig : public MIDIMacroConfigData public: - MIDIMacroConfig() { Reset(); }; + MIDIMacroConfig() { Reset(); } // Get macro type from a macro string - parameteredMacroType GetParameteredMacroType(uint32 macroIndex) const; - fixedMacroType GetFixedMacroType() const; + ParameteredMacro GetParameteredMacroType(uint32 macroIndex) const; + FixedMacro GetFixedMacroType() const; // Create a new macro protected: - void CreateParameteredMacro(char (¶meteredMacro)[MACRO_LENGTH], parameteredMacroType macroType, int subType) const; + void CreateParameteredMacro(Macro ¶meteredMacro, ParameteredMacro macroType, int subType) const; public: - void CreateParameteredMacro(uint32 macroIndex, parameteredMacroType macroType, int subType = 0) + void CreateParameteredMacro(uint32 macroIndex, ParameteredMacro macroType, int subType = 0) { CreateParameteredMacro(szMidiSFXExt[macroIndex], macroType, subType); - }; - std::string CreateParameteredMacro(parameteredMacroType macroType, int subType = 0) const + } + std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const { - char parameteredMacro[MACRO_LENGTH]; + Macro parameteredMacro; CreateParameteredMacro(parameteredMacro, macroType, subType); return std::string(parameteredMacro); - }; + } protected: - void CreateFixedMacro(char (&fixedMacros)[128][MACRO_LENGTH], fixedMacroType macroType) const; + void CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const; public: - void CreateFixedMacro(fixedMacroType macroType) + void CreateFixedMacro(FixedMacro macroType) { CreateFixedMacro(szMidiZXXExt, macroType); - }; + } #ifdef MODPLUG_TRACKER @@ -129,8 +139,8 @@ public: // Translate macro type or macro string to macro name CString GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin = nullptr) const; - CString GetParameteredMacroName(parameteredMacroType macroType) const; - CString GetFixedMacroName(fixedMacroType macroType) const; + CString GetParameteredMacroName(ParameteredMacro macroType) const; + CString GetFixedMacroName(FixedMacro macroType) const; // Extract information from a parametered macro string. int MacroToPlugParam(uint32 macroIndex) const; @@ -159,10 +169,10 @@ public: protected: // Helper function for FixMacroFormat() - void UpgradeMacroString(char *macro) const; + void UpgradeMacroString(Macro ¯o) const; // Remove blanks and other unwanted characters from macro strings for internal usage. - std::string GetSafeMacro(const char *macro) const; + std::string GetSafeMacro(const Macro ¯o) const; }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.h index e4d41b400..d4b7fe978 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/FileReaderFwd.h" OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h index 5cd945b00..cd21343cf 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include "../common/FileReaderFwd.h" diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp index 52388f436..347dc7c48 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp @@ -79,9 +79,9 @@ ResamplingIndex ResamplingModeToMixFlags(ResamplingMode resamplingMode) { case SRCMODE_NEAREST: return ndxNoInterpolation; case SRCMODE_LINEAR: return ndxLinear; - case SRCMODE_SPLINE: return ndxFastSinc; - case SRCMODE_POLYPHASE: return ndxKaiser; - case SRCMODE_FIRFILTER: return ndxFIRFilter; + case SRCMODE_CUBIC: return ndxFastSinc; + case SRCMODE_SINC8LP: return ndxKaiser; + case SRCMODE_SINC8: return ndxFIRFilter; case SRCMODE_AMIGA: return ndxAmigaBlep; default: MPT_ASSERT_NOTREACHED(); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h index 27fbb4a01..fd90ec43f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "MixerInterface.h" OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h index ef5c5b5b2..94885abc2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN #define MPT_INTMIXER @@ -22,6 +24,7 @@ typedef float mixsample_t; #endif #define MIXBUFFERSIZE 512 +#define NUMMIXINPUTBUFFERS 4 #define VOLUMERAMPPRECISION 12 // Fractional bits in volume ramp variables diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h index c70cbad4f..09a9f7001 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerInterface.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "Snd_defs.h" #include "ModChannel.h" @@ -23,8 +25,8 @@ class CResampler; template struct MixerTraits { - static const int numChannelsIn = channelsIn; // Number of channels in sample - static const int numChannelsOut = channelsOut; // Number of mixer output channels + enum : int { numChannelsIn = channelsIn }; // Number of channels in sample + enum : int { numChannelsOut = channelsOut }; // Number of mixer output channels typedef out output_t; // Output buffer sample type typedef in input_t; // Input buffer sample type typedef out outbuf_t[channelsOut]; // Output buffer sampling point type @@ -44,7 +46,7 @@ struct NoInterpolation MPT_FORCEINLINE void operator() (typename Traits::outbuf_t &outSample, const typename Traits::input_t * const inBuffer, const int32) { - static_assert(Traits::numChannelsIn <= Traits::numChannelsOut, "Too many input channels"); + static_assert(static_cast(Traits::numChannelsIn) <= static_cast(Traits::numChannelsOut), "Too many input channels"); for(int i = 0; i < Traits::numChannelsIn; i++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp index e4bc4bb00..f56df384f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp @@ -2,14 +2,8 @@ * MixerLoops.cpp * -------------- * Purpose: Utility inner loops for mixer-related functionality. - * Notes : - * x86 ( AMD/INTEL ) based low level based mixing functions: - * This file contains critical code. The basic X86 functions are - * defined at the bottom of the file. #define's are used to isolate - * the different flavours of functionality: - * ENABLE_MMX, ENABLE_3DNOW, ENABLE_SSE flags must be set to - * to compile the optimized sections of the code. In both cases the - * X86_xxxxxx functions will compile. + * Notes : This file contains performance-critical loops with variants + * optimized for various instruction sets. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -17,15 +11,16 @@ #include "stdafx.h" - #include "MixerLoops.h" - -#include "Sndfile.h" +#include "Snd_defs.h" +#include "ModChannel.h" +#ifdef ENABLE_SSE2 +#include +#endif OPENMPT_NAMESPACE_BEGIN - //////////////////////////////////////////////////////////////////////////////////// // 3DNow! optimizations @@ -162,8 +157,6 @@ mainloop: #ifdef ENABLE_SSE2 -#include - static void SSE2_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) { __m128 i2fc = _mm_load_ps1(&_i2fc); @@ -218,7 +211,7 @@ static void SSE2_FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *p #endif // ENABLE_SSE2 -#ifdef ENABLE_SSE +#if defined(ENABLE_X86) && defined(ENABLE_SSE) static void SSE_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) { @@ -275,7 +268,7 @@ mainloop: } } -#endif // ENABLE_SSE +#endif // ENABLE_X86 && ENABLE_SSE @@ -429,13 +422,13 @@ void StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCou return; } #endif // ENABLE_SSE2 - #ifdef ENABLE_SSE + #if defined(ENABLE_X86) && defined(ENABLE_SSE) if(GetProcSupport() & PROCSUPPORT_SSE) { SSE_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); return; } - #endif // ENABLE_SSE + #endif // ENABLE_X86 && ENABLE_SSE #ifdef ENABLE_X86_AMD if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) { @@ -482,13 +475,13 @@ void FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _i2fc) { - #ifdef ENABLE_SSE + #if defined(ENABLE_X86) && defined(ENABLE_SSE) if(GetProcSupport() & PROCSUPPORT_SSE) { SSE_MonoMixToFloat(pSrc, pOut, nCount, _i2fc); return; } - #endif // ENABLE_SSE + #endif // ENABLE_X86 && ENABLE_SSE #ifdef ENABLE_X86_AMD if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) { @@ -506,7 +499,7 @@ void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _ } -void FloatToMonoMix(const float *pIn, int *pOut, uint32 nCount, const float _f2ic) +void FloatToMonoMix(const float *pIn, int32 *pOut, uint32 nCount, const float _f2ic) { #ifdef ENABLE_X86_AMD @@ -733,8 +726,8 @@ static void C_StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rof #endif rofs -= x_r; lofs -= x_l; - pBuffer[i*2] = x_r; - pBuffer[i*2+1] = x_l; + pBuffer[i*2] = rofs; + pBuffer[i*2+1] = lofs; } #ifndef MPT_INTMIXER @@ -821,8 +814,8 @@ static void C_EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSampl #endif rofs -= x_r; lofs -= x_l; - pBuffer[i*2] += x_r; - pBuffer[i*2+1] += x_l; + pBuffer[i*2] += rofs; + pBuffer[i*2+1] += lofs; } #ifndef MPT_INTMIXER if(mpt::abs(rofs) < OFSTHRESHOLD) rofs = 0; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h index cc138e475..27be04a99 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h @@ -10,16 +10,14 @@ #pragma once +#include "BuildSettings.h" #include "Mixer.h" - OPENMPT_NAMESPACE_BEGIN - struct ModChannel; - void StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc); void FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 uint32, const float _f2ic); void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 uint32, const float _i2fc); @@ -40,5 +38,4 @@ void DeinterleaveStereo(const mixsample_t *input, mixsample_t *outputL, mixsampl void EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSamples); void StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rofs, mixsample_t &lofs); - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp index d8eb14dad..f9615de4b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp @@ -33,6 +33,8 @@ MixerSettings::MixerSettings() VolumeRampUpMicroseconds = 363; // 16 @44100 VolumeRampDownMicroseconds = 952; // 42 @44100 + NumInputChannels = 0; + } int32 MixerSettings::GetVolumeRampUpSamples() const diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.h index 9b1e6de6e..2de146940 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN @@ -17,7 +19,7 @@ struct MixerSettings { int32 m_nStereoSeparation; - static const int32 StereoSeparationScale = 128; + enum : int32 { StereoSeparationScale = 128 }; uint32 m_nMaxMixChannels; uint32 DSPMask; @@ -25,6 +27,7 @@ struct MixerSettings uint32 gdwMixingFreq; uint32 gnChannels; uint32 m_nPreAmp; + std::size_t NumInputChannels; int32 VolumeRampUpMicroseconds; int32 VolumeRampDownMicroseconds; @@ -41,7 +44,7 @@ struct MixerSettings bool IsValid() const { - return (gdwMixingFreq > 0) && (gnChannels == 1 || gnChannels == 2 || gnChannels == 4); + return (gdwMixingFreq > 0) && (gnChannels == 1 || gnChannels == 2 || gnChannels == 4) && (NumInputChannels == 0 || NumInputChannels == 1 || NumInputChannels == 2 || NumInputChannels == 4); } MixerSettings(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp index f2ccb843b..2aeed18c5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp @@ -37,9 +37,11 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI } nTremorCount = 0; nEFxSpeed = 0; - proTrackerOffset = 0; + prevNoteOffset = 0; lastZxxParam = 0xFF; isFirstTick = false; + isPreviewNote = false; + rowCommand.Clear(); } if(resetMask & resetSetPosAdvanced) @@ -63,13 +65,12 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI nOldHiOffset = 0; nLeftVU = nRightVU = 0; - //-->Custom tuning related + // Custom tuning related m_ReCalculateFreqOnFirstTick = false; m_CalculateFreq = false; m_PortamentoFineSteps = 0; m_PortamentoTickSlide = 0; m_Freq = 0; - //<--Custom tuning related. } if(resetMask & resetChannelSettings) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h index e86f90fdc..88c6df521 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "ModSample.h" #include "ModInstrument.h" #include "modcommand.h" @@ -62,7 +64,7 @@ struct ModChannel // Information not used in the mixer const ModInstrument *pModInstrument; // Currently assigned instrument slot - SmpLength proTrackerOffset; // Offset for instrument-less notes in ProTracker mode + SmpLength prevNoteOffset; // Offset for instrument-less notes for ProTracker/ScreamTracker SmpLength oldOffset; FlagSet dwOldFlags; // Flags from previous tick int32 newLeftVol, newRightVol; @@ -72,8 +74,8 @@ struct ModChannel int32 cachedPeriod, glissandoPeriod; int32 nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros EnvInfo VolEnv, PanEnv, PitchEnv; // Envelope playback info - int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) - int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) + int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) 0...64 + int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64 int32 nFineTune, nTranspose; int32 nPortamentoSlide, nAutoVibDepth; uint32 nEFxOffset; // offset memory for Invert Loop (EFx, .MOD only) @@ -88,7 +90,8 @@ struct ModChannel uint8 resamplingMode; uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote uint8 nRestoreCutoffOnNewNote; // ditto - uint8 nNote, nNNA; + uint8 nNote; + NewNoteAction nNNA; uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros uint8 nArpeggioLastNote, nArpeggioBaseNote; // For plugin arpeggio uint8 nNewNote, nNewIns, nOldIns, nCommand, nArpeggio; @@ -112,6 +115,7 @@ struct ModChannel uint8 nNoteSlideCounter, nNoteSlideSpeed, nNoteSlideStep; // IMF / PTM Note Slide uint8 lastZxxParam; // Memory for \xx slides bool isFirstTick : 1; + bool isPreviewNote : 1; //-->Variables used to make user-definable tuning modes work with pattern effects. //If true, freq should be recalculated in ReadNote() on first tick. @@ -125,14 +129,13 @@ struct ModChannel int32 m_PortamentoFineSteps, m_PortamentoTickSlide; uint32 m_Freq; - //<---- //NOTE_PCs memory. float m_plugParamValueStep, m_plugParamTargetValue; uint16 m_RowPlugParam; PLUGINDEX m_RowPlug; - void ClearRowCmd() { rowCommand = ModCommand::Empty(); } + void ClearRowCmd() { rowCommand = ModCommand(); } // Get a reference to a specific envelope of this channel const EnvInfo &GetEnvelope(EnvelopeType envType) const diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp index b388d569e..d5eb6e667 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp @@ -110,7 +110,7 @@ int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int } } - Limit(value, 0, ENV_PRECISION); + Limit(value, int32(0), ENV_PRECISION); return (value * rangeOut + ENV_PRECISION / 2) / ENV_PRECISION; } @@ -318,7 +318,7 @@ void ModInstrument::Sanitize(MODTYPE modType) MPT_UNREFERENCED_PARAMETER(modType); const uint8 range = ENVELOPE_MAX; #else - const uint8 range = modType == MOD_TYPE_AMS2 ? uint8_max : ENVELOPE_MAX; + const uint8 range = modType == MOD_TYPE_AMS ? uint8_max : ENVELOPE_MAX; #endif VolEnv.Sanitize(); PanEnv.Sanitize(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h index be95a00be..8fc1bae40 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "tuningbase.h" #include "Snd_defs.h" #include "../common/FlagSet.h" @@ -24,10 +26,10 @@ struct EnvelopeNode typedef uint16 tick_t; typedef uint8 value_t; - tick_t tick; // Envelope node position (x axis) - value_t value; // Envelope node value (y axis) + tick_t tick = 0; // Envelope node position (x axis) + value_t value = 0; // Envelope node value (y axis) - EnvelopeNode() : tick(0), value(0) { } + EnvelopeNode() { } EnvelopeNode(tick_t tick, value_t value) : tick(tick), value(value) { } bool operator== (const EnvelopeNode &other) const { return tick == other.tick && value == other.value; } @@ -36,19 +38,12 @@ struct EnvelopeNode // Instrument Envelopes struct InstrumentEnvelope : public std::vector { - FlagSet dwFlags; // Envelope flags - uint8 nLoopStart; // Loop start node - uint8 nLoopEnd; // Loop end node - uint8 nSustainStart; // Sustain start node - uint8 nSustainEnd; // Sustain end node - uint8 nReleaseNode; // Release node - - InstrumentEnvelope() - { - nLoopStart = nLoopEnd = 0; - nSustainStart = nSustainEnd = 0; - nReleaseNode = ENV_RELEASE_NODE_UNSET; - } + FlagSet dwFlags; // Envelope flags + uint8 nLoopStart = 0; // Loop start node + uint8 nLoopEnd = 0; // Loop end node + uint8 nSustainStart = 0; // Sustain start node + uint8 nSustainEnd = 0; // Sustain end node + uint8 nReleaseNode = ENV_RELEASE_NODE_UNSET; // Release node // Convert envelope data between various formats. void Convert(MODTYPE fromType, MODTYPE toType); @@ -69,7 +64,6 @@ struct InstrumentEnvelope : public std::vector // Instrument Struct struct ModInstrument { - FlagSet dwFlags; // Instrument flags uint32 nFadeOut; // Instrument fadeout speed uint32 nGlobalVol; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) uint32 nPan; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. @@ -82,9 +76,10 @@ struct ModInstrument uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader) int8 midiPWD; // MIDI Pitch Wheel Depth in semitones - uint8 nNNA; // New note action (NNA_* constants) - uint8 nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action, DCT_* constants) - uint8 nDNA; // Duplicate note action (DNA_* constants) + FlagSet dwFlags; // Instrument flags + NewNoteAction nNNA; // New note action + DuplicateCheckType nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action) + DuplicateNoteAction nDNA; // Duplicate note action uint8 nPanSwing; // Random panning factor (0...64) uint8 nVolSwing; // Random volume factor (0...100) uint8 nIFC; // Default filter cutoff (0...127). Used if the high bit is set @@ -96,7 +91,7 @@ struct ModInstrument PLUGINDEX nMixPlug; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) uint8 nCutSwing; // Random cutoff factor (0...64) uint8 nResSwing; // Random resonance factor (0...64) - uint8 nFilterMode; // Default filter mode (FLTMODE_* constants) + InstrFilterMode nFilterMode; // Default filter mode uint8 nPluginVelocityHandling; // How to deal with plugin velocity (PLUGIN_VELOCITYHANDLING_* constants) uint8 nPluginVolumeHandling; // How to deal with plugin volume (PLUGIN_VOLUMEHANDLING_* constants) TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp index 016f5e422..73a54ae85 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp @@ -108,6 +108,12 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType) { uFlags.reset(SMP_KEEPONDISK); } + + // No Adlib instruments in formats that can't handle it. + if(!CSoundFile::SupportsOPL(toType) && uFlags[CHN_ADLIB]) + { + SetAdlib(false); + } } @@ -121,7 +127,7 @@ void ModSample::Initialize(MODTYPE type) nPan = 128; nVolume = 256; nGlobalVol = 64; - uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | SMP_MODIFIED | SMP_KEEPONDISK); + uFlags.reset(CHN_PANNING | CHN_SUSTAINLOOP | CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_ADLIB | SMP_MODIFIED | SMP_KEEPONDISK); if(type == MOD_TYPE_XM) { uFlags.set(CHN_PANNING); @@ -135,11 +141,7 @@ void ModSample::Initialize(MODTYPE type) rootNote = 0; filename[0] = '\0'; - // Default cues compatible with old-style volume column offset - for(int i = 0; i < 9; i++) - { - cues[i] = (i + 1) << 11; - } + SetDefaultCuePoints(); } @@ -164,7 +166,7 @@ size_t ModSample::AllocateSample() { FreeSample(); - if((pSample = AllocateSample(nLength, GetBytesPerSample())) == nullptr) + if((pData.pSample = AllocateSample(nLength, GetBytesPerSample())) == nullptr) { return 0; } else @@ -174,11 +176,11 @@ size_t ModSample::AllocateSample() } -// Allocate sample memory. On sucess, a pointer to the silenced sample buffer is returned. On failure, nullptr is returned. -// numSamples must contain the sample length, bytesPerSample the size of a sampling point multiplied with the number of channels. -void *ModSample::AllocateSample(SmpLength numSamples, size_t bytesPerSample) +// Allocate sample memory. On success, a pointer to the silenced sample buffer is returned. On failure, nullptr is returned. +// numFrames must contain the sample length, bytesPerSample the size of a sampling point multiplied with the number of channels. +void *ModSample::AllocateSample(SmpLength numFrames, size_t bytesPerSample) { - const size_t allocSize = GetRealSampleBufferSize(numSamples, bytesPerSample); + const size_t allocSize = GetRealSampleBufferSize(numFrames, bytesPerSample); if(allocSize != 0) { @@ -223,8 +225,8 @@ size_t ModSample::GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerS void ModSample::FreeSample() { - FreeSample(pSample); - pSample = nullptr; + FreeSample(pData.pSample); + pData.pSample = nullptr; } @@ -304,7 +306,7 @@ void ModSample::SanitizeLoops() uint32 ModSample::TransposeToFrequency(int transpose, int finetune) { - return Util::Round(std::pow(2.0, (transpose * 128.0 + finetune) * (1.0 / (12.0 * 128.0))) * 8363.0); + return mpt::saturate_round(std::pow(2.0, (transpose * 128.0 + finetune) * (1.0 / (12.0 * 128.0))) * 8363.0); } @@ -317,46 +319,65 @@ void ModSample::TransposeToFrequency() // Return tranpose.finetune as 25.7 fixed point value. int ModSample::FrequencyToTranspose(uint32 freq) { - return Util::Round(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2))); + return mpt::saturate_round(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2))); } void ModSample::FrequencyToTranspose() { - int f2t; + int f2t = 0; if(nC5Speed) f2t = FrequencyToTranspose(nC5Speed); - else - f2t = 0; - int transpose = f2t >> 7; - int finetune = f2t & 0x7F; //0x7F == 111 1111 - if(finetune > 80) // XXX Why is this 80? - { - transpose++; - finetune -= 128; - } - Limit(transpose, -127, 128); - RelativeTone = static_cast(transpose); - nFineTune = static_cast(finetune); + RelativeTone = static_cast(f2t >> 7); + nFineTune = static_cast(f2t & 0x7F); } // Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up) void ModSample::Transpose(double amount) { - nC5Speed = Util::Round(nC5Speed * std::pow(2.0, amount)); + nC5Speed = mpt::saturate_round(nC5Speed * std::pow(2.0, amount)); } // Check if the sample's cue points are the default cue point set. bool ModSample::HasCustomCuePoints() const { - for(SmpLength i = 0; i < CountOf(cues); i++) + if(!uFlags[CHN_ADLIB]) { - if(cues[i] != (i + 1) << 11) return true; + for(SmpLength i = 0; i < CountOf(cues); i++) + { + if(cues[i] != (i + 1) << 11) return true; + } } return false; } +void ModSample::SetDefaultCuePoints() +{ + // Default cues compatible with old-style volume column offset + for(int i = 0; i < 9; i++) + { + cues[i] = (i + 1) << 11; + } +} + +void ModSample::SetAdlib(bool enable, OPLPatch patch) +{ + if(!enable && uFlags[CHN_ADLIB]) + { + SetDefaultCuePoints(); + } + uFlags.set(CHN_ADLIB, enable); + if(enable) + { + // Bogus sample to make playback work + uFlags.reset(CHN_16BIT | CHN_STEREO); + nLength = 4; + AllocateSample(); + adlib = patch; + } +} + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h index 6b24e5550..924e8c52b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN class CSoundFile; @@ -25,45 +27,90 @@ struct ModSample void *pSample; // Pointer to sample data int8 *pSample8; // Pointer to 8-bit sample data int16 *pSample16; // Pointer to 16-bit sample data - }; + } pData; uint32 nC5Speed; // Frequency of middle-C, in Hz (for IT/S3M/MPTM) uint16 nPan; // Default sample panning (if pan flag is set), 0...256 uint16 nVolume; // Default volume, 0...256 (ignored if uFlags[SMP_NODEFAULTVOLUME] is set) uint16 nGlobalVol; // Global volume (sample volume is multiplied by this), 0...64 SampleFlags uFlags; // Sample flags (see ChannelFlags enum) int8 RelativeTone; // Relative note to middle c (for MOD/XM) - int8 nFineTune; // Finetune period (for MOD/XM), -128...127 - uint8 nVibType; // Auto vibrato type, see VibratoType enum - uint8 nVibSweep; // Auto vibrato sweep (i.e. how long it takes until the vibrato effect reaches its full strength) + int8 nFineTune; // Finetune period (for MOD/XM), -128...127, unit is 1/128th of a semitone + VibratoType nVibType; // Auto vibrato type + uint8 nVibSweep; // Auto vibrato sweep (i.e. how long it takes until the vibrato effect reaches its full depth) uint8 nVibDepth; // Auto vibrato depth uint8 nVibRate; // Auto vibrato rate (speed) uint8 rootNote; // For multisample import //char name[MAX_SAMPLENAME]; // Maybe it would be nicer to have sample names here, but that would require some refactoring. - char filename [MAX_SAMPLEFILENAME]; - SmpLength cues[9]; + char filename[MAX_SAMPLEFILENAME]; + union + { + SmpLength cues[9]; + OPLPatch adlib; + }; ModSample(MODTYPE type = MOD_TYPE_NONE) { - pSample = nullptr; + pData.pSample = nullptr; Initialize(type); } - bool HasSampleData() const { return pSample != nullptr && nLength != 0; } + bool HasSampleData() const noexcept + { + MPT_ASSERT(!pData.pSample || (pData.pSample && nLength > 0)); // having sample pointer implies non-zero sample length + return pData.pSample != nullptr && nLength != 0; + } + + MPT_FORCEINLINE const void *samplev() const noexcept + { + return pData.pSample; + } + MPT_FORCEINLINE void *samplev() noexcept + { + return pData.pSample; + } + MPT_FORCEINLINE const mpt::byte *sampleb() const noexcept + { + return mpt::void_cast(pData.pSample); + } + MPT_FORCEINLINE mpt::byte *sampleb() noexcept + { + return mpt::void_cast(pData.pSample); + } + MPT_FORCEINLINE const int8 *sample8() const noexcept + { + MPT_ASSERT(GetElementarySampleSize() == sizeof(int8)); + return pData.pSample8; + } + MPT_FORCEINLINE int8 *sample8() noexcept + { + MPT_ASSERT(GetElementarySampleSize() == sizeof(int8)); + return pData.pSample8; + } + MPT_FORCEINLINE const int16 *sample16() const noexcept + { + MPT_ASSERT(GetElementarySampleSize() == sizeof(int16)); + return pData.pSample16; + } + MPT_FORCEINLINE int16 *sample16() noexcept + { + MPT_ASSERT(GetElementarySampleSize() == sizeof(int16)); + return pData.pSample16; + } // Return the size of one (elementary) sample in bytes. - uint8 GetElementarySampleSize() const { return (uFlags & CHN_16BIT) ? 2 : 1; } + uint8 GetElementarySampleSize() const noexcept { return (uFlags & CHN_16BIT) ? 2 : 1; } // Return the number of channels in the sample. - uint8 GetNumChannels() const { return (uFlags & CHN_STEREO) ? 2 : 1; } + uint8 GetNumChannels() const noexcept { return (uFlags & CHN_STEREO) ? 2 : 1; } // Return the number of bytes per frame (Channels * Elementary Sample Size) - uint8 GetBytesPerSample() const { return GetElementarySampleSize() * GetNumChannels(); } + uint8 GetBytesPerSample() const noexcept { return GetElementarySampleSize() * GetNumChannels(); } // Return the size which pSample is at least. - SmpLength GetSampleSizeInBytes() const { return nLength * GetBytesPerSample(); } + SmpLength GetSampleSizeInBytes() const noexcept { return nLength * GetBytesPerSample(); } - // Returns sample rate of the sample. The argument is needed because + // Returns sample rate of the sample. The argument is needed because // the sample rate is obtained differently for different module types. uint32 GetSampleRate(const MODTYPE type) const; @@ -77,7 +124,7 @@ struct ModSample // Returns number of bytes allocated, 0 on failure. size_t AllocateSample(); // Allocate sample memory. On sucess, a pointer to the silenced sample buffer is returned. On failure, nullptr is returned. - static void *AllocateSample(SmpLength numSamples, size_t bytesPerSample); + static void *AllocateSample(SmpLength numFrames, size_t bytesPerSample); // Compute sample buffer size in bytes, including any overhead introduced by pre-computed loops and such. Returns 0 if sample is too big. static size_t GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerSample); @@ -105,6 +152,9 @@ struct ModSample // Check if the sample's cue points are the default cue point set. bool HasCustomCuePoints() const; + void SetDefaultCuePoints(); + + void SetAdlib(bool enable, OPLPatch patch = OPLPatch{{}}); }; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h index 2741f230c..d03cbf35c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../soundbase/SampleFormatCopy.h" @@ -31,7 +33,7 @@ size_t CopyMonoSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourc size_t numFrames = countFrames; SampleConversion sampleConv(conv); const mpt::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); - typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.pSample); + typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { *outBuf = sampleConv(inBuf); @@ -55,7 +57,7 @@ size_t CopyStereoInterleavedSample(ModSample &sample, const Tbyte *sourceBuffer, SampleConversion sampleConvLeft(conv); SampleConversion sampleConvRight(conv); const mpt::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); - typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.pSample); + typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { *outBuf = sampleConvLeft(inBuf); @@ -85,7 +87,7 @@ size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_ size_t numSamplesLeft = countSamplesLeft; SampleConversion sampleConvLeft(conv); const mpt::byte * MPT_RESTRICT inBufLeft = mpt::byte_cast(sourceBuffer); - typename SampleConversion::output_t * MPT_RESTRICT outBufLeft = static_cast(sample.pSample); + typename SampleConversion::output_t * MPT_RESTRICT outBufLeft = static_cast(sample.samplev()); while(numSamplesLeft--) { *outBufLeft = sampleConvLeft(inBufLeft); @@ -96,7 +98,7 @@ size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_ size_t numSamplesRight = countSamplesRight; SampleConversion sampleConvRight(conv); const mpt::byte * MPT_RESTRICT inBufRight = mpt::byte_cast(sourceBuffer) + sample.nLength * SampleConversion::input_inc; - typename SampleConversion::output_t * MPT_RESTRICT outBufRight = static_cast(sample.pSample) + 1; + typename SampleConversion::output_t * MPT_RESTRICT outBufRight = static_cast(sample.samplev()) + 1; while(numSamplesRight--) { *outBufRight = sampleConvRight(inBufRight); @@ -133,7 +135,7 @@ size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size { inBuf = sourceBuffer; // Copying buffer. - typename SampleConversion::output_t *outBuf = static_cast(sample.pSample); + typename SampleConversion::output_t *outBuf = static_cast(sample.samplev()); for(size_t i = numSamples; i != 0; i--) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp index 430d7f1bb..503691312 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp @@ -139,11 +139,11 @@ void ModSequence::RemovePattern(PATTERNINDEX pat) { // First, calculate the offset that needs to be applied to jump commands const ORDERINDEX orderLength = GetLengthTailTrimmed(); - std::vector jumpOffset(orderLength, 0); + std::vector newPosition(orderLength); ORDERINDEX maxJump = 0; for(ORDERINDEX i = 0; i < orderLength; i++) { - jumpOffset[i] = i - maxJump; + newPosition[i] = i - maxJump; if(at(i) == pat) { maxJump++; @@ -154,22 +154,22 @@ void ModSequence::RemovePattern(PATTERNINDEX pat) return; } - erase(std::remove_if(begin(), end(), [pat](PATTERNINDEX p) { return p == pat; }), end()); + erase(std::remove(begin(), end(), pat), end()); // Only apply to patterns actually found in this sequence for(auto p : *this) if(m_sndFile.Patterns.IsValidPat(p)) { for(auto &m : m_sndFile.Patterns[p]) { - if(m.command == CMD_POSITIONJUMP && m.param < jumpOffset.size()) + if(m.command == CMD_POSITIONJUMP && m.param < newPosition.size()) { - m.param = static_cast(jumpOffset[m.param]); + m.param = static_cast(newPosition[m.param]); } } } - if(m_restartPos < jumpOffset.size()) + if(m_restartPos < newPosition.size()) { - m_restartPos = jumpOffset[m_restartPos]; + m_restartPos = newPosition[m_restartPos]; } } @@ -500,7 +500,8 @@ bool ModSequence::IsPositionLocked(ORDERINDEX position) const ///////////////////////////////////// -size_t ModSequence::WriteAsByte(FILE *f, const ORDERINDEX count, uint8 stopIndex, uint8 ignoreIndex) const +#ifndef MODPLUG_NO_FILESAVE +size_t ModSequence::WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex, uint8 ignoreIndex) const { const size_t limit = std::min(count, GetLength()); @@ -511,15 +512,16 @@ size_t ModSequence::WriteAsByte(FILE *f, const ORDERINDEX count, uint8 stopIndex if(pat == GetInvalidPatIndex()) temp = stopIndex; else if(pat == GetIgnoreIndex() || pat > 0xFF) temp = ignoreIndex; - fwrite(&temp, 1, 1, f); + mpt::IO::WriteIntLE(f, temp); } // Fill non-existing order items with stop indices for(size_t i = limit; i < count; i++) { - fwrite(&stopIndex, 1, 1, f); + mpt::IO::WriteIntLE(f, stopIndex); } return count; //Returns the number of bytes written. } +#endif // MODPLUG_NO_FILESAVE void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t) @@ -541,6 +543,7 @@ void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t) } +#ifndef MODPLUG_NO_FILESAVE void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq) { const uint16 size = seq().GetLength(); @@ -550,12 +553,14 @@ void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq) mpt::IO::WriteIntLE(oStrm, static_cast(pat)); } } +#endif // MODPLUG_NO_FILESAVE +#ifndef MODPLUG_NO_FILESAVE void WriteModSequence(std::ostream& oStrm, const ModSequence& seq) { srlztn::SsbWrite ssb(oStrm); - ssb.BeginWrite(FileIdSequence, MptVersion::num); + ssb.BeginWrite(FileIdSequence, Version::Current().GetRawVersion()); ssb.WriteItem(seq.GetName(), "n"); const uint16 length = seq.GetLengthTailTrimmed(); ssb.WriteItem(length, "l"); @@ -564,12 +569,13 @@ void WriteModSequence(std::ostream& oStrm, const ModSequence& seq) ssb.WriteItem(seq.GetRestartPos(), "r"); ssb.FinishWrite(); } +#endif // MODPLUG_NO_FILESAVE void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t) { srlztn::SsbRead ssb(iStrm); - ssb.BeginRead(FileIdSequence, MptVersion::num); + ssb.BeginRead(FileIdSequence, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; std::string str; @@ -586,10 +592,11 @@ void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t) } +#ifndef MODPLUG_NO_FILESAVE void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq) { srlztn::SsbWrite ssb(oStrm); - ssb.BeginWrite(FileIdSequences, MptVersion::num); + ssb.BeginWrite(FileIdSequences, Version::Current().GetRawVersion()); const uint8 nSeqs = seq.GetNumSequences(); const uint8 nCurrent = seq.GetCurrentSequenceIndex(); ssb.WriteItem(nSeqs, "n"); @@ -600,12 +607,13 @@ void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq) } ssb.FinishWrite(); } +#endif // MODPLUG_NO_FILESAVE void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t) { srlztn::SsbRead ssb(iStrm); - ssb.BeginRead(FileIdSequences, MptVersion::num); + ssb.BeginRead(FileIdSequences, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; SEQUENCEINDEX seqs = 0; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h index 8ebf60234..09e0454ce 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include #include "Snd_defs.h" @@ -95,8 +97,10 @@ public: // If another usage is found, the pattern is replaced by a copy and the new index is returned. PATTERNINDEX EnsureUnique(ORDERINDEX ord); +#ifndef MODPLUG_NO_FILESAVE // Write order items as bytes. '---' is written as stopIndex, '+++' is written as ignoreIndex - size_t WriteAsByte(FILE *f, const ORDERINDEX count, uint8 stopIndex = 0xFF, uint8 ignoreIndex = 0xFE) const; + size_t WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex = 0xFF, uint8 ignoreIndex = 0xFE) const; +#endif // MODPLUG_NO_FILESAVE // Returns true if the IT orderlist datafield is not sufficient to store orderlist information. bool NeedsExtraDatafield() const; @@ -185,13 +189,19 @@ public: const char FileIdSequences[] = "mptSeqC"; const char FileIdSequence[] = "mptSeq"; +#ifndef MODPLUG_NO_FILESAVE void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq); +#endif // MODPLUG_NO_FILESAVE void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize = 0); +#ifndef MODPLUG_NO_FILESAVE void WriteModSequence(std::ostream& oStrm, const ModSequence& seq); +#endif // MODPLUG_NO_FILESAVE void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t); +#ifndef MODPLUG_NO_FILESAVE void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq); +#endif // MODPLUG_NO_FILESAVE void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp new file mode 100644 index 000000000..4f949528e --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp @@ -0,0 +1,275 @@ +/* + * OPL.cpp + * ------- + * Purpose: Translate data coming from OpenMPT's mixer into OPL commands to be sent to the Opal emulator. + * Notes : (currently none) + * Authors: OpenMPT Devs + * Schism Tracker contributors (bisqwit, JosepMa, Malvineous, code relicensed from GPL to BSD with permission) + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "../common/misc_util.h" +#include "OPL.h" +#include "opal.h" + +OPENMPT_NAMESPACE_BEGIN + +OPL::OPL() +{ + m_KeyOnBlock.fill(0); + m_OPLtoChan.fill(CHANNELINDEX_INVALID); + m_ChanToOPL.fill(OPL_CHANNEL_INVALID); +} + + +OPL::~OPL() +{ + // This destructor is put here so that we can forward-declare the Opal emulator class. +} + + +void OPL::Initialize(uint32 samplerate) +{ + if(m_opl == nullptr) + m_opl = mpt::make_unique(samplerate); + else + m_opl->SetSampleRate(samplerate); + Reset(); +} + + +void OPL::Mix(int32 *target, size_t count, uint32 volumeFactorQ16) +{ + if(!m_isActive) + return; + + // This factor causes a sample voice to be more or less as loud as an OPL voice + const int32 factor = (volumeFactorQ16 * 6169) / (1 << 16); + while(count--) + { + int16 l, r; + m_opl->Sample(&l, &r); + target[0] += l * factor; + target[1] += r * factor; + target += 2; + } +} + + +uint16 OPL::ChannelToRegister(uint8 oplCh) +{ + if(oplCh < 9) + return oplCh; + else + return (oplCh - 9) | 0x100; +} + + +// Translate a channel's first operator address into a register +uint16 OPL::OperatorToRegister(uint8 oplCh) +{ + static const uint8 OPLChannelToOperator[] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; + if(oplCh < 9) + return OPLChannelToOperator[oplCh]; + else + return OPLChannelToOperator[oplCh - 9] | 0x100; +} + + +uint8 OPL::GetVoice(CHANNELINDEX c) const +{ + return m_ChanToOPL[c]; +} + + +uint8 OPL::AllocateVoice(CHANNELINDEX c) +{ + // Can we re-use a previous channel? + if(m_ChanToOPL[c] != OPL_CHANNEL_INVALID) + { + return GetVoice(c); + } + // Search for unused channel or channel with released note + uint8 releasedChn = OPL_CHANNEL_INVALID; + for(uint8 oplCh = 0; oplCh < OPL_CHANNELS; oplCh++) + { + if(m_OPLtoChan[oplCh] == CHANNELINDEX_INVALID) + { + m_OPLtoChan[oplCh] = c; + m_ChanToOPL[c] = oplCh; + return GetVoice(c); + } else if(!(m_KeyOnBlock[oplCh] & KEYON_BIT)) + { + releasedChn = oplCh; + } + } + if(releasedChn != OPL_CHANNEL_INVALID) + { + m_ChanToOPL[m_OPLtoChan[releasedChn]] = OPL_CHANNEL_INVALID; + m_OPLtoChan[releasedChn] = c; + m_ChanToOPL[c] = releasedChn; + } + return GetVoice(c); +} + + +void OPL::MoveChannel(CHANNELINDEX from, CHANNELINDEX to) +{ + uint8 oplCh = m_ChanToOPL[from]; + if(oplCh == OPL_CHANNEL_INVALID) + return; + m_OPLtoChan[oplCh] = to; + m_ChanToOPL[from] = OPL_CHANNEL_INVALID; + m_ChanToOPL[to] = oplCh; +} + + +void OPL::NoteOff(CHANNELINDEX c) +{ + uint8 oplCh = GetVoice(c); + if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) + return; + m_KeyOnBlock[oplCh] &= ~KEYON_BIT; + m_opl->Port(KEYON_BLOCK | ChannelToRegister(oplCh), m_KeyOnBlock[oplCh]); +} + + +void OPL::NoteCut(CHANNELINDEX c) +{ + NoteOff(c); + Volume(c, 0, false); +} + + +void OPL::Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beatingOscillators) +{ + uint8 oplCh = GetVoice(c); + if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) + return; + + uint16 fnum = 1023; + uint8 block = 7; + if(milliHertz <= 6208431) + { + if(milliHertz > 3104215) block = 7; + else if(milliHertz > 1552107) block = 6; + else if(milliHertz > 776053) block = 5; + else if(milliHertz > 388026) block = 4; + else if(milliHertz > 194013) block = 3; + else if(milliHertz > 97006) block = 2; + else if(milliHertz > 48503) block = 1; + else block = 0; + + fnum = static_cast(Util::muldivr_unsigned(milliHertz, 1 << (20 - block), OPL_BASERATE * 1000)); + MPT_ASSERT(fnum < 1024); + } + + // Evil CDFM hack! Composer 670 slightly detunes each note based on the OPL channel number modulo 4. + // We allocate our OPL channels dynamically, which would result in slightly different beating characteristics, + // but we can just take the pattern channel number instead, as the pattern channel layout is always identical. + if(beatingOscillators) + fnum = std::min(fnum + (c & 3), 1023); + + fnum |= (block << 10); + + uint16 channel = ChannelToRegister(oplCh); + m_KeyOnBlock[oplCh] = (keyOff ? 0 : KEYON_BIT) | (fnum >> 8); // Key on bit + Octave (block) + F-number high 2 bits + m_opl->Port(FNUM_LOW | channel, fnum & 0xFF); // F-Number low 8 bits + m_opl->Port(KEYON_BLOCK | channel, m_KeyOnBlock[oplCh]); + + m_isActive = true; +} + + +uint8 OPL::CalcVolume(uint8 trackerVol, uint8 kslVolume) +{ + if(trackerVol >= 63u) + return kslVolume; + if(trackerVol > 0) + trackerVol++; + return (kslVolume & KSL_MASK) | (63u - ((63u - (kslVolume & TOTAL_LEVEL_MASK)) * trackerVol) / 64u); +} + + +void OPL::Volume(CHANNELINDEX c, uint8 vol, bool applyToModulator) +{ + uint8 oplCh = GetVoice(c); + if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) + return; + + const auto &patch = m_Patches[oplCh]; + const uint16 modulator = OperatorToRegister(oplCh), carrier = modulator + 3; + if((patch[10] & CONNECTION_BIT) || applyToModulator) + { + // Set volume of both operators in additive mode + m_opl->Port(KSL_LEVEL + modulator, CalcVolume(vol, patch[2])); + } + if(!applyToModulator) + { + m_opl->Port(KSL_LEVEL + carrier, CalcVolume(vol, patch[3])); + } +} + + +int8 OPL::Pan(CHANNELINDEX c, int32 pan) +{ + uint8 oplCh = GetVoice(c); + if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) + return 0; + + const auto &patch = m_Patches[oplCh]; + uint8 fbConn = patch[10] & ~STEREO_BITS; + // OPL3 only knows hard left, center and right, so we need to translate our + // continuous panning range into one of those three states. + // 0...84 = left, 85...170 = center, 171...256 = right + if(pan <= 170) + fbConn |= VOICE_TO_LEFT; + if(pan >= 85) + fbConn |= VOICE_TO_RIGHT; + + m_opl->Port(FEEDBACK_CONNECTION | ChannelToRegister(oplCh), fbConn); + return ((fbConn & VOICE_TO_LEFT) ? -1 : 0) + ((fbConn & VOICE_TO_RIGHT) ? 1 : 0); +} + + +void OPL::Patch(CHANNELINDEX c, const OPLPatch &patch) +{ + uint8 oplCh = AllocateVoice(c); + if(oplCh == OPL_CHANNEL_INVALID || m_opl == nullptr) + return; + + m_Patches[oplCh] = patch; + + const uint16 modulator = OperatorToRegister(oplCh), carrier = modulator + 3; + for(uint8 op = 0; op < 2; op++) + { + const auto opReg = op ? carrier : modulator; + m_opl->Port(AM_VIB | opReg, patch[0 + op]); + m_opl->Port(KSL_LEVEL | opReg, patch[2 + op]); + m_opl->Port(ATTACK_DECAY | opReg, patch[4 + op]); + m_opl->Port(SUSTAIN_RELEASE | opReg, patch[6 + op]); + m_opl->Port(WAVE_SELECT | opReg, patch[8 + op]); + } + + m_opl->Port(FEEDBACK_CONNECTION | ChannelToRegister(oplCh), patch[10]); +} + + +void OPL::Reset() +{ + if(m_isActive) + { + for(CHANNELINDEX chn = 0; chn < MAX_CHANNELS; chn++) + { + NoteCut(chn); + } + m_isActive = false; + } + + m_KeyOnBlock.fill(0); + m_OPLtoChan.fill(CHANNELINDEX_INVALID); + m_ChanToOPL.fill(OPL_CHANNEL_INVALID); +} + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h new file mode 100644 index 000000000..331be872b --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h @@ -0,0 +1,110 @@ +/* + * OPL.h + * ----- + * Purpose: Translate data coming from OpenMPT's mixer into OPL commands to be sent to the Opal emulator. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + +#include "Snd_defs.h" + +class Opal; + +OPENMPT_NAMESPACE_BEGIN + +class OPL +{ +public: + enum OPLRegisters : uint8 + { + // Operators (combine with result of OperatorToRegister) + AM_VIB = 0x20, // AM / VIB / EG / KSR / Multiple (0x20 to 0x35) + KSL_LEVEL = 0x40, // KSL / Total level (0x40 to 0x55) + ATTACK_DECAY = 0x60, // Attack rate / Decay rate (0x60 to 0x75) + SUSTAIN_RELEASE = 0x80, // Sustain level / Release rate (0x80 to 0x95) + WAVE_SELECT = 0xE0, // Wave select (0xE0 to 0xF5) + + // Channels (combine with result of ChannelToRegister) + FNUM_LOW = 0xA0, // F-number low bits (0xA0 to 0xA8) + KEYON_BLOCK = 0xB0, // F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + FEEDBACK_CONNECTION = 0xC0, // Feedback / Connection (0xC0 to 0xC8) + }; + + enum OPLValues : uint8 + { + // AM_VIB + TREMOLO_ON = 0x80, + VIBRATO_ON = 0x40, + SUSTAIN_ON = 0x20, + KSR = 0x10, // Key scaling rate + MULTIPLE_MASK = 0x0F, // Frequency multiplier + + // KSL_LEVEL + KSL_MASK = 0xC0, // Envelope scaling bits + TOTAL_LEVEL_MASK = 0x3F, // Strength (volume) of OP + + // ATTACK_DECAY + ATTACK_MASK = 0xF0, + DECAY_MASK = 0x0F, + + // SUSTAIN_RELEASE + SUSTAIN_MASK = 0xF0, + RELEASE_MASK = 0x0F, + + // KEYON_BLOCK + KEYON_BIT = 0x20, + + // FEEDBACK_CONNECTION + FEEDBACK_MASK = 0x0E, // Valid just for first OP of a voice + CONNECTION_BIT = 0x01, + VOICE_TO_LEFT = 0x10, + VOICE_TO_RIGHT = 0x20, + STEREO_BITS = VOICE_TO_LEFT | VOICE_TO_RIGHT, + }; + + OPL(); + ~OPL(); + + void Initialize(uint32 samplerate); + void Mix(int32 *buffer, size_t count, uint32 volumeFactorQ16); + + void NoteOff(CHANNELINDEX c); + void NoteCut(CHANNELINDEX c); + void Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beatingOscillators); + void Volume(CHANNELINDEX c, uint8 vol, bool applyToModulator); + int8 Pan(CHANNELINDEX c, int32 pan); + void Patch(CHANNELINDEX c, const OPLPatch &patch); + void Reset(); + bool IsActive(CHANNELINDEX c) { return GetVoice(c) != OPL_CHANNEL_INVALID; } + void MoveChannel(CHANNELINDEX from, CHANNELINDEX to); + +protected: + static uint16 ChannelToRegister(uint8 oplCh); + static uint16 OperatorToRegister(uint8 oplCh); + static uint8 CalcVolume(uint8 trackerVol, uint8 kslVolume); + uint8 GetVoice(CHANNELINDEX c) const; + uint8 AllocateVoice(CHANNELINDEX c); + + enum + { + OPL_CHANNELS = 18, // 9 for OPL2 or 18 for OPL3 + OPL_CHANNEL_INVALID = 0xFF, + OPL_BASERATE = 49716, + }; + + std::unique_ptr m_opl; + + std::array m_KeyOnBlock; + std::array m_OPLtoChan; + std::array m_ChanToOPL; + std::array m_Patches; + + bool m_isActive = false; +}; + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h index b5884c4f5..4d97f9240 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/Endianness.h" #include "../common/mptIO.h" diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp index 8d98b64f4..4b357d6e9 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp @@ -270,6 +270,8 @@ static constexpr int32 WinSincIntegral[2][2048] = }; +// we do not initialize blepState here +// cppcheck-suppress uninitMemberVar State::State(uint32 sampleRate) { double amigaClocksPerSample = static_cast(PAULA_HZ) / sampleRate; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h index 360c1caa8..5de16a192 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h index 0f470bca8..5c4b290b1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + #include "WindowedFIR.h" #include "Mixer.h" @@ -59,12 +61,12 @@ public: uint8 gbWFIRType; bool emulateAmiga; public: - CResamplerSettings() + MPT_CONSTEXPR11_FUN CResamplerSettings() + : SrcMode(Resampling::Default()) + , gdWFIRCutoff(0.97) + , gbWFIRType(WFIR_KAISER4T) + , emulateAmiga(false) { - SrcMode = SRCMODE_POLYPHASE; - gdWFIRCutoff = 0.97; - gbWFIRType = WFIR_KAISER4T; - emulateAmiga = false; } bool operator == (const CResamplerSettings &cmp) const { @@ -125,8 +127,6 @@ public: { InitializeTablesFromScratch(false); } - ~CResampler() {} - bool IsHQ() const { return m_Settings.SrcMode >= SRCMODE_SPLINE && m_Settings.SrcMode < SRCMODE_DEFAULT; } private: void InitFloatmixerTables(); void InitializeTablesFromScratch(bool force=false); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp index 121dd2120..c670b5d7f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp @@ -35,6 +35,13 @@ RowVisitor::RowVisitor(const CSoundFile &sf, SEQUENCEINDEX sequence) } +RowVisitor& RowVisitor::operator=(RowVisitor &&other) +{ + m_visitedRows = std::move(other.m_visitedRows); + return *this; +} + + const ModSequence &RowVisitor::Order() const { if(m_sequence >= m_sndFile.Order.GetNumSequences()) @@ -58,9 +65,9 @@ void RowVisitor::Initialize(bool reset) if(m_visitOrder.capacity() < MAX_PATTERN_ROWS) { ROWINDEX maxRows = 0; - for(PATTERNINDEX pat = 0; pat < m_sndFile.Patterns.Size(); pat++) + for(const auto &pat :m_sndFile.Patterns) { - maxRows = std::max(maxRows, m_sndFile.Patterns[pat].GetNumRows()); + maxRows = std::max(maxRows, pat.GetNumRows()); } m_visitOrder.reserve(maxRows); } @@ -162,11 +169,11 @@ size_t RowVisitor::GetVisitedRowsVectorSize(PATTERNINDEX pattern) const // Find the first row that has not been played yet. // The order and row is stored in the order and row variables on success, on failure they contain invalid values. -// If fastSearch is true (default), only the first row of each pattern is looked at, otherwise every row is examined. +// If onlyUnplayedPatterns is true (default), only completely unplayed patterns are considered, otherwise a song can start anywhere. // Function returns true on success. -bool RowVisitor::GetFirstUnvisitedRow(ORDERINDEX &ord, ROWINDEX &row, bool fastSearch) const +bool RowVisitor::GetFirstUnvisitedRow(ORDERINDEX &ord, ROWINDEX &row, bool onlyUnplayedPatterns) const { - auto &order = Order(); + const auto &order = Order(); const ORDERINDEX endOrder = order.GetLengthTailTrimmed(); for(ord = 0; ord < endOrder; ord++) { @@ -179,15 +186,29 @@ bool RowVisitor::GetFirstUnvisitedRow(ORDERINDEX &ord, ROWINDEX &row, bool fastS if(ord >= m_visitedRows.size()) { // Not yet initialized => unvisited + row = 0; return true; } - const ROWINDEX endRow = (fastSearch ? 1 : m_sndFile.Patterns[pattern].GetNumRows()); - for(row = 0; row < endRow; row++) + const auto &visitedRows = m_visitedRows[ord]; + auto foundRow = std::find(visitedRows.begin(), visitedRows.end(), onlyUnplayedPatterns); + if(onlyUnplayedPatterns && foundRow == visitedRows.end()) { - if(row >= m_visitedRows[ord].size() || m_visitedRows[ord][row] == false) + // No row of this pattern has been played yet. + row = 0; + return true; + } else if(!onlyUnplayedPatterns) + { + // Return the first unplayed row in this pattern + if(foundRow != visitedRows.end()) { - // Not yet initialized, or unvisited + row = static_cast(std::distance(visitedRows.begin(), foundRow)); + return true; + } + if(visitedRows.size() < m_sndFile.Patterns[pattern].GetNumRows()) + { + // History is not fully initialized + row = static_cast(visitedRows.size()); return true; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h index 3eb8518f1..6a72c948b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include "Snd_defs.h" @@ -32,6 +34,7 @@ protected: public: RowVisitor(const CSoundFile &sf, SEQUENCEINDEX sequence = SEQUENCEINDEX_INVALID); + RowVisitor& operator=(RowVisitor &&other); // Resize / Clear the row vector. // If reset is true, the vector is not only resized to the required dimensions, but also completely cleared (i.e. all visited rows are unset). @@ -41,13 +44,13 @@ public: void Visit(ORDERINDEX ord, ROWINDEX row) { SetVisited(ord, row, true); - }; + } // Mark a row as not visited. void Unvisit(ORDERINDEX ord, ROWINDEX row) { SetVisited(ord, row, false); - }; + } // Returns whether a given row has been visited yet. // If autoSet is true, the queried row will automatically be marked as visited. @@ -59,15 +62,9 @@ public: // Find the first row that has not been played yet. // The order and row is stored in the order and row variables on success, on failure they contain invalid values. - // If fastSearch is true (default), only the first row of each pattern is looked at, otherwise every row is examined. + // If onlyUnplayedPatterns is true (default), only completely unplayed patterns are considered, otherwise a song can start anywhere. // Function returns true on success. - bool GetFirstUnvisitedRow(ORDERINDEX &order, ROWINDEX &row, bool fastSearch) const; - - // Retrieve visited rows vector from another RowVisitor object. - void Set(const RowVisitor &other) - { - m_visitedRows = other.m_visitedRows; - } + bool GetFirstUnvisitedRow(ORDERINDEX &order, ROWINDEX &row, bool onlyUnplayedPatterns) const; // Set all rows of a previous pattern loop as unvisited. void ResetPatternLoop(ORDERINDEX ord, ROWINDEX startRow); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp index 4d2d98717..e69f5723b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp @@ -11,19 +11,18 @@ #include "stdafx.h" #include "Loaders.h" #include "S3MTools.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" OPENMPT_NAMESPACE_BEGIN - // Convert an S3M sample header to OpenMPT's internal sample header. void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(MOD_TYPE_S3M); mpt::String::Read(mptSmp.filename, filename); - if((sampleType == typePCM || sampleType == typeNone) && !memcmp(magic, "SCRS", 4)) + if(sampleType == typePCM || sampleType == typeNone) { // Sample Length and Loops if(sampleType == typePCM) @@ -39,20 +38,28 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const mptSmp.nLoopStart = mptSmp.nLoopEnd = 0; mptSmp.uFlags.reset(); } - - // Volume / Panning - mptSmp.nVolume = MIN(defaultVolume, 64) * 4; - - // C-5 frequency - mptSmp.nC5Speed = c5speed; - if(mptSmp.nC5Speed == 0) - { - mptSmp.nC5Speed = 8363; - } else if(mptSmp.nC5Speed < 1024) - { - mptSmp.nC5Speed = 1024; - } + } else if(sampleType == typeAdMel) + { + OPLPatch patch; + std::memcpy(patch.data() + 0, mpt::as_raw_memory(length).data(), 4); + std::memcpy(patch.data() + 4, mpt::as_raw_memory(loopStart).data(), 4); + std::memcpy(patch.data() + 8, mpt::as_raw_memory(loopEnd).data(), 4); + mptSmp.SetAdlib(true, patch); } + + // Volume / Panning + mptSmp.nVolume = std::min(defaultVolume, 64) * 4; + + // C-5 frequency + mptSmp.nC5Speed = c5speed; + if(mptSmp.nC5Speed == 0) + { + mptSmp.nC5Speed = 8363; + } else if(mptSmp.nC5Speed < 1024) + { + mptSmp.nC5Speed = 1024; + } + } @@ -61,13 +68,21 @@ SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp) { SmpLength smpLength = 0; mpt::String::Write(filename, mptSmp.filename); + memcpy(magic, "SCRS", 4); - if(mptSmp.pSample != nullptr) + if(mptSmp.uFlags[CHN_ADLIB]) + { + memcpy(magic, "SCRI", 4); + sampleType = typeAdMel; + std::memcpy(mpt::as_raw_memory(length ).data(), mptSmp.adlib.data() + 0, 4); + std::memcpy(mpt::as_raw_memory(loopStart).data(), mptSmp.adlib.data() + 4, 4); + std::memcpy(mpt::as_raw_memory(loopEnd ).data(), mptSmp.adlib.data() + 8, 4); + } else if(mptSmp.HasSampleData()) { sampleType = typePCM; - length = static_cast(MIN(mptSmp.nLength, uint32_max)); - loopStart = static_cast(MIN(mptSmp.nLoopStart, uint32_max)); - loopEnd = static_cast(MIN(mptSmp.nLoopEnd, uint32_max)); + length = mpt::saturate_cast(mptSmp.nLength); + loopStart = mpt::saturate_cast(mptSmp.nLoopStart); + loopEnd = mpt::saturate_cast(mptSmp.nLoopEnd); smpLength = length; @@ -93,7 +108,6 @@ SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp) { c5speed = ModSample::TransposeToFrequency(mptSmp.RelativeTone, mptSmp.nFineTune); } - memcpy(magic, "SCRS", 4); return smpLength; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h index d9dfa8865..00753aa36 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../soundlib/ModSample.h" #include "../soundlib/SampleIO.h" @@ -44,6 +46,7 @@ struct S3MFileHeader trkST3_20 = 0x1320, trkIT2_14 = 0x3214, trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012 + trkCamoto = 0xCA00, }; // Flags diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp index b52bcecb1..09aed0a0b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp @@ -124,9 +124,6 @@ struct FLACDecoder // Source bit depth const unsigned int bps = frame->header.bits_per_sample; - int8 *sampleData8 = sample.pSample8 + offset; - int16 *sampleData16 = sample.pSample16 + offset; - MPT_ASSERT((bps <= 8 && sample.GetElementarySampleSize() == 1) || (bps > 8 && sample.GetElementarySampleSize() == 2)); MPT_ASSERT(modChannels <= FLAC__stream_decoder_get_channels(decoder)); MPT_ASSERT(bps == FLAC__stream_decoder_get_bits_per_sample(decoder)); @@ -137,15 +134,19 @@ struct FLACDecoder { if(bps <= 8) { + int8 *sampleData8 = sample.sample8() + offset; CopySample, SC::DecodeIdentity > >(sampleData8 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } else if(bps <= 16) { + int16 *sampleData16 = sample.sample16() + offset; CopySample, SC::DecodeIdentity > >(sampleData16 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } else if(bps <= 24) { + int16 *sampleData16 = sample.sample16() + offset; CopySample, SC::DecodeIdentity > >(sampleData16 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } else if(bps <= 32) { + int16 *sampleData16 = sample.sample16() + offset; CopySample, SC::DecodeIdentity > >(sampleData16 + chn, copySamples, modChannels, buffer[chn], srcSize, 1); } } @@ -183,7 +184,7 @@ struct FLACDecoder // We're not really going to read a WAV file here because there will be only one RIFF chunk per metadata event, but we can still re-use the code for parsing RIFF metadata... WAVReader riffReader(data); riffReader.FindMetadataChunks(chunks); - riffReader.ApplySampleSettings(sample, client.sndFile.m_szNames[client.sample]); + riffReader.ApplySampleSettings(sample, client.sndFile.GetCharsetInternal(), client.sndFile.m_szNames[client.sample]); } else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT && client.ready) { // Try reading Vorbis Comments for sample title, sample rate and loop points @@ -194,7 +195,9 @@ struct FLACDecoder const FLAC__uint32 length = metadata->data.vorbis_comment.comments[i].length; if(length > 6 && !mpt::CompareNoCaseAscii(tag, "TITLE=", 6)) { - mpt::String::Read(client.sndFile.m_szNames[client.sample], tag + 6, length - 6); + mpt::ustring sampleName; + mpt::String::Read(sampleName, client.sndFile.GetCharsetInternal(), tag + 6, length - 6); + mpt::String::Copy(client.sndFile.m_szNames[client.sample], mpt::ToCharset(mpt::CharsetUTF8, sampleName)); } else if(length > 11 && !mpt::CompareNoCaseAscii(tag, "SAMPLERATE=", 11)) { uint32 sampleRate = ConvertStrTo(tag + 11); @@ -438,7 +441,7 @@ bool CSoundFile::ReadFLACSample(SAMPLEINDEX sample, FileReader &file) FLAC__stream_decoder_finish(decoder); FLAC__stream_decoder_delete(decoder); - if(client.ready && Samples[sample].pSample != nullptr) + if(client.ready && Samples[sample].HasSampleData()) { Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); @@ -453,29 +456,79 @@ bool CSoundFile::ReadFLACSample(SAMPLEINDEX sample, FileReader &file) #ifdef MPT_WITH_FLAC -// Helper function for copying OpenMPT's sample data to FLAC's int32 buffer. -template -inline static void SampleToFLAC32(FLAC__int32 *dst, const T *src, SmpLength numSamples) -{ - for(SmpLength i = 0; i < numSamples; i++) - { - dst[i] = src[i]; - } -}; // RAII-style helper struct for FLAC encoder struct FLAC__StreamEncoder_RAII { - FLAC__StreamEncoder *encoder; - FILE *f; + std::ostream &f; + FLAC__StreamEncoder *encoder = nullptr; operator FLAC__StreamEncoder *() { return encoder; } - FLAC__StreamEncoder_RAII() : encoder(FLAC__stream_encoder_new()), f(nullptr) { } + FLAC__StreamEncoder_RAII(std::ostream &f_) : f(f_), encoder(FLAC__stream_encoder_new()) { } ~FLAC__StreamEncoder_RAII() { FLAC__stream_encoder_delete(encoder); - if(f != nullptr) fclose(f); + } + + static FLAC__StreamEncoderWriteStatus StreamEncoderWriteCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) + { + mpt::ofstream & file = *reinterpret_cast(client_data); + MPT_UNUSED_VARIABLE(encoder); + MPT_UNUSED_VARIABLE(samples); + MPT_UNUSED_VARIABLE(current_frame); + if(!mpt::IO::WriteRaw(file, mpt::as_span(buffer, bytes))) + { + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + static FLAC__StreamEncoderSeekStatus StreamEncoderSeekCallback(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) + { + mpt::ofstream & file = *reinterpret_cast(client_data); + MPT_UNUSED_VARIABLE(encoder); + if(!Util::TypeCanHoldValue(absolute_byte_offset)) + { + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + } + if(!mpt::IO::SeekAbsolute(file, static_cast(absolute_byte_offset))) + { + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + } + return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + } + static FLAC__StreamEncoderTellStatus StreamEncoderTellCallback(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + { + mpt::ofstream & file = *reinterpret_cast(client_data); + MPT_UNUSED_VARIABLE(encoder); + mpt::IO::Offset pos = mpt::IO::TellWrite(file); + if(pos < 0) + { + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + } + if(!mpt::IO::OffsetFits(pos)) + { + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + } + *absolute_byte_offset = static_cast(pos); + return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + } + +}; + +class FLAC__StreamMetadata_RAII : public std::vector +{ +public: + FLAC__StreamMetadata_RAII(std::initializer_list init) + : std::vector(init) + { } + + ~FLAC__StreamMetadata_RAII() + { + for(auto m : *this) + { + FLAC__metadata_object_delete(m); + } } }; @@ -483,10 +536,10 @@ struct FLAC__StreamEncoder_RAII #ifndef MODPLUG_NO_FILESAVE -bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const +bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const { #ifdef MPT_WITH_FLAC - FLAC__StreamEncoder_RAII encoder; + FLAC__StreamEncoder_RAII encoder(f); if(encoder == nullptr) { return false; @@ -496,7 +549,7 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, const mpt::PathString &file uint32 sampleRate = sample.GetSampleRate(GetType()); // First off, set up all the metadata... - FLAC__StreamMetadata *metadata[] = + FLAC__StreamMetadata_RAII metadata = { FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT), FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION), // MPT sample information @@ -509,9 +562,9 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, const mpt::PathString &file { // Store sample name FLAC__StreamMetadata_VorbisComment_Entry entry; - FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "TITLE", m_szNames[nSample]); + FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "TITLE", mpt::ToCharset(mpt::CharsetUTF8, GetCharsetInternal(), m_szNames[nSample]).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); - FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ENCODER", MptVersion::GetOpenMPTVersionStr().c_str()); + FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ENCODER", mpt::ToCharset(mpt::CharsetUTF8, Version::Current().GetOpenMPTVersionString()).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); if(sampleRate > FLAC__MAX_SAMPLE_RATE) { @@ -613,56 +666,42 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, const mpt::PathString &file FLAC__stream_encoder_set_bits_per_sample(encoder, sample.GetElementarySampleSize() * 8); FLAC__stream_encoder_set_sample_rate(encoder, sampleRate); FLAC__stream_encoder_set_total_samples_estimate(encoder, sample.nLength); - FLAC__stream_encoder_set_metadata(encoder, metadata, numBlocks); + FLAC__stream_encoder_set_metadata(encoder, metadata.data(), numBlocks); #ifdef MODPLUG_TRACKER FLAC__stream_encoder_set_compression_level(encoder, TrackerSettings::Instance().m_FLACCompressionLevel); #endif // MODPLUG_TRACKER - bool result = false; - FLAC__int32 *sampleData = nullptr; - - encoder.f = mpt_fopen(filename, "wb"); - if(encoder.f == nullptr || FLAC__stream_encoder_init_FILE(encoder, encoder.f, nullptr, nullptr) != FLAC__STREAM_ENCODER_INIT_STATUS_OK) + bool success = FLAC__stream_encoder_init_stream(encoder, &FLAC__StreamEncoder_RAII::StreamEncoderWriteCallback, &FLAC__StreamEncoder_RAII::StreamEncoderSeekCallback, &FLAC__StreamEncoder_RAII::StreamEncoderTellCallback, nullptr, &encoder.f) == FLAC__STREAM_ENCODER_INIT_STATUS_OK; + + // Convert and encode sample data + SmpLength framesRemain = sample.nLength, framesRead = 0; + const uint8 numChannels = sample.GetNumChannels(); + FLAC__int32 buffer[mpt::IO::BUFFERSIZE_TINY]; + while(framesRemain && success) { - goto fail; + const SmpLength copyFrames = std::min(framesRemain, mpt::saturate_cast(mpt::size(buffer) / numChannels)); + + // First, convert to a 32-bit integer buffer + switch(sample.GetElementarySampleSize()) + { + case 1: std::copy(sample.sample8() + framesRead * numChannels, sample.sample8() + (framesRead + copyFrames) * numChannels, std::begin(buffer)); break; + case 2: std::copy(sample.sample16() + framesRead * numChannels, sample.sample16() + (framesRead + copyFrames) * numChannels, std::begin(buffer)); break; + default: MPT_ASSERT_NOTREACHED(); + } + + // Now do the actual encoding + success = FLAC__stream_encoder_process_interleaved(encoder, buffer, copyFrames) != false; + + framesRead += copyFrames; + framesRemain -= copyFrames; } - // Convert sample data to signed 32-Bit integer array. - const SmpLength numSamples = sample.nLength * sample.GetNumChannels(); - sampleData = new (std::nothrow) FLAC__int32[numSamples]; - if(sampleData == nullptr) - { - goto fail; - } - - if(sample.GetElementarySampleSize() == 1) - { - SampleToFLAC32(sampleData, sample.pSample8, numSamples); - } else if(sample.GetElementarySampleSize() == 2) - { - SampleToFLAC32(sampleData, sample.pSample16, numSamples); - } else - { - MPT_ASSERT_NOTREACHED(); - } - - // Do the actual conversion. - FLAC__stream_encoder_process_interleaved(encoder, sampleData, sample.nLength); - result = true; - -fail: FLAC__stream_encoder_finish(encoder); - delete[] sampleData; - for(auto m : metadata) - { - FLAC__metadata_object_delete(m); - } - - return result; + return success; #else MPT_UNREFERENCED_PARAMETER(nSample); - MPT_UNREFERENCED_PARAMETER(filename); + MPT_UNREFERENCED_PARAMETER(f); return false; #endif // MPT_WITH_FLAC } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp index a48ea48e1..f43743d8e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp @@ -26,9 +26,7 @@ #include "MPEGFrame.h" #endif // MPT_ENABLE_MP3_SAMPLES #if defined(MPT_WITH_MINIMP3) -extern "C" { #include -} #endif // MPT_WITH_MINIMP3 // mpg123 must be last because of mpg123 large file support insanity @@ -51,11 +49,7 @@ OPENMPT_NAMESPACE_BEGIN #if defined(MPT_WITH_MPG123) -#if MPT_COMPILER_MSVCCLANGC2 -typedef _off_t mpg123_off_t; -#else // !MPT_COMPILER_MSVCCLANGC2 typedef off_t mpg123_off_t; -#endif // MPT_COMPILER_MSVCCLANGC2 typedef size_t mpg123_size_t; @@ -97,14 +91,14 @@ public: public: ComponentMPG123() #if defined(MPT_ENABLE_MPG123_DELAYLOAD) - : ComponentBundledDLL(MPT_PATHSTRING("openmpt-mpg123")) + : ComponentBundledDLL(P_("openmpt-mpg123")) #else : ComponentBuiltin() #endif { return; } - bool DoInitialize() + bool DoInitialize() override { #if defined(MPT_ENABLE_MPG123_DELAYLOAD) if(!ComponentBundledDLL::DoInitialize()) @@ -126,12 +120,44 @@ public: } } }; -MPT_REGISTERED_COMPONENT(ComponentMPG123, "Mpg123") +MPT_REGISTERED_COMPONENT(ComponentMPG123, "") + +static mpt::ustring ReadMPG123String(const mpg123_string &str) +{ + mpt::ustring result; + if(!str.p) + { + return result; + } + if(str.fill < 1) + { + return result; + } + result = mpt::ToUnicode(mpt::CharsetUTF8, std::string(str.p, str.p + str.fill - 1)); + return result; +} + +static mpt::ustring ReadMPG123String(const mpg123_string *str) +{ + mpt::ustring result; + if(!str) + { + return result; + } + result = ReadMPG123String(*str); + return result; +} + +template +static mpt::ustring ReadMPG123String(const char (&str)[N]) +{ + return mpt::ToUnicode(mpt::CharsetISO8859_1, mpt::String::ReadBuf(mpt::String::spacePadded, str)); +} #endif // MPT_WITH_MPG123 -bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool mo3Decode) +bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, bool mo3Decode) { #if defined(MPT_WITH_MPG123) || defined(MPT_WITH_MINIMP3) @@ -189,89 +215,381 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool mo3Dec return false; } - mpg123_handle *mh; - int err; - if((mh = mpg123_new(0, &err)) == nullptr) + struct MPG123Handle + { + mpg123_handle *mh; + MPG123Handle() : mh(mpg123_new(0, nullptr)) { } + ~MPG123Handle() { mpg123_delete(mh); } + operator mpg123_handle *() { return mh; } + }; + + bool hasLameXingVbriHeader = false; + + if(!raw) + { + + mpg123_off_t length_raw = 0; + mpg123_off_t length_hdr = 0; + + // libmpg123 provides no way to determine whether it parsed ID3V2 or VBR tags. + // Thus, we use a pre-scan with those disabled and compare the resulting length. + // We ignore ID3V2 stream length here, althrough we parse the ID3V2 header. + // libmpg123 only accounts for the VBR info frame if gapless &&!ignore_infoframe, + // thus we switch both of those for comparison. + { + MPG123Handle mh; + if(!mh) + { + return false; + } + file.Rewind(); + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_AUTO_RESAMPLE, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_IGNORE_INFOFRAME, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_SKIP_ID3V2, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_IGNORE_STREAMLENGTH, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_INDEX_SIZE, -1000, 0.0)) // auto-grow + { + return false; + } + if(mpg123_replace_reader_handle(mh, ComponentMPG123::FileReaderRead, ComponentMPG123::FileReaderLSeek, 0)) + { + return false; + } + if(mpg123_open_handle(mh, &file)) + { + return false; + } + if(mpg123_scan(mh)) + { + return false; + } + long rate = 0; + int channels = 0; + int encoding = 0; + if(mpg123_getformat(mh, &rate, &channels, &encoding)) + { + return false; + } + if((channels != 1 && channels != 2) || (encoding & (MPG123_ENC_16 | MPG123_ENC_SIGNED)) != (MPG123_ENC_16 | MPG123_ENC_SIGNED)) + { + return false; + } + mpg123_frameinfo frameinfo; + MemsetZero(frameinfo); + if(mpg123_info(mh, &frameinfo)) + { + return false; + } + if(frameinfo.layer < 1 || frameinfo.layer > 3) + { + return false; + } + if(mpg123_param(mh, MPG123_FORCE_RATE, rate, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, (channels > 1) ? MPG123_FORCE_STEREO : MPG123_FORCE_MONO, 0.0)) + { + return false; + } + length_raw = mpg123_length(mh); + } + + { + MPG123Handle mh; + if(!mh) + { + return false; + } + file.Rewind(); + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_AUTO_RESAMPLE, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_GAPLESS, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_IGNORE_INFOFRAME, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_SKIP_ID3V2, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_IGNORE_STREAMLENGTH, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_INDEX_SIZE, -1000, 0.0)) // auto-grow + { + return false; + } + if(mpg123_replace_reader_handle(mh, ComponentMPG123::FileReaderRead, ComponentMPG123::FileReaderLSeek, 0)) + { + return false; + } + if(mpg123_open_handle(mh, &file)) + { + return false; + } + if(mpg123_scan(mh)) + { + return false; + } + long rate = 0; + int channels = 0; + int encoding = 0; + if(mpg123_getformat(mh, &rate, &channels, &encoding)) + { + return false; + } + if((channels != 1 && channels != 2) || (encoding & (MPG123_ENC_16 | MPG123_ENC_SIGNED)) != (MPG123_ENC_16 | MPG123_ENC_SIGNED)) + { + return false; + } + mpg123_frameinfo frameinfo; + MemsetZero(frameinfo); + if(mpg123_info(mh, &frameinfo)) + { + return false; + } + if(frameinfo.layer < 1 || frameinfo.layer > 3) + { + return false; + } + if(mpg123_param(mh, MPG123_FORCE_RATE, rate, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, (channels > 1) ? MPG123_FORCE_STEREO : MPG123_FORCE_MONO, 0.0)) + { + return false; + } + length_hdr = mpg123_length(mh); + } + + hasLameXingVbriHeader = (length_raw != length_hdr); + + } + + // Set up decoder... + MPG123Handle mh; + if(!mh) { return false; } file.Rewind(); - - long rate; int nchannels, encoding; - SmpLength length; - // Set up decoder... if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0)) { - mpg123_delete(mh); + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_AUTO_RESAMPLE, 0.0)) + { + return false; + } + if(mpg123_param(mh, raw ? MPG123_REMOVE_FLAGS : MPG123_ADD_FLAGS, MPG123_GAPLESS, 0.0)) + { + return false; + } + if(mpg123_param(mh, raw ? MPG123_ADD_FLAGS : MPG123_REMOVE_FLAGS, MPG123_IGNORE_INFOFRAME, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_REMOVE_FLAGS, MPG123_SKIP_ID3V2, 0.0)) + { + return false; + } + if(mpg123_param(mh, raw ? MPG123_ADD_FLAGS : MPG123_REMOVE_FLAGS, MPG123_IGNORE_STREAMLENGTH, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_INDEX_SIZE, -1000, 0.0)) // auto-grow + { return false; } if(mpg123_replace_reader_handle(mh, ComponentMPG123::FileReaderRead, ComponentMPG123::FileReaderLSeek, 0)) { - mpg123_delete(mh); return false; } if(mpg123_open_handle(mh, &file)) { - mpg123_delete(mh); return false; } if(mpg123_scan(mh)) { - mpg123_delete(mh); return false; } - if(mpg123_getformat(mh, &rate, &nchannels, &encoding)) + long rate = 0; + int channels = 0; + int encoding = 0; + if(mpg123_getformat(mh, &rate, &channels, &encoding)) { - mpg123_delete(mh); return false; } - if(!nchannels || nchannels > 2 || (encoding & (MPG123_ENC_16 | MPG123_ENC_SIGNED)) != (MPG123_ENC_16 | MPG123_ENC_SIGNED)) + if((channels != 1 && channels != 2) || (encoding & (MPG123_ENC_16 | MPG123_ENC_SIGNED)) != (MPG123_ENC_16 | MPG123_ENC_SIGNED)) { - mpg123_delete(mh); return false; } - length = mpg123_length(mh); - if(length == 0) + mpg123_frameinfo frameinfo; + MemsetZero(frameinfo); + if(mpg123_info(mh, &frameinfo)) + { + return false; + } + if(frameinfo.layer < 1 || frameinfo.layer > 3) + { + return false; + } + // We force samplerate, channels and sampleformat, which in + // combination with auto-resample (set above) will cause libmpg123 + // to stay with the given format even for completely confused + // MPG123_FRANKENSTEIN streams. + // Note that we cannot rely on mpg123_length() for the way we + // decode the mpeg streams because it depends on the actual frame + // sample rate instead of the returned sample rate. + if(mpg123_param(mh, MPG123_FORCE_RATE, rate, 0.0)) + { + return false; + } + if(mpg123_param(mh, MPG123_ADD_FLAGS, (channels > 1) ? MPG123_FORCE_STEREO : MPG123_FORCE_MONO, 0.0)) { - mpg123_delete(mh); return false; } - if(length > MAX_SAMPLE_LENGTH) + std::vector data; + + // decoder delay + std::size_t data_skip_frames = 0; + if(!raw && !hasLameXingVbriHeader) + { + if(frameinfo.layer == 1) + { + data_skip_frames = 240 + 1; + } else if(frameinfo.layer == 2) + { + data_skip_frames = 240 + 1; + } else if(frameinfo.layer == 3) + { + data_skip_frames = 528 + 1; + } + } + + std::vector buf_bytes; + std::vector buf_samples; + bool decode_error = false; + bool decode_done = false; + while(!decode_error && !decode_done) + { + buf_bytes.resize(mpg123_outblock(mh)); + buf_samples.resize(buf_bytes.size() / sizeof(int16)); + mpg123_size_t buf_bytes_decoded = 0; + int mpg123_read_result = mpg123_read(mh, mpt::byte_cast(buf_bytes.data()), buf_bytes.size(), &buf_bytes_decoded); + std::memcpy(buf_samples.data(), buf_bytes.data(), buf_bytes_decoded); + data.insert(data.end(), buf_samples.data(), buf_samples.data() + buf_bytes_decoded / sizeof(int16)); + if((data.size() / channels) > MAX_SAMPLE_LENGTH) + { + break; + } + if(mpg123_read_result == MPG123_OK) + { + // continue + } else if(mpg123_read_result == MPG123_NEW_FORMAT) + { + // continue + } else if(mpg123_read_result == MPG123_DONE) + { + decode_done = true; + } else + { + decode_error = true; + } + } + + if((data.size() / channels) > MAX_SAMPLE_LENGTH) { - mpg123_delete(mh); return false; } + FileTags tags; + mpg123_id3v1 *id3v1 = nullptr; + mpg123_id3v2 *id3v2 = nullptr; + if(mpg123_id3(mh, &id3v1, &id3v2) == MPG123_OK) + { + if(id3v2) + { + if(tags.title.empty()) tags.title = ReadMPG123String(id3v2->title); + if(tags.artist.empty()) tags.artist = ReadMPG123String(id3v2->artist); + if(tags.album.empty()) tags.album = ReadMPG123String(id3v2->album); + if(tags.year.empty()) tags.year = ReadMPG123String(id3v2->year); + if(tags.genre.empty()) tags.genre = ReadMPG123String(id3v2->genre); + if(tags.comments.empty()) tags.comments = ReadMPG123String(id3v2->comment); + } + if(id3v1) + { + if(tags.title.empty()) tags.title = ReadMPG123String(id3v1->title); + if(tags.artist.empty()) tags.artist = ReadMPG123String(id3v1->artist); + if(tags.album.empty()) tags.album = ReadMPG123String(id3v1->album); + if(tags.year.empty()) tags.year = ReadMPG123String(id3v1->year); + if(tags.comments.empty()) tags.comments = ReadMPG123String(id3v1->comment); + } + } + mpt::ustring sampleName = GetSampleNameFromTags(tags); + DestroySampleThreadsafe(sample); if(!mo3Decode) { - strcpy(m_szNames[sample], ""); + mpt::String::Copy(m_szNames[sample], mpt::ToCharset(GetCharsetInternal(), sampleName)); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; } - Samples[sample].nLength = length; + Samples[sample].nLength = mpt::saturate_cast((data.size() / channels) - data_skip_frames); Samples[sample].uFlags.set(CHN_16BIT); - Samples[sample].uFlags.set(CHN_STEREO, nchannels == 2); + Samples[sample].uFlags.set(CHN_STEREO, channels == 2); Samples[sample].AllocateSample(); - if(Samples[sample].pSample != nullptr) + if(Samples[sample].HasSampleData()) { - mpg123_size_t ndecoded = 0; - mpg123_read(mh, static_cast(Samples[sample].pSample), Samples[sample].GetSampleSizeInBytes(), &ndecoded); + std::memcpy(Samples[sample].sampleb(), data.data() + (data_skip_frames * channels), (data.size() - (data_skip_frames * channels)) * sizeof(int16)); } - mpg123_delete(mh); if(!mo3Decode) { Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); } - return Samples[sample].pSample != nullptr; + return Samples[sample].HasSampleData(); #elif defined(MPT_WITH_MINIMP3) + MPT_UNREFERENCED_PARAMETER(raw); + file.Rewind(); FileReader::PinnedRawDataView rawDataView = file.GetPinnedRawDataView(); int64 bytes_left = rawDataView.size(); @@ -279,43 +597,51 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool mo3Dec std::vector raw_sample_data; - mp3_decoder_t *mp3 = mp3_create(); + mp3dec_t mp3; + std::memset(&mp3, 0, sizeof(mp3dec_t)); + mp3dec_init(&mp3); int rate = 0; int channels = 0; - mp3_info_t info; - int frame_size = 0; + mp3dec_frame_info_t info; + std::memset(&info, 0, sizeof(mp3dec_frame_info_t)); do { - int16 sample_buf[MP3_MAX_SAMPLES_PER_FRAME]; - frame_size = mp3_decode(mp3, const_cast(stream_pos), mpt::saturate_cast(bytes_left), sample_buf, &info); // workaround lack of const qualifier in mp3_decode (all internal functions have the required const correctness) - if(rate != 0 && rate != info.sample_rate) break; // inconsistent stream - if(channels != 0 && channels != info.channels) break; // inconsistent stream - rate = info.sample_rate; - channels = info.channels; - if(rate <= 0) break; // broken stream - if(channels != 1 && channels != 2) break; // broken stream - stream_pos += frame_size; - bytes_left -= frame_size; - if(info.audio_bytes >= 0) + int16 sample_buf[MINIMP3_MAX_SAMPLES_PER_FRAME]; + int frame_samples = mp3dec_decode_frame(&mp3, stream_pos, mpt::saturate_cast(bytes_left), sample_buf, &info); + if(frame_samples < 0 || info.frame_bytes < 0) break; // internal error in minimp3 + if(frame_samples > 0 && info.frame_bytes == 0) break; // internal error in minimp3 + if(frame_samples == 0 && info.frame_bytes == 0) break; // end of stream, no progress + if(frame_samples == 0 && info.frame_bytes > 0) MPT_DO { } MPT_WHILE_0; // decoder skipped non-mp3 data + if(frame_samples > 0 && info.frame_bytes > 0) MPT_DO { } MPT_WHILE_0; // normal + if(info.frame_bytes > 0) { - try + if(rate != 0 && rate != info.hz) break; // inconsistent stream + if(channels != 0 && channels != info.channels) break; // inconsistent stream + rate = info.hz; + channels = info.channels; + if(rate <= 0) break; // broken stream + if(channels != 1 && channels != 2) break; // broken stream + stream_pos += mpt::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); + bytes_left -= mpt::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); + if(frame_samples > 0) { - raw_sample_data.insert(raw_sample_data.end(), sample_buf, sample_buf + (info.audio_bytes / sizeof(int16))); - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); - break; + try + { + raw_sample_data.insert(raw_sample_data.end(), sample_buf, sample_buf + frame_samples * channels); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + break; + } } } if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH) { break; } - } while((bytes_left >= 0) && (frame_size > 0)); - - mp3_free(mp3); + } while(bytes_left > 0); if(rate == 0 || channels == 0 || raw_sample_data.empty()) { @@ -340,9 +666,9 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool mo3Dec Samples[sample].uFlags.set(CHN_STEREO, channels == 2); Samples[sample].AllocateSample(); - if(Samples[sample].pSample != nullptr) + if(Samples[sample].HasSampleData()) { - std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].pSample16); + std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].sample16()); } if(!mo3Decode) @@ -350,12 +676,13 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool mo3Dec Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); } - return Samples[sample].pSample != nullptr; + return Samples[sample].HasSampleData(); #else MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); + MPT_UNREFERENCED_PARAMETER(raw); MPT_UNREFERENCED_PARAMETER(mo3Decode); #endif // MPT_WITH_MPG123 || MPT_WITH_MINIMP3 diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp index eafa3598c..b40a13248 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp @@ -25,6 +25,7 @@ #include "../common/ComponentManager.h" #if defined(MPT_WITH_MEDIAFOUNDATION) #include +#include #include #include #include @@ -38,23 +39,11 @@ OPENMPT_NAMESPACE_BEGIN #if defined(MPT_WITH_MEDIAFOUNDATION) -template -static void mptMFSafeRelease(T **ppT) +struct PropVariant : PROPVARIANT { - if(*ppT) - { - (*ppT)->Release(); - *ppT = NULL; - } -} - -#define MPT_MF_CHECKED(x) MPT_DO { \ - HRESULT hr = (x); \ - if(!SUCCEEDED(hr)) \ - { \ - goto fail; \ - } \ -} MPT_WHILE_0 + PropVariant() { PropVariantInit(this); } + ~PropVariant() { PropVariantClear(this); } +}; // Implementing IMFByteStream is apparently not enough to stream raw bytes // data to MediaFoundation. @@ -77,36 +66,44 @@ static FileTags ReadMFMetadata(IMFMediaSource *mediaSource) FileTags tags; - IMFPresentationDescriptor *presentationDescriptor = NULL; + CComPtr presentationDescriptor; + if(!SUCCEEDED(mediaSource->CreatePresentationDescriptor(&presentationDescriptor))) + { + return tags; + } DWORD streams = 0; - IMFMetadataProvider *metadataProvider = NULL; - IMFMetadata *metadata = NULL; - PROPVARIANT varPropNames; - PropVariantInit(&varPropNames); + if(!SUCCEEDED(presentationDescriptor->GetStreamDescriptorCount(&streams))) + { + return tags; + } + CComPtr metadataProvider; + if(!SUCCEEDED(MFGetService(mediaSource, MF_METADATA_PROVIDER_SERVICE, IID_IMFMetadataProvider, (void**)&metadataProvider))) + { + return tags; + } + CComPtr metadata; + if(!SUCCEEDED(metadataProvider->GetMFMetadata(presentationDescriptor, 0, 0, &metadata))) + { + return tags; + } - MPT_MF_CHECKED(mediaSource->CreatePresentationDescriptor(&presentationDescriptor)); - MPT_MF_CHECKED(presentationDescriptor->GetStreamDescriptorCount(&streams)); - MPT_MF_CHECKED(MFGetService(mediaSource, MF_METADATA_PROVIDER_SERVICE, IID_IMFMetadataProvider, (void**)&metadataProvider)); - MPT_MF_CHECKED(metadataProvider->GetMFMetadata(presentationDescriptor, 0, 0, &metadata)); - - MPT_MF_CHECKED(metadata->GetAllPropertyNames(&varPropNames)); + PropVariant varPropNames; + if(!SUCCEEDED(metadata->GetAllPropertyNames(&varPropNames))) + { + return tags; + } for(DWORD propIndex = 0; propIndex < varPropNames.calpwstr.cElems; ++propIndex) { - - PROPVARIANT propVal; - PropVariantInit(&propVal); - + PropVariant propVal; LPWSTR propName = varPropNames.calpwstr.pElems[propIndex]; if(S_OK != metadata->GetProperty(propName, &propVal)) { - PropVariantClear(&propVal); break; } - std::wstring stringVal; - std::vector wcharVal(256); #if !MPT_OS_WINDOWS_WINRT // WTF, no PropVariantToString() in WinRT + std::vector wcharVal(256); for(;;) { HRESULT hrToString = PropVariantToString(propVal, wcharVal.data(), mpt::saturate_cast(wcharVal.size())); @@ -116,16 +113,13 @@ static FileTags ReadMFMetadata(IMFMediaSource *mediaSource) break; } else if(hrToString == ERROR_INSUFFICIENT_BUFFER) { - wcharVal.resize(wcharVal.size() * 2); + wcharVal.resize(Util::ExponentialGrow(wcharVal.size())); } else { break; } } #endif // !MPT_OS_WINDOWS_WINRT - - PropVariantClear(&propVal); - if(stringVal.length() > 0) { if(propName == std::wstring(L"Author")) tags.artist = mpt::ToUnicode(stringVal); @@ -137,13 +131,6 @@ static FileTags ReadMFMetadata(IMFMediaSource *mediaSource) } } -fail: - - PropVariantClear(&varPropNames); - mptMFSafeRelease(&metadata); - mptMFSafeRelease(&metadataProvider); - mptMFSafeRelease(&presentationDescriptor); - return tags; } @@ -158,7 +145,7 @@ public: { return; } - virtual bool DoInitialize() + bool DoInitialize() override { if(!mpt::Windows::Version::Current().IsAtLeast(mpt::Windows::Version::Win7)) { @@ -166,10 +153,10 @@ public: } #if !MPT_OS_WINDOWS_WINRT if(!(true - && AddLibrary("mf", mpt::LibraryPath::System(MPT_PATHSTRING("mf"))) - && AddLibrary("mfplat", mpt::LibraryPath::System(MPT_PATHSTRING("mfplat"))) - && AddLibrary("mfreadwrite", mpt::LibraryPath::System(MPT_PATHSTRING("mfreadwrite"))) - && AddLibrary("propsys", mpt::LibraryPath::System(MPT_PATHSTRING("propsys"))) + && AddLibrary("mf", mpt::LibraryPath::System(P_("mf"))) + && AddLibrary("mfplat", mpt::LibraryPath::System(P_("mfplat"))) + && AddLibrary("mfreadwrite", mpt::LibraryPath::System(P_("mfreadwrite"))) + && AddLibrary("propsys", mpt::LibraryPath::System(P_("propsys"))) )) { return false; @@ -263,11 +250,11 @@ std::vector CSoundFile::GetMediaFoundationFileTypes() std::wstring guid = std::wstring(valueNameBuf); mpt::ustring description = mpt::ToUnicode(std::wstring(reinterpret_cast(valueData))); - description = mpt::String::Replace(description, MPT_USTRING("Byte Stream Handler"), MPT_USTRING("Files")); - description = mpt::String::Replace(description, MPT_USTRING("ByteStreamHandler"), MPT_USTRING("Files")); + description = mpt::String::Replace(description, U_("Byte Stream Handler"), U_("Files")); + description = mpt::String::Replace(description, U_("ByteStreamHandler"), U_("Files")); guidMap[guid] - .ShortName(MPT_USTRING("mf")) + .ShortName(U_("mf")) .Description(description) ; @@ -322,60 +309,70 @@ bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, file.Rewind(); // When using MF to decode MP3 samples in MO3 files, we need the mp3 file extension // for some of them or otherwise MF refuses to recognize them. - mpt::PathString tmpfileExtension = (mo3Decode ? MPT_PATHSTRING("mp3") : MPT_PATHSTRING("tmp")); + mpt::PathString tmpfileExtension = (mo3Decode ? P_("mp3") : P_("tmp")); OnDiskFileWrapper diskfile(file, tmpfileExtension); if(!diskfile.IsValid()) { return false; } - bool result = false; - - std::vector rawData; - FileTags tags; - std::string sampleName; - - IMFSourceResolver *sourceResolver = NULL; - MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID; - IUnknown *unknownMediaSource = NULL; - IMFMediaSource *mediaSource = NULL; - IMFSourceReader *sourceReader = NULL; - IMFMediaType *uncompressedAudioType = NULL; - IMFMediaType *partialType = NULL; - UINT32 numChannels = 0; - UINT32 samplesPerSecond = 0; - UINT32 bitsPerSample = 0; - - IMFSample *mfSample = NULL; - DWORD mfSampleFlags = 0; - IMFMediaBuffer *buffer = NULL; - - SmpLength length = 0; + #define MPT_MF_CHECKED(x) MPT_DO { \ + HRESULT hr = (x); \ + if(!SUCCEEDED(hr)) \ + { \ + return false; \ + } \ + } MPT_WHILE_0 + CComPtr sourceResolver; MPT_MF_CHECKED(MFCreateSourceResolver(&sourceResolver)); - MPT_MF_CHECKED(sourceResolver->CreateObjectFromURL(diskfile.GetFilename().AsNative().c_str(), MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE | MF_RESOLUTION_READ, NULL, &objectType, &unknownMediaSource)); - if(objectType != MF_OBJECT_MEDIASOURCE) goto fail; + MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID; + CComPtr unknownMediaSource; + MPT_MF_CHECKED(sourceResolver->CreateObjectFromURL(diskfile.GetFilename().ToWide().c_str(), MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE | MF_RESOLUTION_READ, NULL, &objectType, &unknownMediaSource)); + if(objectType != MF_OBJECT_MEDIASOURCE) + { + return false; + } + CComPtr mediaSource; MPT_MF_CHECKED(unknownMediaSource->QueryInterface(&mediaSource)); - tags = ReadMFMetadata(mediaSource); + FileTags tags = ReadMFMetadata(mediaSource); + CComPtr sourceReader; MPT_MF_CHECKED(MFCreateSourceReaderFromMediaSource(mediaSource, NULL, &sourceReader)); + CComPtr partialType; MPT_MF_CHECKED(MFCreateMediaType(&partialType)); MPT_MF_CHECKED(partialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)); MPT_MF_CHECKED(partialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM)); MPT_MF_CHECKED(sourceReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, partialType)); + CComPtr uncompressedAudioType; MPT_MF_CHECKED(sourceReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, &uncompressedAudioType)); MPT_MF_CHECKED(sourceReader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE)); + UINT32 numChannels = 0; MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &numChannels)); + UINT32 samplesPerSecond = 0; MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &samplesPerSecond)); + UINT32 bitsPerSample = 0; MPT_MF_CHECKED(uncompressedAudioType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bitsPerSample)); - if(numChannels <= 0 || numChannels > 2) goto fail; - if(samplesPerSecond <= 0) goto fail; - if(bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) goto fail; + if(numChannels <= 0 || numChannels > 2) + { + return false; + } + if(samplesPerSecond <= 0) + { + return false; + } + if(bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) + { + return false; + } + std::vector rawData; for(;;) { - mfSampleFlags = 0; + CComPtr mfSample; + DWORD mfSampleFlags = 0; + CComPtr buffer; MPT_MF_CHECKED(sourceReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &mfSampleFlags, NULL, &mfSample)); if(mfSampleFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) { @@ -397,23 +394,16 @@ bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, break; } } - mptMFSafeRelease(&buffer); - mptMFSafeRelease(&mfSample); } - mptMFSafeRelease(&uncompressedAudioType); - mptMFSafeRelease(&partialType); - mptMFSafeRelease(&sourceReader); - - sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); + std::string sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); if(rawData.size() / numChannels / (bitsPerSample / 8) > MAX_SAMPLE_LENGTH) { - result = false; - goto fail; + return false; } - length = static_cast(rawData.size() / numChannels / (bitsPerSample/8)); + SmpLength length = mpt::saturate_cast(rawData.size() / numChannels / (bitsPerSample/8)); DestroySampleThreadsafe(sample); if(!mo3Decode) @@ -426,10 +416,9 @@ bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, Samples[sample].uFlags.set(CHN_16BIT, bitsPerSample >= 16); Samples[sample].uFlags.set(CHN_STEREO, numChannels == 2); Samples[sample].AllocateSample(); - if(Samples[sample].pSample == nullptr) + if(!Samples[sample].HasSampleData()) { - result = false; - goto fail; + return false; } if(bitsPerSample == 24) @@ -453,10 +442,10 @@ bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, } else { // just copy - std::copy(rawData.data(), rawData.data() + rawData.size(), mpt::void_cast(Samples[sample].pSample)); + std::copy(rawData.data(), rawData.data() + rawData.size(), mpt::byte_cast(Samples[sample].sampleb())); } - result = true; + #undef MPT_MF_CHECKED if(!mo3Decode) { @@ -464,18 +453,7 @@ bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, Samples[sample].PrecomputeLoops(*this, false); } -fail: - - mptMFSafeRelease(&buffer); - mptMFSafeRelease(&mfSample); - mptMFSafeRelease(&uncompressedAudioType); - mptMFSafeRelease(&partialType); - mptMFSafeRelease(&sourceReader); - mptMFSafeRelease(&mediaSource); - mptMFSafeRelease(&unknownMediaSource); - mptMFSafeRelease(&sourceResolver); - - return result; + return true; #endif diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp index 6f66adf60..83c97a73a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp @@ -77,6 +77,8 @@ bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) int channels = 0; std::vector raw_sample_data; + std::string sampleName; + FileReader initial = file.GetChunk(65536); // 512 is recommended by libopusfile if(op_test(NULL, initial.GetRawData(), initial.GetLength()) != 0) { @@ -106,6 +108,8 @@ bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) channels = 2; } + sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(GetOpusFileTags(of))); + std::vector decodeBuf(120 * 48000 / 1000); // 120ms (max Opus packet), 48kHz bool eof = false; while(!eof) @@ -152,7 +156,7 @@ bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) } DestroySampleThreadsafe(sample); - strcpy(m_szNames[sample], ""); + mpt::String::Copy(m_szNames[sample], sampleName); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = mpt::saturate_cast(raw_sample_data.size() / channels); @@ -165,7 +169,7 @@ bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) return false; } - std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].pSample16); + std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].sample16()); Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp index 78ccf7dab..d0ba12ce2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp @@ -253,7 +253,7 @@ bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) std::size_t offset = 0; int consumed = 0; int error = 0; - stb_vorbis *vorb = stb_vorbis_open_pushdata(data, mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); + stb_vorbis *vorb = stb_vorbis_open_pushdata(mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); file.Skip(consumed); data += consumed; dataLeft -= consumed; @@ -272,7 +272,7 @@ bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) int frame_channels = 0; int decodedSamples = 0; float **output = nullptr; - consumed = stb_vorbis_decode_frame_pushdata(vorb, data, mpt::saturate_cast(dataLeft), &frame_channels, &output, &decodedSamples); + consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &frame_channels, &output, &decodedSamples); file.Skip(consumed); data += consumed; dataLeft -= consumed; @@ -322,7 +322,7 @@ bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) return false; } - std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].pSample16); + std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].sample16()); Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp index 29fe7a58c..968848465 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp @@ -33,27 +33,30 @@ #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" #include "../soundlib/ModSampleCopy.h" +#include OPENMPT_NAMESPACE_BEGIN - bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize, bool includeInstrumentFormats) { if(!nSample || nSample >= MAX_SAMPLES) return false; if(!ReadWAVSample(nSample, file, mayNormalize) && !(includeInstrumentFormats && ReadXISample(nSample, file)) && !(includeInstrumentFormats && ReadITISample(nSample, file)) + && !ReadW64Sample(nSample, file) + && !ReadCAFSample(nSample, file) && !ReadAIFFSample(nSample, file, mayNormalize) && !ReadITSSample(nSample, file) && !(includeInstrumentFormats && ReadPATSample(nSample, file)) && !ReadIFFSample(nSample, file) && !ReadS3ISample(nSample, file) + && !ReadSBISample(nSample, file) && !ReadAUSample(nSample, file, mayNormalize) && !ReadFLACSample(nSample, file) && !ReadOpusSample(nSample, file) && !ReadVorbisSample(nSample, file) - && !ReadMP3Sample(nSample, file) + && !ReadMP3Sample(nSample, file, false) && !ReadMediaFoundationSample(nSample, file) ) { @@ -64,6 +67,10 @@ bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool { m_nSamples = nSample; } + if(Samples[nSample].uFlags[CHN_ADLIB]) + { + InitOPL(); + } return true; } @@ -289,13 +296,13 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & targetSmp = sourceSmp; strcpy(m_szNames[targetSample], srcSong.m_szNames[sourceSample]); - if(sourceSmp.pSample) + if(sourceSmp.HasSampleData()) { - targetSmp.pSample = nullptr; // Don't want to delete the original sample! + targetSmp.pData.pSample = nullptr; // Don't want to delete the original sample! if(targetSmp.AllocateSample()) { SmpLength nSize = sourceSmp.GetSampleSizeInBytes(); - memcpy(targetSmp.pSample, sourceSmp.pSample, nSize); + memcpy(targetSmp.sampleb(), sourceSmp.sampleb(), nSize); targetSmp.PrecomputeLoops(*this, false); } // Remember on-disk path (for MPTM files), but don't implicitely enable on-disk storage @@ -313,7 +320,15 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & } #endif + if(targetSmp.uFlags[CHN_ADLIB] && !SupportsOPL()) + { + AddToLog("OPL instruments are not supported by this format."); + } targetSmp.Convert(srcSong.GetType(), GetType()); + if(targetSmp.uFlags[CHN_ADLIB]) + { + InitOPL(); + } return true; } @@ -368,7 +383,7 @@ static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file { for(uint32 i = 0; i < 8; i++) { - uint8 delta = data[dataPos]; + uint8 delta = mpt::byte_cast(data[dataPos]); if(i & 1) { delta >>= 4; @@ -421,7 +436,7 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo sample.Initialize(); sample.nLength = wavFile.GetSampleLength(); sample.nC5Speed = wavFile.GetSampleRate(); - wavFile.ApplySampleSettings(sample, m_szNames[nSample]); + wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[nSample]); FileReader sampleChunk = wavFile.GetSampleData(); @@ -441,12 +456,12 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo { return false; } - IMAADPCMUnpack16(sample.pSample16, sample.nLength, sampleChunk, wavFile.GetBlockAlign(), wavFile.GetNumChannels()); + IMAADPCMUnpack16(sample.sample16(), sample.nLength, sampleChunk, wavFile.GetBlockAlign(), wavFile.GetNumChannels()); sample.PrecomputeLoops(*this, false); } else if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtMP3) { // MP3 in WAV - bool loadedMP3 = ReadMP3Sample(nSample, sampleChunk, true) || ReadMediaFoundationSample(nSample, sampleChunk, true); + bool loadedMP3 = ReadMP3Sample(nSample, sampleChunk, false, true) || ReadMediaFoundationSample(nSample, sampleChunk, true); if(!loadedMP3) { return false; @@ -521,14 +536,8 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo #ifndef MODPLUG_NO_FILESAVE -bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const +bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const { - mpt::ofstream f(filename, std::ios::binary); - if(!f) - { - return false; - } - WAVWriter file(&f); if(!file.IsValid()) @@ -556,8 +565,8 @@ bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, const mpt::PathString &filen } FileTags tags; + tags.SetEncoder(); tags.title = mpt::ToUnicode(GetCharsetInternal(), m_szNames[nSample]); - tags.encoder = mpt::ToUnicode(mpt::CharsetUTF8, MptVersion::GetOpenMPTVersionStr()); file.WriteMetatags(tags); return true; @@ -566,19 +575,253 @@ bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, const mpt::PathString &filen #endif // MODPLUG_NO_FILESAVE + +///////////////// +// Sony Wave64 // + + +struct Wave64FileHeader +{ + GUIDms GuidRIFF; + uint64le FileSize; + GUIDms GuidWAVE; +}; + +MPT_BINARY_STRUCT(Wave64FileHeader, 40) + + +struct Wave64ChunkHeader +{ + GUIDms GuidChunk; + uint64le Size; +}; + +MPT_BINARY_STRUCT(Wave64ChunkHeader, 24) + + +struct Wave64Chunk +{ + Wave64ChunkHeader header; + + FileReader::off_t GetLength() const + { + uint64 length = header.Size; + if(length < sizeof(Wave64ChunkHeader)) + { + length = 0; + } else + { + length -= sizeof(Wave64ChunkHeader); + } + return mpt::saturate_cast(length); + } + + mpt::UUID GetID() const + { + return mpt::UUID(header.GuidChunk); + } +}; + +MPT_BINARY_STRUCT(Wave64Chunk, 24) + + +static void Wave64TagFromLISTINFO(mpt::ustring & dst, uint16 codePage, const ChunkReader::ChunkList & infoChunk, RIFFChunk::ChunkIdentifiers id) +{ + if(!infoChunk.ChunkExists(id)) + { + return; + } + FileReader textChunk = infoChunk.GetChunk(id); + if(!textChunk.IsValid()) + { + return; + } + std::string str; + textChunk.ReadString(str, textChunk.GetLength()); + str = mpt::String::Replace(str, std::string("\r\n"), std::string("\n")); + str = mpt::String::Replace(str, std::string("\r"), std::string("\n")); + dst = mpt::ToUnicode(codePage, mpt::CharsetWindows1252, str); +} + + +bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize) +{ + file.Rewind(); + + constexpr mpt::UUID guidRIFF = "66666972-912E-11CF-A5D6-28DB04C10000"_uuid; + constexpr mpt::UUID guidWAVE = "65766177-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; + + constexpr mpt::UUID guidLIST = "7473696C-912F-11CF-A5D6-28DB04C10000"_uuid; + constexpr mpt::UUID guidFMT = "20746D66-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; + //constexpr mpt::UUID guidFACT = "74636166-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; + constexpr mpt::UUID guidDATA = "61746164-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; + //constexpr mpt::UUID guidLEVL = "6C76656C-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; + //constexpr mpt::UUID guidJUNK = "6b6E756A-ACF3-11D3-8CD1-00C04f8EDB8A"_uuid; + //constexpr mpt::UUID guidBEXT = "74786562-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; + //constexpr mpt::UUID guiMARKER = "ABF76256-392D-11D2-86C7-00C04F8EDB8A"_uuid; + //constexpr mpt::UUID guiSUMMARYLIST = "925F94BC-525A-11D2-86DC-00C04F8EDB8A"_uuid; + + constexpr mpt::UUID guidCSET = "54455343-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid; + + Wave64FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(mpt::UUID(fileHeader.GuidRIFF) != guidRIFF) + { + return false; + } + if(mpt::UUID(fileHeader.GuidWAVE) != guidWAVE) + { + return false; + } + if(fileHeader.FileSize != file.GetLength()) + { + return false; + } + + ChunkReader chunkFile = file; + auto chunkList = chunkFile.ReadChunks(8); + + if(!chunkList.ChunkExists(guidFMT)) + { + return false; + } + FileReader formatChunk = chunkList.GetChunk(guidFMT); + WAVFormatChunk format; + if(!formatChunk.ReadStruct(format)) + { + return false; + } + uint16 sampleFormat = format.format; + if(format.format == WAVFormatChunk::fmtExtensible) + { + WAVFormatChunkExtension formatExt; + if(!formatChunk.ReadStruct(formatExt)) + { + return false; + } + sampleFormat = static_cast(mpt::UUID(formatExt.subFormat).GetData1()); + } + if(format.sampleRate == 0) + { + return false; + } + if(format.numChannels == 0) + { + return false; + } + if(format.numChannels > 2) + { + return false; + } + if(sampleFormat != WAVFormatChunk::fmtPCM && sampleFormat != WAVFormatChunk::fmtFloat) + { + return false; + } + if(sampleFormat == WAVFormatChunk::fmtFloat && format.bitsPerSample != 32 && format.bitsPerSample != 64) + { + return false; + } + if(sampleFormat == WAVFormatChunk::fmtPCM && format.bitsPerSample > 64) + { + return false; + } + + SampleIO::Bitdepth bitDepth; + switch((format.bitsPerSample - 1) / 8u) + { + default: + case 0: bitDepth = SampleIO::_8bit ; break; + case 1: bitDepth = SampleIO::_16bit; break; + case 2: bitDepth = SampleIO::_24bit; break; + case 3: bitDepth = SampleIO::_32bit; break; + case 7: bitDepth = SampleIO::_64bit; break; + } + SampleIO sampleIO( + bitDepth, + (format.numChannels > 1) ? SampleIO::stereoInterleaved : SampleIO::mono, + SampleIO::littleEndian, + (sampleFormat == WAVFormatChunk::fmtFloat) ? SampleIO::floatPCM : SampleIO::signedPCM); + if(format.bitsPerSample <= 8) + { + sampleIO |= SampleIO::unsignedPCM; + } + if(mayNormalize) + { + sampleIO.MayNormalize(); + } + + FileTags tags; + + uint16 codePage = 28591; // mpt::CharsetISO8859_1 + FileReader csetChunk = chunkList.GetChunk(guidCSET); + if(csetChunk.IsValid()) + { + if(csetChunk.CanRead(2)) + { + codePage = csetChunk.ReadUint16LE(); + } + } + + if(chunkList.ChunkExists(guidLIST)) + { + ChunkReader listChunk = chunkList.GetChunk(guidLIST); + if(listChunk.ReadMagic("INFO")) + { + auto infoChunk = listChunk.ReadChunks(2); + Wave64TagFromLISTINFO(tags.title, codePage, infoChunk, RIFFChunk::idINAM); + Wave64TagFromLISTINFO(tags.encoder, codePage, infoChunk, RIFFChunk::idISFT); + //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICOP); + Wave64TagFromLISTINFO(tags.artist, codePage, infoChunk, RIFFChunk::idIART); + Wave64TagFromLISTINFO(tags.album, codePage, infoChunk, RIFFChunk::idIPRD); + Wave64TagFromLISTINFO(tags.comments, codePage, infoChunk, RIFFChunk::idICMT); + //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idIENG); + //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idISBJ); + Wave64TagFromLISTINFO(tags.genre, codePage, infoChunk, RIFFChunk::idIGNR); + //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICRD); + Wave64TagFromLISTINFO(tags.year, codePage, infoChunk, RIFFChunk::idYEAR); + Wave64TagFromLISTINFO(tags.trackno, codePage, infoChunk, RIFFChunk::idTRCK); + Wave64TagFromLISTINFO(tags.url, codePage, infoChunk, RIFFChunk::idTURL); + //Wave64TagFromLISTINFO(tags.bpm, codePage, infoChunk, void); + } + } + + if(!chunkList.ChunkExists(guidDATA)) + { + return false; + } + FileReader audioData = chunkList.GetChunk(guidDATA); + + SmpLength length = mpt::saturate_cast(audioData.GetLength() / (sampleIO.GetEncodedBitsPerSample()/8)); + + ModSample &mptSample = Samples[nSample]; + DestroySampleThreadsafe(nSample); + mptSample.Initialize(); + mptSample.nLength = length; + mptSample.nC5Speed = format.sampleRate; + + sampleIO.ReadSample(mptSample, audioData); + + mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + + mptSample.Convert(MOD_TYPE_IT, GetType()); + mptSample.PrecomputeLoops(*this, false); + + return true; + +} + + + #ifndef MODPLUG_NO_FILESAVE /////////////////////////////////////////////////////////////// // Save RAW -bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const +bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, std::ostream &f) const { - mpt::ofstream f(filename, std::ios::binary); - if(!f) - { - return false; - } - const ModSample &sample = Samples[nSample]; SampleIO( sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit, @@ -699,7 +942,7 @@ static double PatchFreqToNote(uint32 nFreq) static int32 PatchFreqToNoteInt(uint32 nFreq) { - return Util::Round(PatchFreqToNote(nFreq)); + return mpt::saturate_round(PatchFreqToNote(nFreq)); } @@ -885,8 +1128,8 @@ bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file) S3MSampleHeader sampleHeader; if(!file.ReadStruct(sampleHeader) - || sampleHeader.sampleType != S3MSampleHeader::typePCM - || memcmp(sampleHeader.magic, "SCRS", 4) + || (sampleHeader.sampleType != S3MSampleHeader::typePCM && sampleHeader.sampleType != S3MSampleHeader::typeAdMel) + || (memcmp(sampleHeader.magic, "SCRS", 4) && memcmp(sampleHeader.magic, "SCRI", 4)) || !file.Seek((sampleHeader.dataPointer[1] << 4) | (sampleHeader.dataPointer[2] << 12) | (sampleHeader.dataPointer[0] << 20))) { return false; @@ -897,12 +1140,73 @@ bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file) ModSample &sample = Samples[nSample]; sampleHeader.ConvertToMPT(sample); mpt::String::Read(m_szNames[nSample], sampleHeader.name); - sampleHeader.GetSampleFormat(false).ReadSample(sample, file); + + if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) + sampleHeader.GetSampleFormat(false).ReadSample(sample, file); + else if(SupportsOPL()) + InitOPL(); + else + AddToLog("OPL instruments are not supported by this format."); + sample.Convert(MOD_TYPE_S3M, GetType()); sample.PrecomputeLoops(*this, false); return true; } +#ifndef MODPLUG_NO_FILESAVE + +bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const +{ + const ModSample &sample = Samples[smp]; + S3MSampleHeader sampleHeader; + MemsetZero(sampleHeader); + SmpLength length = sampleHeader.ConvertToS3M(sample); + mpt::String::Write(sampleHeader.name, m_szNames[smp]); + mpt::String::Write(sampleHeader.reserved2, mpt::ToCharset(mpt::CharsetUTF8, Version::Current().GetOpenMPTVersionString())); + if(length) + sampleHeader.dataPointer[1] = sizeof(S3MSampleHeader) >> 4; + mpt::IO::Write(f, sampleHeader); + if(length) + sampleHeader.GetSampleFormat(false).WriteSample(f, sample, length); + + return true; +} + +#endif // MODPLUG_NO_FILESAVE + + +///////////////////////////////////////////////////////////// +// SBI OPL patch files + +bool CSoundFile::ReadSBISample(SAMPLEINDEX sample, FileReader &file) +{ + file.Rewind(); + if(!file.ReadMagic("SBI\x1A") + || !file.CanRead(32 + sizeof(OPLPatch)) + || file.CanRead(64)) // Arbitrary threshold to reject files that are unlikely to be SBI files + return false; + + if(!SupportsOPL()) + { + AddToLog("OPL instruments are not supported by this format."); + return true; + } + + DestroySampleThreadsafe(sample); + InitOPL(); + + ModSample &mptSmp = Samples[sample]; + mptSmp.Initialize(MOD_TYPE_S3M); + file.ReadString(m_szNames[sample], 32); + OPLPatch patch; + file.ReadArray(patch); + mptSmp.SetAdlib(true, patch); + + mptSmp.Convert(MOD_TYPE_S3M, GetType()); + return true; +} + + ///////////////////////////////////////////////////////////// // XI Instruments @@ -1002,10 +1306,9 @@ bool CSoundFile::ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) } } - pIns->Convert(MOD_TYPE_XM, GetType()); - // Read MPT crap ReadExtendedInstrumentProperties(pIns, file); + pIns->Convert(MOD_TYPE_XM, GetType()); pIns->Sanitize(GetType()); return true; } @@ -1013,16 +1316,10 @@ bool CSoundFile::ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) #ifndef MODPLUG_NO_FILESAVE -bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString &filename) const +bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const { ModInstrument *pIns = Instruments[nInstr]; - if(pIns == nullptr || filename.empty()) - { - return false; - } - - FILE *f; - if((f = mpt_fopen(filename, "wb")) == nullptr) + if(pIns == nullptr) { return false; } @@ -1038,7 +1335,7 @@ bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString header.instrument.ApplyAutoVibratoToXM(Samples[samples[0]], GetType()); } - fwrite(&header, 1, sizeof(XIInstrumentHeader), f); + mpt::IO::Write(f, header); std::vector sampleFlags(samples.size()); @@ -1057,7 +1354,7 @@ bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString mpt::String::Write(xmSample.name, m_szNames[samples[i]]); - fwrite(&xmSample, 1, sizeof(xmSample), f); + mpt::IO::Write(f, xmSample); } // XI Sample Data @@ -1070,12 +1367,9 @@ bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString } // Write 'MPTX' extension tag - char code[4]; - memcpy(code, "XTPM", 4); - fwrite(code, 1, 4, f); + mpt::IO::WriteText(f, "XTPM"); WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header. - fclose(f); return true; } @@ -1199,7 +1493,7 @@ struct SFZEnvelope static EnvelopeNode::tick_t ToTicks(float duration, float tickDuration) { - return std::max(EnvelopeNode::tick_t(1), Util::Round(duration / tickDuration)); + return std::max(EnvelopeNode::tick_t(1), mpt::saturate_round(duration / tickDuration)); } EnvelopeNode::value_t ToValue(float value, EnvelopeType envType) const @@ -1211,7 +1505,7 @@ struct SFZEnvelope value += ENVELOPE_MID; } Limit(value, ENVELOPE_MIN, ENVELOPE_MAX); - return Util::Round(value); + return mpt::saturate_round(value); } void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType) const @@ -1294,7 +1588,7 @@ struct SFZRegion int8 finetune = 0; uint8 keyLo = 0, keyHi = 127, keyRoot = 60; uint8 resonance = 0; // 0...40dB - uint8 filterType = FLTMODE_UNCHANGED; + InstrFilterMode filterType = FLTMODE_UNCHANGED; uint8 polyphony = 255; bool useSampleKeyRoot = false; bool invertPhase = false; @@ -1305,7 +1599,7 @@ struct SFZRegion double valueF = ConvertStrTo(valueStr); MPT_CONSTANT_IF(std::numeric_limits::is_integer) { - valueF = Util::Round(valueF); + valueF = mpt::round(valueF); } Limit(valueF, static_cast(valueMin), static_cast(valueMax)); value = static_cast(valueF); @@ -1551,7 +1845,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) } } else if(s.substr(0, 9) == "#include " || s.substr(0, 9) == "#include\t") { - AddToLog(LogWarning, MPT_USTRING("#include directive is not supported.")); + AddToLog(LogWarning, U_("#include directive is not supported.")); auto fileStart = s.find("\"", 9); // Yes, there can be arbitrary characters before the opening quote, at least that's how sforzando does it. auto fileEnd = s.find("\"", fileStart + 1); if(fileStart != std::string::npos && fileEnd != std::string::npos) @@ -1663,7 +1957,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) FileReader smpFile = GetFileReader(f); if(!ReadSampleFromFile(smp, smpFile, false)) { - AddToLog(LogWarning, MPT_USTRING("Unable to load sample: ") + filename.ToUnicode()); + AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); prevSmp--; continue; } @@ -1676,7 +1970,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) mpt::String::Copy(m_szNames[smp], filename.GetFileName().ToLocale()); } } - sample.uFlags.set(SMP_KEEPONDISK, sample.pSample != nullptr); + sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData()); if(region.useSampleKeyRoot) { @@ -1708,7 +2002,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) pIns->nNNA = NNA_NOTEOFF; if(region.polyphony == 1) { - pIns->nDNA = NNA_NOTECUT; + pIns->nDNA = DNA_NOTECUT; pIns->nDCT = DCT_SAMPLE; } region.ampEnv.ConvertToMPT(pIns, *this, ENV_VOLUME); @@ -1716,7 +2010,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) //region.filterEnv.ConvertToMPT(pIns, *this, ENV_PITCH); sample.rootNote = region.keyRoot + NOTE_MIN; - sample.nGlobalVol = Util::Round(64 * std::pow(10.0, region.volume / 20.0)); + sample.nGlobalVol = mpt::saturate_round(64 * std::pow(10.0, region.volume / 20.0)); if(region.panning != -128) { sample.nPan = static_cast(Util::muldivr_unsigned(region.panning + 100, 256, 200)); @@ -1728,7 +2022,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) { sample.nVibSweep = 255; if(region.pitchLfoFade > 0) - sample.nVibSweep = Util::Round(255 / region.pitchLfoFade); + sample.nVibSweep = mpt::saturate_round(255 / region.pitchLfoFade); sample.nVibDepth = static_cast(Util::muldivr(region.pitchLfoDepth, 32, 100)); sample.nVibRate = region.pitchLfoFreq * 4; } @@ -1791,7 +2085,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) } // Loop cross-fade - SmpLength fadeSamples = Util::Round(region.loopCrossfade * origSampleRate); + SmpLength fadeSamples = mpt::saturate_round(region.loopCrossfade * origSampleRate); LimitMax(fadeSamples, sample.uFlags[CHN_SUSTAINLOOP] ? sample.nSustainStart : sample.nLoopStart); if(fadeSamples > 0) { @@ -1803,7 +2097,7 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) if(region.offset && region.offset < sample.nLength) { auto offset = region.offset * sample.GetBytesPerSample(); - memmove(sample.pSample8, sample.pSample8 + offset, sample.nLength * sample.GetBytesPerSample() - offset); + memmove(sample.sampleb(), sample.sampleb() + offset, sample.nLength * sample.GetBytesPerSample() - offset); if(region.end > region.offset) region.end -= region.offset; sample.nLength -= region.offset; @@ -1827,6 +2121,166 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) pIns->Convert(MOD_TYPE_MPT, GetType()); return true; } + +#ifndef MODPLUG_NO_FILESAVE + +static double SFZLinear2dB(double volume) +{ + return (volume > 0.0 ? 20.0 * std::log10(volume) : -144.0); +} + +bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const +{ +#ifdef MODPLUG_TRACKER + const mpt::FlushMode flushMode = mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave); +#else + const mpt::FlushMode flushMode = mpt::FlushMode::Full; +#endif + const ModInstrument *ins = Instruments[nInstr]; + if(ins == nullptr) + { + return false; + } + const mpt::PathString sampleBaseName = filename.GetFileName(); + const mpt::PathString sampleDirName = sampleBaseName + P_("/"); + const mpt::PathString sampleBasePath = filename.GetPath() + sampleDirName; + if(!sampleBasePath.IsDirectory() && !::CreateDirectory(sampleBasePath.AsNative().c_str(), nullptr)) + { + return false; + } + + if(strcmp(ins->name, "")) + { + f << "// Name: " << mpt::ToCharset(mpt::CharsetUTF8, GetCharsetInternal(), ins->name) << "\n"; + } + f << "// Created with " << mpt::ToCharset(mpt::CharsetUTF8, Version::Current().GetOpenMPTVersionString()) << "\n\n"; + + f << "\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n"; + f << ""; + f << "\nbend_up=" << ins->midiPWD * 100; + if(ins->IsCutoffEnabled()) + { + f << "\ncutoff=" << CSoundFile::CutOffToFrequency(ins->GetCutoff()); + } + if(ins->IsResonanceEnabled()) + { + f << "\nresonance=" << Util::muldivr_unsigned(ins->GetResonance(), 24, 128); + } + if(ins->IsCutoffEnabled() || ins->IsResonanceEnabled()) + { + f << "\nfil_type=" << (ins->nFilterMode == FLTMODE_HIGHPASS ? "hpf_2p" : "lpf_2p"); + } + if(ins->dwFlags[INS_SETPANNING]) + { + f << "\npan=" << (Util::muldivr_unsigned(ins->nPan, 200, 256) - 100); + } + if(ins->nGlobalVol != 64) + { + f << "\nvolume=" << SFZLinear2dB(ins->nGlobalVol / 64.0); + } + + size_t numSamples = 0; + for(size_t i = 0; i < mpt::size(ins->Keyboard); i++) + { + if(ins->Keyboard[i] < 1 || ins->Keyboard[i] > GetNumSamples()) + continue; + + numSamples++; + size_t endOfRegion = i + 1; + while(endOfRegion < mpt::size(ins->Keyboard)) + { + if(ins->Keyboard[endOfRegion] != ins->Keyboard[i] || ins->NoteMap[endOfRegion] != (ins->NoteMap[i] + endOfRegion - i)) + break; + endOfRegion++; + } + endOfRegion--; + + mpt::PathString sampleName = sampleBasePath + sampleBaseName + P_(" ") + mpt::PathString::FromUnicode(mpt::ufmt::val(numSamples)); + if(useFLACsamples) + sampleName += P_(".flac"); + else + sampleName += P_(".wav"); + + try + { + mpt::SafeOutputFile fSmp(sampleName, std::ios::binary, flushMode); + if(fSmp) + { + //fSmp.exceptions(fSmp.exceptions() | std::ios::badbit | std::ios::failbit); + + if(useFLACsamples) + SaveFLACSample(ins->Keyboard[i], fSmp); + else + SaveWAVSample(ins->Keyboard[i], fSmp); + } + } catch(const std::exception &) + { + AddToLog(LogError, MPT_USTRING("Unable to save sample: ") + sampleName.ToUnicode()); + } + + const ModSample &sample = Samples[ins->Keyboard[i]]; + f << "\n\n"; + if(strcmp(m_szNames[ins->Keyboard[i]], "")) + { + f << "\nregion_label=" << mpt::ToCharset(mpt::CharsetUTF8, GetCharsetInternal(), m_szNames[ins->Keyboard[i]]); + } + f << "\nsample=" << sampleName.GetFullFileName().ToUTF8(); + f << "\nlokey=" << i; + f << "\nhikey=" << endOfRegion; + if(sample.rootNote != NOTE_NONE) + { + f << "\npitch_keycenter=" << sample.rootNote - NOTE_MIN; + } else + { + f << "\npitch_keycenter=" << NOTE_MIDDLEC + i - ins->NoteMap[i]; + } + if(sample.uFlags[CHN_PANNING]) + { + f << "\npan=" << (Util::muldivr_unsigned(sample.nPan, 200, 256) - 100); + } + if(sample.nGlobalVol != 64) + { + f << "\nvolume=" << SFZLinear2dB((ins->nGlobalVol * sample.nGlobalVol) / 4096.0); + } + const char *loopMode; + SmpLength loopStart = 0, loopEnd = 0; + bool loopType = false; + if(sample.uFlags[CHN_SUSTAINLOOP]) + { + loopMode = "loop_sustain"; + loopStart = sample.nSustainStart; + loopEnd = sample.nSustainEnd; + loopType = sample.uFlags[CHN_PINGPONGSUSTAIN]; + } else if(sample.uFlags[CHN_LOOP]) + { + loopMode = "loop_continuous"; + loopStart = sample.nLoopStart; + loopEnd = sample.nLoopEnd; + loopType = sample.uFlags[CHN_SUSTAINLOOP]; + // TODO backward loop (supported by engine but no UI) + } else + { + loopMode = "no_loop"; + } + f << "\nloop_mode=" << loopMode; + if(loopStart < loopEnd) + { + f << "\nloop_start=" << loopStart; + f << "\nloop_end=" << loopEnd; + f << "\nloop_type=" << (loopType ? "alternate" : "forward"); + } + if(sample.uFlags.test_all(CHN_SUSTAINLOOP | CHN_LOOP)) + { + f << "\n// Warning: Only sustain loop was exported!"; + } + i = endOfRegion; + } + + return true; +} + +#endif // MODPLUG_NO_FILESAVE + #else bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX, FileReader &) { @@ -1835,6 +2289,319 @@ bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX, FileReader &) #endif // MPT_EXTERNAL_SAMPLES + +/////////////// +// Apple CAF // + + +struct CAFFileHeader +{ + uint32be mFileType; + uint16be mFileVersion; + uint16be mFileFlags; +}; + +MPT_BINARY_STRUCT(CAFFileHeader, 8) + + +struct CAFChunkHeader +{ + uint32be mChunkType; + int64be mChunkSize; +}; + +MPT_BINARY_STRUCT(CAFChunkHeader, 12) + + +struct CAFChunk +{ + enum ChunkIdentifiers + { + iddesc = MagicBE("desc"), + iddata = MagicBE("data"), + idstrg = MagicBE("strg"), + idinfo = MagicBE("info") + }; + + CAFChunkHeader header; + + FileReader::off_t GetLength() const + { + int64 length = header.mChunkSize; + if(length == -1) + { + length = std::numeric_limits::max(); // spec + } + if(length < 0) + { + length = std::numeric_limits::max(); // heuristic + } + return mpt::saturate_cast(length); + } + + ChunkIdentifiers GetID() const + { + return static_cast(header.mChunkType.get()); + } +}; + +MPT_BINARY_STRUCT(CAFChunk, 12) + + +enum { + CAFkAudioFormatLinearPCM = MagicBE("lpcm"), + CAFkAudioFormatAppleIMA4 = MagicBE("ima4"), + CAFkAudioFormatMPEG4AAC = MagicBE("aac "), + CAFkAudioFormatMACE3 = MagicBE("MAC3"), + CAFkAudioFormatMACE6 = MagicBE("MAC6"), + CAFkAudioFormatULaw = MagicBE("ulaw"), + CAFkAudioFormatALaw = MagicBE("alaw"), + CAFkAudioFormatMPEGLayer1 = MagicBE(".mp1"), + CAFkAudioFormatMPEGLayer2 = MagicBE(".mp2"), + CAFkAudioFormatMPEGLayer3 = MagicBE(".mp3"), + CAFkAudioFormatAppleLossless = MagicBE("alac") +}; + + +enum { + CAFkCAFLinearPCMFormatFlagIsFloat = (1L << 0), + CAFkCAFLinearPCMFormatFlagIsLittleEndian = (1L << 1) +}; + + +struct CAFAudioFormat +{ + float64be mSampleRate; + uint32be mFormatID; + uint32be mFormatFlags; + uint32be mBytesPerPacket; + uint32be mFramesPerPacket; + uint32be mChannelsPerFrame; + uint32be mBitsPerChannel; +}; + +MPT_BINARY_STRUCT(CAFAudioFormat, 32) + + +static void CAFSetTagFromInfoKey(mpt::ustring & dst, const std::map & infoMap, const std::string & key) +{ + auto item = infoMap.find(key); + if(item == infoMap.end()) + { + return; + } + if(item->second.empty()) + { + return; + } + dst = mpt::ToUnicode(mpt::CharsetUTF8, item->second); +} + + +bool CSoundFile::ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize) +{ + file.Rewind(); + + CAFFileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) + { + return false; + } + if(fileHeader.mFileType != MagicBE("caff")) + { + return false; + } + if(fileHeader.mFileVersion != 1) + { + return false; + } + + ChunkReader chunkFile = file; + auto chunkList = chunkFile.ReadChunks(0); + + CAFAudioFormat audioFormat; + if(!chunkList.GetChunk(CAFChunk::iddesc).ReadStruct(audioFormat)) + { + return false; + } + if(audioFormat.mSampleRate <= 0.0) + { + return false; + } + if(audioFormat.mChannelsPerFrame == 0) + { + return false; + } + if(audioFormat.mChannelsPerFrame > 2) + { + return false; + } + + if(!Util::TypeCanHoldValue(mpt::saturate_round(audioFormat.mSampleRate))) + { + return false; + } + uint32 sampleRate = static_cast(mpt::saturate_round(audioFormat.mSampleRate)); + if(sampleRate <= 0) + { + return false; + } + + SampleIO sampleIO; + if(audioFormat.mFormatID == CAFkAudioFormatLinearPCM) + { + if(audioFormat.mFramesPerPacket != 1) + { + return false; + } + if(audioFormat.mBytesPerPacket == 0) + { + return false; + } + if(audioFormat.mBitsPerChannel == 0) + { + return false; + } + if(audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat) + { + if(audioFormat.mBitsPerChannel != 32 && audioFormat.mBitsPerChannel != 64) + { + return false; + } + if(audioFormat.mBytesPerPacket != audioFormat.mChannelsPerFrame * audioFormat.mBitsPerChannel/8) + { + return false; + } + } + if(audioFormat.mBytesPerPacket % audioFormat.mChannelsPerFrame != 0) + { + return false; + } + if(audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 1 + && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 2 + && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 3 + && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 4 + && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 8 + ) + { + return false; + } + SampleIO::Channels channels = (audioFormat.mChannelsPerFrame == 2) ? SampleIO::stereoInterleaved : SampleIO::mono; + SampleIO::Endianness endianness = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsLittleEndian) ? SampleIO::littleEndian : SampleIO::bigEndian; + SampleIO::Encoding encoding = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat) ? SampleIO::floatPCM : SampleIO::signedPCM; + SampleIO::Bitdepth bitdepth = static_cast((audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame) * 8); + sampleIO = SampleIO(bitdepth, channels, endianness, encoding); + } else + { + return false; + } + + if(mayNormalize) + { + sampleIO.MayNormalize(); + } + + /* + std::map stringMap; // UTF-8 + if(chunkList.ChunkExists(CAFChunk::idstrg)) + { + FileReader stringsChunk = chunkList.GetChunk(CAFChunk::idstrg); + uint32 numEntries = stringsChunk.ReadUint32BE(); + if(stringsChunk.Skip(12 * numEntries)) + { + FileReader stringData = stringsChunk.ReadChunk(stringsChunk.BytesLeft()); + stringsChunk.Seek(4); + for(uint32 entry = 0; entry < numEntries && stringsChunk.CanRead(12); entry++) + { + uint32 stringID = stringsChunk.ReadUint32BE(); + int64 offset = stringsChunk.ReadIntBE(); + if(offset >= 0 && Util::TypeCanHoldValue(offset)) + { + stringData.Seek(mpt::saturate_cast(offset)); + std::string str; + if(stringData.ReadNullString(str)) + { + stringMap[stringID] = str; + } + } + } + } + } + */ + + std::map infoMap; // UTF-8 + if(chunkList.ChunkExists(CAFChunk::idinfo)) + { + FileReader informationChunk = chunkList.GetChunk(CAFChunk::idinfo); + uint32 numEntries = informationChunk.ReadUint32BE(); + for(uint32 entry = 0; entry < numEntries && informationChunk.CanRead(2); entry++) + { + std::string key; + std::string value; + if(!informationChunk.ReadNullString(key)) + { + break; + } + if(!informationChunk.ReadNullString(value)) + { + break; + } + if(!key.empty() && !value.empty()) + { + infoMap[key] = value; + } + } + } + FileTags tags; + CAFSetTagFromInfoKey(tags.bpm, infoMap, "tempo"); + //CAFSetTagFromInfoKey(void, infoMap, "key signature"); + //CAFSetTagFromInfoKey(void, infoMap, "time signature"); + CAFSetTagFromInfoKey(tags.artist, infoMap, "artist"); + CAFSetTagFromInfoKey(tags.album, infoMap, "album"); + CAFSetTagFromInfoKey(tags.trackno, infoMap, "track number"); + CAFSetTagFromInfoKey(tags.year, infoMap, "year"); + //CAFSetTagFromInfoKey(void, infoMap, "composer"); + //CAFSetTagFromInfoKey(void, infoMap, "lyricist"); + CAFSetTagFromInfoKey(tags.genre, infoMap, "genre"); + CAFSetTagFromInfoKey(tags.title, infoMap, "title"); + //CAFSetTagFromInfoKey(void, infoMap, "recorded date"); + CAFSetTagFromInfoKey(tags.comments, infoMap, "comments"); + //CAFSetTagFromInfoKey(void, infoMap, "copyright"); + //CAFSetTagFromInfoKey(void, infoMap, "source encoder"); + CAFSetTagFromInfoKey(tags.encoder, infoMap, "encoding application"); + //CAFSetTagFromInfoKey(void, infoMap, "nominal bit rate"); + //CAFSetTagFromInfoKey(void, infoMap, "channel layout"); + //CAFSetTagFromInfoKey(tags.url, infoMap, void); + + if(!chunkList.ChunkExists(CAFChunk::iddata)) + { + return false; + } + FileReader dataChunk = chunkList.GetChunk(CAFChunk::iddata); + dataChunk.Skip(4); // edit count + FileReader audioData = dataChunk.ReadChunk(dataChunk.BytesLeft()); + + SmpLength length = mpt::saturate_cast((audioData.GetLength() / audioFormat.mBytesPerPacket) * audioFormat.mFramesPerPacket); + + ModSample &mptSample = Samples[nSample]; + DestroySampleThreadsafe(nSample); + mptSample.Initialize(); + mptSample.nLength = length; + mptSample.nC5Speed = sampleRate; + + sampleIO.ReadSample(mptSample, audioData); + + mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + + mptSample.Convert(MOD_TYPE_IT, GetType()); + mptSample.PrecomputeLoops(*this, false); + + return true; + +} + + + ///////////////////////////////////////////////////////////////////////////////////////// // AIFF File I/O @@ -1855,11 +2622,11 @@ struct AIFFChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idCOMM = MAGIC4BE('C','O','M','M'), - idSSND = MAGIC4BE('S','S','N','D'), - idINST = MAGIC4BE('I','N','S','T'), - idMARK = MAGIC4BE('M','A','R','K'), - idNAME = MAGIC4BE('N','A','M','E'), + idCOMM = MagicBE("COMM"), + idSSND = MagicBE("SSND"), + idINST = MagicBE("INST"), + idMARK = MagicBE("MARK"), + idNAME = MagicBE("NAME"), }; uint32be id; // See ChunkIdentifiers @@ -2125,6 +2892,53 @@ bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayN } +static bool AUIsAnnotationLineWithField(const std::string & line) +{ + std::size_t pos = line.find('='); + if(pos == std::string::npos) + { + return false; + } + if(pos == 0) + { + return false; + } + std::string field = line.substr(0, pos); + bool invalidChars = false; + for(auto c : field) + { + if(!IsInRange(c, 'a', 'z') && !IsInRange(c, 'A', 'Z') && !IsInRange(c, '0', '9') && c != '-' && c != '_') + { + invalidChars = true; + } + } + if(invalidChars) + { + return false; + } + return true; +} + +static std::string AUTrimFieldFromAnnotationLine(const std::string & line) +{ + if(!AUIsAnnotationLineWithField(line)) + { + return line; + } + std::size_t pos = line.find('='); + return line.substr(pos + 1); +} + +static std::string AUGetAnnotationFieldFromLine(const std::string & line) +{ + if(!AUIsAnnotationLineWithField(line)) + { + return std::string(); + } + std::size_t pos = line.find('='); + return line.substr(0, pos); +} + bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize) { file.Rewind(); @@ -2133,12 +2947,20 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor if(!file.ReadMagic(".snd")) return false; - uint32 dataOffset = file.ReadUint32BE(); + uint32 dataOffset = file.ReadUint32BE(); // must be divisible by 8 according to spec, however, there are files that ignore this requirement uint32 dataSize = file.ReadUint32BE(); uint32 encoding = file.ReadUint32BE(); uint32 sampleRate = file.ReadUint32BE(); uint32 channels = file.ReadUint32BE(); + // According to spec, a minimum 8 byte annotation field after the header fields is required, + // however, there are files in the wild that violate this requirement. + // Thus, check for 24 instead of 32 here. + if(dataOffset < 24) // data offset points inside header + { + return false; + } + if(channels < 1 || channels > 2) return false; @@ -2162,8 +2984,64 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor default: return false; } - if(!file.Seek(dataOffset)) + if(!file.LengthIsAtLeast(dataOffset)) + { return false; + } + + FileTags tags; + + // This reads annotation metadata as written by OpenMPT, sox, ffmpeg. + // Additionally, we fall back to just reading the whole field as a single comment. + file.Seek(24); + std::vector annotationData; + file.ReadVector(annotationData, dataOffset - 24); + std::string annotation(annotationData.begin(), annotationData.end()); + annotation = mpt::String::RTrim(annotation, std::string(1, '\0')); + std::size_t term = annotation.find(std::string(1, '\0')); + if(term != std::string::npos) + { // only up to first \0 byte + annotation = annotation.substr(0, term); + } + annotation = mpt::String::Replace(annotation, "\r\n", "\n"); + annotation = mpt::String::Replace(annotation, "\r", "\n"); + mpt::Charset charset = mpt::IsUTF8(annotation) ? mpt::CharsetUTF8 : mpt::CharsetISO8859_1; + std::vector lines = mpt::String::Split(annotation, "\n"); + bool has_fields = false; + for(const auto & line : lines) + { + if(AUIsAnnotationLineWithField(line)) + { + has_fields = true; + } + } + if(has_fields) + { + std::map> lines_per_field; + std::string last_field = "comment"; + for(const auto & line : lines) + { + if(AUIsAnnotationLineWithField(line)) + { + last_field = mpt::ToLowerCaseAscii(mpt::String::Trim(AUGetAnnotationFieldFromLine(line))); + } + lines_per_field[last_field].push_back(AUTrimFieldFromAnnotationLine(line)); + } + tags.title = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["title" ], std::string("\n"))); + tags.artist = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["artist" ], std::string("\n"))); + tags.album = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["album" ], std::string("\n"))); + tags.trackno = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["track" ], std::string("\n"))); + tags.genre = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["genre" ], std::string("\n"))); + tags.comments = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["comment"], std::string("\n"))); + } else + { + // Most applications tend to write their own name here, + // thus there is little use in interpreting the string as a title. + annotation = mpt::String::RTrim(annotation, std::string("\r\n")); + tags.comments = mpt::ToUnicode(charset, annotation); + } + + file.Seek(dataOffset); ModSample &mptSample = Samples[nSample]; DestroySampleThreadsafe(nSample); @@ -2173,7 +3051,7 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor LimitMax(length, dataSize); mptSample.nLength = (length * 8u) / (sampleIO.GetEncodedBitsPerSample() * channels); mptSample.nC5Speed = sampleRate; - strcpy(m_szNames[nSample], ""); + mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); if(mayNormalize) { @@ -2211,9 +3089,19 @@ bool CSoundFile::ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewin file.Seek(sampleHeader.ConvertToMPT(sample)); mpt::String::Read(m_szNames[nSample], sampleHeader.name); - if(!sample.uFlags[SMP_KEEPONDISK]) + if(sample.uFlags[CHN_ADLIB]) { - sampleHeader.GetSampleFormat().ReadSample(Samples[nSample], file); + OPLPatch patch; + file.ReadArray(patch); + sample.SetAdlib(true, patch); + InitOPL(); + if(!SupportsOPL()) + { + AddToLog("OPL instruments are not supported by this format."); + } + } else if(!sample.uFlags[SMP_KEEPONDISK]) + { + sampleHeader.GetSampleFormat().ReadSample(sample, file); } else { // External sample @@ -2232,7 +3120,7 @@ bool CSoundFile::ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewin } if(!LoadExternalSample(nSample, filename)) { - AddToLog(LogWarning, MPT_USTRING("Unable to load sample: ") + filename.ToUnicode()); + AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); } } else { @@ -2334,13 +3222,13 @@ bool CSoundFile::ReadITIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) } } - pIns->Convert(MOD_TYPE_IT, GetType()); - if(file.Seek(extraOffset)) { // Read MPT crap ReadExtendedInstrumentProperties(pIns, file); } + + pIns->Convert(MOD_TYPE_IT, GetType()); pIns->Sanitize(GetType()); return true; @@ -2349,14 +3237,12 @@ bool CSoundFile::ReadITIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) #ifndef MODPLUG_NO_FILESAVE -bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString &filename, bool compress, bool allowExternal) const +bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool compress, bool allowExternal) const { ITInstrument iti; ModInstrument *pIns = Instruments[nInstr]; - FILE *f; - if((!pIns) || filename.empty()) return false; - if((f = mpt_fopen(filename, "wb")) == nullptr) return false; + if((!pIns) || (filename.empty() && allowExternal)) return false; auto instSize = iti.ConvertToIT(*pIns, false, *this); @@ -2411,11 +3297,11 @@ bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString { #ifdef MPT_EXTERNAL_SAMPLES const std::string filenameU8 = GetSamplePath(smp).AbsolutePathToRelative(filename.GetPath()).ToUTF8(); - const size_t strSize = mpt::saturate_cast(filenameU8.size()); + const size_t strSize = filenameU8.size(); size_t intBytes = 0; if(mpt::IO::WriteVarInt(f, strSize, &intBytes)) { - filePos += intBytes + strSize; + filePos += mpt::saturate_cast(intBytes + strSize); mpt::IO::WriteRaw(f, filenameU8.data(), strSize); } #endif // MPT_EXTERNAL_SAMPLES @@ -2428,7 +3314,6 @@ bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString mpt::IO::WriteRaw(f, "XTPM", 4); WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header. - fclose(f); return true; } @@ -2455,9 +3340,9 @@ struct IFFChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idVHDR = MAGIC4BE('V','H','D','R'), - idBODY = MAGIC4BE('B','O','D','Y'), - idNAME = MAGIC4BE('N','A','M','E'), + idVHDR = MagicBE("VHDR"), + idBODY = MagicBE("BODY"), + idNAME = MagicBE("NAME"), }; uint32be id; // See ChunkIdentifiers diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp index 9d6ccae41..bb371b21d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp @@ -21,15 +21,14 @@ #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif -#include +#include "BitReader.h" OPENMPT_NAMESPACE_BEGIN // Sample decompression routines in other source files void AMSUnpack(const int8 * const source, size_t sourceSize, void * const dest, const size_t destSize, char packCharacter); -uint8 MDLReadBits(uint32 &bitbuf, int32 &bitnum, const uint8 *(&ibuf), size_t &bytesLeft, int8 n); -uintptr_t DMFUnpack(uint8 *psample, const uint8 *ibuf, const uint8 *ibufmax, uint32 maxlen); +uintptr_t DMFUnpack(FileReader &file, uint8 *psample, uint32 maxlen); // Read a sample from memory @@ -59,15 +58,9 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const fileSize = restrictedSampleDataView.size(); } else { - // Only DMF sample compression encoding should fall in this case, - MPT_ASSERT(GetEncoding() == DMF); - // file is guaranteed by the caller to be ONLY data for this sample, - // it is thus efficient to create a view to the whole file object. - // See MPT_ASSERT with fileSize below. - restrictedSampleDataView = file.GetPinnedRawDataView(); - sourceBuf = restrictedSampleDataView.data(); - fileSize = restrictedSampleDataView.size(); + MPT_ASSERT_NOTREACHED(); } + if(!IsVariableLengthEncoded() && sample.nLength > 0x40000) { // Limit sample length to available bytes in file to avoid excessive memory allocation. @@ -94,6 +87,19 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const maxLength /= encodedBytesPerSample; } LimitMax(sample.nLength, mpt::saturate_cast(maxLength)); + } else if(GetEncoding() == IT214 || GetEncoding() == IT215 || GetEncoding() == MDL || GetEncoding() == DMF) + { + // In the best case, IT compression represents each sample point as a single bit. + // In practice, there is of course the two-byte header per compressed block and the initial bit width change. + // As a result, if we have a file length of n, we know that the sample can be at most n*8 sample points long. + // For DMF, there are at least two bits per sample, and for MDL at least 5 (so both are worse than IT). + size_t maxLength = fileSize; + uint8 maxSamplesPerByte = 8 / GetNumChannels(); + if(Util::MaxValueOfType(maxLength) / maxSamplesPerByte >= maxLength) + maxLength *= maxSamplesPerByte; + else + maxLength = Util::MaxValueOfType(maxLength); + LimitMax(sample.nLength, mpt::saturate_cast(maxLength)); } if(sample.nLength < 1) @@ -126,7 +132,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const LimitMax(readLength, file.BytesLeft()); const uint8 *inBuf = mpt::byte_cast(sourceBuf) + sizeof(compressionTable); - int8 *outBuf = sample.pSample8; + int8 *outBuf = sample.sample8(); int8 delta = 0; for(size_t i = readLength; i != 0; i--) @@ -158,7 +164,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const LimitMax(sourceSize, mpt::saturate_cast(packedDataView.size())); bytesRead += sourceSize; - AMSUnpack(reinterpret_cast(packedDataView.data()), packedDataView.size(), sample.pSample, sample.GetSampleSizeInBytes(), packCharacter); + AMSUnpack(reinterpret_cast(packedDataView.data()), packedDataView.size(), sample.samplev(), sample.GetSampleSizeInBytes(), packCharacter); } } else if(GetEncoding() == PTM8Dto16 && GetChannelFormat() == mono && GetBitDepth() == 16) { @@ -169,16 +175,8 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const // Huffman MDL compressed samples if(file.CanRead(8) && (fileSize = file.ReadUint32LE()) >= 4) { - FileReader chunk = file.ReadChunk(fileSize); + BitReader chunk = file.ReadChunk(fileSize); bytesRead = chunk.GetLength() + 4; - uint32 bitBuf = chunk.ReadUint32LE(); - int32 bitNum = 32; - - restrictedSampleDataView = chunk.GetPinnedRawDataView(); - sourceBuf = restrictedSampleDataView.data(); - - const uint8 *inBuf = reinterpret_cast(sourceBuf); - size_t bytesLeft = chunk.BytesLeft(); uint8 dlt = 0, lowbyte = 0; const bool is16bit = GetBitDepth() == 16; @@ -189,20 +187,20 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const uint8 hibyte; if(is16bit) { - lowbyte = MDLReadBits(bitBuf, bitNum, inBuf, bytesLeft, 8); + lowbyte = static_cast(chunk.ReadBits(8)); } - bool sign = MDLReadBits(bitBuf, bitNum, inBuf, bytesLeft, 1) != 0; - if(MDLReadBits(bitBuf, bitNum, inBuf, bytesLeft, 1)) + bool sign = chunk.ReadBits(1) != 0; + if(chunk.ReadBits(1)) { - hibyte = MDLReadBits(bitBuf, bitNum, inBuf, bytesLeft, 3); + hibyte = static_cast(chunk.ReadBits(3)); } else { hibyte = 8; - while(!MDLReadBits(bitBuf, bitNum, inBuf, bytesLeft, 1)) + while(!chunk.ReadBits(1)) { hibyte += 0x10; } - hibyte += MDLReadBits(bitBuf, bitNum, inBuf, bytesLeft, 4); + hibyte += static_cast(chunk.ReadBits(4)); } if(sign) { @@ -211,14 +209,13 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const dlt += hibyte; if(!is16bit) { - sample.pSample8[j] = dlt; - } - else + sample.sample8()[j] = dlt; + } else { - sample.pSample16[j] = lowbyte | (dlt << 8); + sample.sample16()[j] = lowbyte | (dlt << 8); } } - } catch(const std::range_error &) + } catch(const BitReader::eof &) { // Data is not sufficient to decode the whole sample //AddToLog(LogWarning, "Truncated MDL sample block"); @@ -229,17 +226,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const // DMF Huffman compression if(fileSize > 4) { - const uint8 *inBuf = mpt::byte_cast(sourceBuf); - const uint8 *inBufMax = inBuf + fileSize; - uint8 *outBuf = static_cast(sample.pSample); - bytesRead = DMFUnpack(outBuf, inBuf, inBufMax, sample.GetSampleSizeInBytes()); - - // This assertion ensures that, when using variable length samples, - // the caller actually provided a trimmed chunk to read the sample data from. - // This is required as we cannot know the encoded sample data size upfront - // to construct a properly sized pinned view. - MPT_ASSERT(bytesRead == fileSize); - + bytesRead = DMFUnpack(file, mpt::byte_cast(sample.sampleb()), sample.GetSampleSizeInBytes()); } #ifdef MODPLUG_TRACKER } else if((GetEncoding() == uLaw || GetEncoding() == aLaw) && GetBitDepth() == 16 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved)) @@ -317,14 +304,14 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const 944, 912, 1008, 976, 816, 784, 880, 848, }; - const int16 *lut = GetEncoding() == uLaw ? uLawTable : aLawTable; + const auto &lut = GetEncoding() == uLaw ? uLawTable : aLawTable; SmpLength readLength = sample.nLength * GetNumChannels(); - LimitMax(readLength, file.BytesLeft()); + LimitMax(readLength, mpt::saturate_cast(file.BytesLeft())); bytesRead = readLength; const uint8 *inBuf = mpt::byte_cast(sourceBuf); - int16 *outBuf = sample.pSample16; + int16 *outBuf = sample.sample16(); while(readLength--) { @@ -375,13 +362,14 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const bytesRead = CopyStereoSplitSample(sample, sourceBuf, fileSize); break; case deltaPCM: // 8-Bit / Stereo Split / Delta / PCM + case MT2: // same as deltaPCM, but right channel is stored as a difference from the left channel bytesRead = CopyStereoSplitSample(sample, sourceBuf, fileSize); - break; - case MT2: // same as deltaPCM, but right channel is stored as a difference from the left channel - bytesRead = CopyStereoSplitSample(sample, sourceBuf, fileSize); - for(SmpLength i = 0; i < sample.nLength * 2; i += 2) + if(GetEncoding() == MT2) { - sample.pSample8[i + 1] = static_cast(static_cast(sample.pSample8[i + 1]) + static_cast(sample.pSample8[i])); + for(int8 *p = sample.sample8(), *pEnd = p + sample.nLength * 2; p < pEnd; p += 2) + { + p[1] = static_cast(static_cast(p[0]) + static_cast(p[1])); + } } break; default: @@ -467,13 +455,14 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); break; case deltaPCM: // 16-Bit / Stereo Split / Delta / PCM - bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); - break; case MT2: // same as deltaPCM, but right channel is stored as a difference from the left channel bytesRead = CopyStereoSplitSample >(sample, sourceBuf, fileSize); - for(SmpLength i = 0; i < sample.nLength * 2; i += 2) + if(GetEncoding() == MT2) { - sample.pSample16[i + 1] = static_cast(static_cast(sample.pSample16[i + 1]) + static_cast(sample.pSample16[i])); + for(int16 *p = sample.sample16(), *pEnd = p + sample.nLength * 2; p < pEnd; p += 2) + { + p[1] = static_cast(static_cast(p[0]) + static_cast(p[1])); + } } break; default: @@ -733,7 +722,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const if(bytesRead && srcPeak != 1.0f) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. - sample.nGlobalVol = Util::Round(Clamp(sample.nGlobalVol * srcPeak, 1.0f, 64.0f)); + sample.nGlobalVol = mpt::saturate_round(Clamp(sample.nGlobalVol * srcPeak, 1.0f, 64.0f)); sample.uFlags.set(SMP_MODIFIED); } } @@ -754,7 +743,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const if(bytesRead && srcPeak != 1.0) { // Adjust sample volume so we do not affect relative volume of the sample. Normalizing is only done to increase precision. - sample.nGlobalVol = Util::Round(Clamp(sample.nGlobalVol * srcPeak, 1.0f, 64.0f)); + sample.nGlobalVol = mpt::saturate_round(Clamp(sample.nGlobalVol * srcPeak, 1.0, 64.0)); sample.uFlags.set(SMP_MODIFIED); } } @@ -860,30 +849,36 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const // Write a sample to file -size_t SampleIO::WriteSample(std::ostream *f, const ModSample &sample, SmpLength maxSamples) const +size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength maxSamples) const { - if(!sample.HasSampleData()) return 0; - - union + if(sample.uFlags[CHN_ADLIB]) { - int8 buffer8[8192]; - int16 buffer16[4096]; - }; - const void *const pSampleVoid = sample.pSample; - const int8 *const pSample8 = sample.pSample8; - const int16 *const pSample16 = sample.pSample16; + mpt::IO::Write(f, sample.adlib); + return sizeof(sample.adlib); + } + if(!sample.HasSampleData()) + { + return 0; + } + + std::array writeBuffer; + mpt::IO::WriteBuffer fb{f, mpt::as_span(writeBuffer)}; + SmpLength numSamples = sample.nLength; - if(maxSamples && numSamples > maxSamples) numSamples = maxSamples; + if(maxSamples && numSamples > maxSamples) + { + numSamples = maxSamples; + } - size_t len = CalculateEncodedSize(numSamples), bufcount = 0; + std::size_t len = CalculateEncodedSize(numSamples); if(GetBitDepth() == 16 && GetChannelFormat() == mono && GetEndianness() == littleEndian && (GetEncoding() == signedPCM || GetEncoding() == unsignedPCM || GetEncoding() == deltaPCM)) { // 16-bit little-endian mono samples MPT_ASSERT(len == numSamples * 2); - if(!f) return len; + const int16 *const pSample16 = sample.sample16(); const int16 *p = pSample16; int s_old = 0; const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x8000 : 0; @@ -899,20 +894,13 @@ size_t SampleIO::WriteSample(std::ostream *f, const ModSample &sample, SmpLength } if(GetEncoding() == deltaPCM) { - buffer16[bufcount] = SwapBytesLE((int16)(s_new - s_old)); + mpt::IO::Write(fb, mpt::as_le(static_cast(s_new - s_old))); s_old = s_new; } else { - buffer16[bufcount] = SwapBytesLE((int16)(s_new + s_ofs)); - } - bufcount++; - if(bufcount >= mpt::size(buffer16)) - { - mpt::IO::WriteRaw(*f, reinterpret_cast(buffer16), bufcount * 2); - bufcount = 0; + mpt::IO::Write(fb, mpt::as_le(static_cast(s_new + s_ofs))); } } - if (bufcount) mpt::IO::WriteRaw(*f, reinterpret_cast(buffer16), bufcount * 2); } else if(GetBitDepth() == 8 && GetChannelFormat() == stereoSplit && @@ -920,33 +908,25 @@ size_t SampleIO::WriteSample(std::ostream *f, const ModSample &sample, SmpLength { // 8-bit Stereo samples (not interleaved) MPT_ASSERT(len == numSamples * 2); - if(!f) return len; + const int8 *const pSample8 = sample.sample8(); const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; for (uint32 iCh=0; iCh<2; iCh++) { const int8 *p = pSample8 + iCh; int s_old = 0; - - bufcount = 0; - for (uint32 j=0; j(s_new - s_old)); s_old = s_new; } else { - buffer8[bufcount++] = (int8)(s_new + s_ofs); - } - if(bufcount >= mpt::size(buffer8)) - { - mpt::IO::WriteRaw(*f, reinterpret_cast(buffer8), bufcount); - bufcount = 0; + mpt::IO::Write(fb, static_cast(s_new + s_ofs)); } } - if (bufcount) mpt::IO::WriteRaw(*f, reinterpret_cast(buffer8), bufcount); } } @@ -955,65 +935,79 @@ size_t SampleIO::WriteSample(std::ostream *f, const ModSample &sample, SmpLength { // 16-bit little-endian Stereo samples (not interleaved) MPT_ASSERT(len == numSamples * 4); - if(!f) return len; + const int16 *const pSample16 = sample.sample16(); const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x8000 : 0; for (uint32 iCh=0; iCh<2; iCh++) { const int16 *p = pSample16 + iCh; int s_old = 0; - - bufcount = 0; - for (SmpLength j=0; j(s_new - s_old))); s_old = s_new; } else { - buffer16[bufcount] = SwapBytesLE((int16)(s_new + s_ofs)); - } - bufcount++; - if(bufcount >= mpt::size(buffer16)) - { - mpt::IO::WriteRaw(*f, reinterpret_cast(buffer16), bufcount * 2); - bufcount = 0; + mpt::IO::Write(fb, mpt::as_le(static_cast(s_new + s_ofs))); } } - if (bufcount) mpt::IO::WriteRaw(*f, reinterpret_cast(buffer16), bufcount * 2); } } - else if((GetBitDepth() == 8 || (GetBitDepth() == 16 && GetEndianness() == GetNativeEndianness())) && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) + else if(GetBitDepth() == 8 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM) { // Stereo signed interleaved - if(f) mpt::IO::WriteRaw(*f, mpt::void_cast(pSampleVoid), len); + MPT_ASSERT(len == numSamples * 2); + const int8 *const pSample8 = sample.sample8(); + mpt::IO::WriteRaw(f, reinterpret_cast(pSample8), len); + } + + else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM && GetEndianness() == littleEndian) + { + // Stereo signed interleaved + MPT_ASSERT(len == numSamples * 4); + const int16 *const pSample16 = sample.sample16(); + const int16 *p = pSample16; + for(SmpLength j = 0; j < numSamples; j++) + { + mpt::IO::Write(fb, mpt::as_le(p[0])); + mpt::IO::Write(fb, mpt::as_le(p[1])); + p += 2; + } + } + + else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM && GetEndianness() == bigEndian) + { + // Stereo signed interleaved + MPT_ASSERT(len == numSamples * 4); + const int16 *const pSample16 = sample.sample16(); + const int16 *p = pSample16; + for(SmpLength j = 0; j < numSamples; j++) + { + mpt::IO::Write(fb, mpt::as_be(p[0])); + mpt::IO::Write(fb, mpt::as_be(p[1])); + p += 2; + } } else if(GetBitDepth() == 8 && GetChannelFormat() == stereoInterleaved && GetEncoding() == unsignedPCM) { // Stereo unsigned interleaved MPT_ASSERT(len == numSamples * 2); - if(!f) return len; - for(SmpLength j = 0; j < len; j++) + const int8 *const pSample8 = sample.sample8(); + for(SmpLength j = 0; j < numSamples * 2; j++) { - buffer8[bufcount] = (int8)((uint8)(pSample8[j]) + 0x80); - bufcount++; - if(bufcount >= mpt::size(buffer8)) - { - mpt::IO::WriteRaw(*f, reinterpret_cast(buffer8), bufcount); - bufcount = 0; - } + mpt::IO::Write(fb, static_cast(static_cast(pSample8[j]) + 0x80)); } - if (bufcount) mpt::IO::WriteRaw(*f, reinterpret_cast(buffer8), bufcount); } else if(GetEncoding() == IT214 || GetEncoding() == IT215) { // IT2.14-encoded samples - ITCompression its(sample, GetEncoding() == IT215, f, numSamples); + ITCompression its(sample, GetEncoding() == IT215, &f, numSamples); len = its.GetCompressedSize(); } @@ -1022,60 +1016,61 @@ size_t SampleIO::WriteSample(std::ostream *f, const ModSample &sample, SmpLength { MPT_ASSERT(GetBitDepth() == 8); MPT_ASSERT(len == numSamples); - if(!f) return len; - const int8 *p = pSample8; - int sinc = sample.GetElementarySampleSize(); - int s_old = 0; - const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; - MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) + if(sample.uFlags[CHN_16BIT]) { - if (sample.uFlags[CHN_16BIT]) p++; - } - - for (SmpLength j = 0; j < len; j++) + const int16 *const pSample16 = sample.sample16(); + const int16 *p = pSample16; + int s_old = 0; + const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; + for(SmpLength j = 0; j < numSamples; j++) + { + int s_new = mpt::rshift_signed(*p, 8); + p++; + if(sample.uFlags[CHN_STEREO]) + { + s_new = (s_new + (static_cast(*p)) + 1) / 2; + p++; + } + if(GetEncoding() == deltaPCM) + { + mpt::IO::Write(fb, static_cast(s_new - s_old)); + s_old = s_new; + } else + { + mpt::IO::Write(fb, static_cast(s_new + s_ofs)); + } + } + } else { - int s_new = (int8)(*p); - p += sinc; - if (sample.uFlags[CHN_STEREO]) + const int8 *const pSample8 = sample.sample8(); + const int8 *p = pSample8; + int s_old = 0; + const int s_ofs = (GetEncoding() == unsignedPCM) ? 0x80 : 0; + for(SmpLength j = 0; j < numSamples; j++) { - s_new = (s_new + ((int)*p) + 1) / 2; - p += sinc; - } - if (GetEncoding() == deltaPCM) - { - buffer8[bufcount++] = (int8)(s_new - s_old); - s_old = s_new; - } else - { - buffer8[bufcount++] = (int8)(s_new + s_ofs); - } - if(bufcount >= mpt::size(buffer8)) - { - mpt::IO::WriteRaw(*f, reinterpret_cast(buffer8), bufcount); - bufcount = 0; + int s_new = *p; + p++; + if(sample.uFlags[CHN_STEREO]) + { + s_new = (s_new + (static_cast(*p)) + 1) / 2; + p++; + } + if(GetEncoding() == deltaPCM) + { + mpt::IO::Write(fb, static_cast(s_new - s_old)); + s_old = s_new; + } else + { + mpt::IO::Write(fb, static_cast(s_new + s_ofs)); + } } } - if (bufcount) mpt::IO::WriteRaw(*f, reinterpret_cast(buffer8), bufcount); } + return len; } -// Write a sample to file -size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength maxSamples) const -{ - return WriteSample(&f, sample, maxSamples); -} - - -// Write a sample to file -size_t SampleIO::WriteSample(FILE *f, const ModSample &sample, SmpLength maxSamples) const -{ - mpt::FILE_ostream s(f); - return WriteSample(f ? &s : nullptr, sample, maxSamples); -} - - #endif // MODPLUG_NO_FILESAVE diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h index 23b2101af..48014021b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h @@ -12,6 +12,8 @@ #pragma once +#include "BuildSettings.h" + #include "../common/FileReaderFwd.h" @@ -24,27 +26,9 @@ struct ModSample; // Sample import / export formats class SampleIO { -protected: - typedef uint32 format_type; - format_type format; - - // Internal bitmasks - enum Offsets - { - bitOffset = 0, - channelOffset = 8, - endianOffset = 16, - encodingOffset = 24, - - bitMask = 0xFF << bitOffset, - channelMask = 0xFF << channelOffset, - endianMask = 0xFF << endianOffset, - encodingMask = 0x7F << encodingOffset, // 0xff will overflow signed 32bit int, which is the base type for an enum that fits, causing warnings when shifted - }; - public: // Bits per sample - enum Bitdepth + enum Bitdepth : uint8 { _8bit = 8, _16bit = 16, @@ -54,22 +38,22 @@ public: }; // Number of channels + channel format - enum Channels + enum Channels : uint8 { - mono = 0, + mono = 1, stereoInterleaved, // LRLRLR... stereoSplit, // LLL...RRR... }; // Sample byte order - enum Endianness + enum Endianness : uint8 { littleEndian = 0, bigEndian = 1, }; // Sample encoding - enum Encoding + enum Encoding : uint8 { signedPCM = 0, // Integer PCM, signed unsignedPCM, // Integer PCM, unsigned @@ -92,56 +76,45 @@ public: aLaw, // 8-to-16 bit G.711 a-law compression }; - SampleIO(Bitdepth bits = _8bit, Channels channels = mono, Endianness endianness = littleEndian, Encoding encoding = signedPCM) - { - format = (bits << bitOffset) | (channels << channelOffset) | (endianness << endianOffset) | (encoding << encodingOffset); - } +protected: + Bitdepth m_bitdepth; + Channels m_channels; + Endianness m_endianness; + Encoding m_encoding; - SampleIO(const SampleIO &other) : format(other.format) { } +public: + constexpr SampleIO(Bitdepth bits = _8bit, Channels channels = mono, Endianness endianness = littleEndian, Encoding encoding = signedPCM) + : m_bitdepth(bits), m_channels(channels), m_endianness(endianness), m_encoding(encoding) + { } bool operator== (const SampleIO &other) const { - return format == other.format; + return memcmp(this, &other, sizeof(*this)) == 0; } + bool operator!= (const SampleIO &other) const { - return !(*this == other); + return memcmp(this, &other, sizeof(*this)) != 0; } void operator|= (Bitdepth bits) { - format = (format & ~bitMask) | (bits << bitOffset); + m_bitdepth = bits; } void operator|= (Channels channels) { - format = (format & ~channelMask) | (channels << channelOffset); + m_channels = channels; } void operator|= (Endianness endianness) { - format = (format & ~endianMask) | (endianness << endianOffset); + m_endianness = endianness; } void operator|= (Encoding encoding) { - format = (format & ~encodingMask) | (encoding << encodingOffset); - } - - static inline Endianness GetNativeEndianness() - { - const mpt::endian_type endian = mpt::endian(); - MPT_ASSERT((endian == mpt::endian_little) || (endian == mpt::endian_big)); - Endianness result = littleEndian; - MPT_MAYBE_CONSTANT_IF(endian == mpt::endian_little) - { - result = littleEndian; - } - MPT_MAYBE_CONSTANT_IF(endian == mpt::endian_big) - { - result = bigEndian; - } - return result; + m_encoding = encoding; } void MayNormalize() @@ -150,136 +123,111 @@ public: { if(GetEncoding() == SampleIO::signedPCM) { - (*this) |= SampleIO::signedPCMnormalize; + m_encoding = SampleIO::signedPCMnormalize; } else if(GetEncoding() == SampleIO::floatPCM) { - (*this) |= SampleIO::floatPCMnormalize; + m_encoding = SampleIO::floatPCMnormalize; } } } // Return 0 in case of variable-length encoded samples. - uint8 GetEncodedBitsPerSample() const + MPT_CONSTEXPR14_FUN uint8 GetEncodedBitsPerSample() const { - uint8 result = 0; switch(GetEncoding()) { - case signedPCM:// Integer PCM, signed - result = GetBitDepth(); - break; - case unsignedPCM://Integer PCM, unsigned - result = GetBitDepth(); - break; - case deltaPCM:// Integer PCM, delta-encoded - result = GetBitDepth(); - break; - case floatPCM:// Floating point PCM - result = GetBitDepth(); - break; - case IT214:// Impulse Tracker 2.14 compressed - result = 0; // variable-length compressed - break; - case IT215:// Impulse Tracker 2.15 compressed - result = 0; // variable-length compressed - break; - case AMS:// AMS / Velvet Studio packed - result = 0; // variable-length compressed - break; - case DMF:// DMF Huffman compression - result = 0; // variable-length compressed - break; - case MDL:// MDL Huffman compression - result = 0; // variable-length compressed - break; - case PTM8Dto16:// PTM 8-Bit delta value -> 16-Bit sample - result = 16; - break; - case PCM7to8:// 8-Bit sample data with unused high bit - result = 8; - break; - case ADPCM:// 4-Bit ADPCM-packed - result = 4; - break; - case MT2:// MadTracker 2 stereo delta encoding - result = GetBitDepth(); - break; - case floatPCM15:// Floating point PCM with 2^15 full scale - result = GetBitDepth(); - break; - case floatPCM23:// Floating point PCM with 2^23 full scale - result = GetBitDepth(); - break; - case floatPCMnormalize:// Floating point PCM and data will be normalized while reading - result = GetBitDepth(); - break; - case signedPCMnormalize:// Integer PCM and data will be normalized while reading - result = GetBitDepth(); - break; - case uLaw:// G.711 u-law - result = 8; - break; - case aLaw:// G.711 a-law - result = 8; - break; + case signedPCM: // Integer PCM, signed + case unsignedPCM: //Integer PCM, unsigned + case deltaPCM: // Integer PCM, delta-encoded + case floatPCM: // Floating point PCM + case MT2: // MadTracker 2 stereo delta encoding + case floatPCM15: // Floating point PCM with 2^15 full scale + case floatPCM23: // Floating point PCM with 2^23 full scale + case floatPCMnormalize: // Floating point PCM and data will be normalized while reading + case signedPCMnormalize: // Integer PCM and data will be normalized while reading + return GetBitDepth(); + + case IT214: // Impulse Tracker 2.14 compressed + case IT215: // Impulse Tracker 2.15 compressed + case AMS: // AMS / Velvet Studio packed + case DMF: // DMF Huffman compression + case MDL: // MDL Huffman compression + return 0; // variable-length compressed + + case PTM8Dto16: // PTM 8-Bit delta value -> 16-Bit sample + return 16; + case PCM7to8: // 8-Bit sample data with unused high bit + return 8; + case ADPCM: // 4-Bit ADPCM-packed + return 4; + case uLaw: // G.711 u-law + return 8; + case aLaw: // G.711 a-law + return 8; + + default: + return 0; } - return result; } // Return the static header size additional to the raw encoded sample data. - std::size_t GetEncodedHeaderSize() const + MPT_CONSTEXPR14_FUN std::size_t GetEncodedHeaderSize() const { - std::size_t result = 0; - if(GetEncoding() == ADPCM) + switch(GetEncoding()) { - result = 16; + case ADPCM: + return 16; + default: + return 0; } - return result; } // Returns true if the encoded size cannot be calculated apriori from the encoding format and the sample length. - bool IsVariableLengthEncoded() const + MPT_CONSTEXPR14_FUN bool IsVariableLengthEncoded() const { return GetEncodedBitsPerSample() == 0; } - bool UsesFileReaderForDecoding() const + // Returns true if the decoder for a given format uses FileReader interface and thus do not need to call GetPinnedRawDataView() + MPT_CONSTEXPR14_FUN bool UsesFileReaderForDecoding() const { - if(GetEncoding() == IT214 || GetEncoding() == IT215) + switch(GetEncoding()) { - // IT compressed samples use FileReader interface and thus do not need to call GetPinnedRawDataView() + case IT214: + case IT215: + case AMS: + case DMF: + case MDL: return true; + default: + return false; } - if(GetEncoding() == AMS || GetEncoding() == MDL) - { - return true; - } - return false; } // Get bits per sample - uint8 GetBitDepth() const + constexpr uint8 GetBitDepth() const { - return static_cast((format & bitMask) >> bitOffset); + return static_cast(m_bitdepth); } // Get channel layout - Channels GetChannelFormat() const + constexpr Channels GetChannelFormat() const { - return static_cast((format & channelMask) >> channelOffset); + return m_channels; } // Get number of channels - uint8 GetNumChannels() const + constexpr uint8 GetNumChannels() const { return GetChannelFormat() == mono ? 1u : 2u; } // Get sample byte order - Endianness GetEndianness() const + constexpr Endianness GetEndianness() const { - return static_cast((format & endianMask) >> endianOffset); + return m_endianness; } // Get sample format / encoding - Encoding GetEncoding() const + constexpr Encoding GetEncoding() const { - return static_cast((format & encodingMask) >> encodingOffset); + return m_encoding; } // Returns the encoded size of the sample. In case of variable-length encoding returns 0. @@ -289,24 +237,21 @@ public: { return 0; } - if(GetEncodedBitsPerSample() % 8 != 0) + uint8 bps = GetEncodedBitsPerSample(); + if(bps % 8u != 0) { - MPT_ASSERT(GetEncoding() == ADPCM && GetEncodedBitsPerSample() == 4); + MPT_ASSERT(GetEncoding() == ADPCM && bps == 4); return GetEncodedHeaderSize() + (((length + 1) / 2) * GetNumChannels()); // round up } - return GetEncodedHeaderSize() + (length * (GetEncodedBitsPerSample()/8) * GetNumChannels()); + return GetEncodedHeaderSize() + (length * (bps / 8) * GetNumChannels()); } // Read a sample from memory size_t ReadSample(ModSample &sample, FileReader &file) const; #ifndef MODPLUG_NO_FILESAVE - // Optionally write a sample to file - size_t WriteSample(std::ostream *f, const ModSample &sample, SmpLength maxSamples = 0) const; // Write a sample to file size_t WriteSample(std::ostream &f, const ModSample &sample, SmpLength maxSamples = 0) const; - // Write a sample to file - size_t WriteSample(FILE *f, const ModSample &sample, SmpLength maxSamples = 0) const; #endif // MODPLUG_NO_FILESAVE }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h index 7d15847d6..838fee642 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h @@ -11,7 +11,8 @@ #pragma once -#include "../common/typedefs.h" +#include "BuildSettings.h" + #include "../common/FlagSet.h" @@ -39,65 +40,63 @@ typedef uint8 SEQUENCEINDEX; typedef uint32 SmpLength; -const SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in *frames* - // Note: Sample size in bytes can be more than this (= 256 MB). +const SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in frames. Sample size in bytes can be more than this (= 256 MB). -const ROWINDEX MAX_PATTERN_ROWS = 1024; -const ORDERINDEX MAX_ORDERS = ORDERINDEX_MAX + 1; -const PATTERNINDEX MAX_PATTERNS = 4000; -const SAMPLEINDEX MAX_SAMPLES = 4000; -const INSTRUMENTINDEX MAX_INSTRUMENTS = 256; -const PLUGINDEX MAX_MIXPLUGINS = 250; +const ROWINDEX MAX_PATTERN_ROWS = 1024; +const ORDERINDEX MAX_ORDERS = ORDERINDEX_MAX + 1; +const PATTERNINDEX MAX_PATTERNS = 4000; +const SAMPLEINDEX MAX_SAMPLES = 4000; +const INSTRUMENTINDEX MAX_INSTRUMENTS = 256; +const PLUGINDEX MAX_MIXPLUGINS = 250; -const SEQUENCEINDEX MAX_SEQUENCES = 50; +const SEQUENCEINDEX MAX_SEQUENCES = 50; -const CHANNELINDEX MAX_BASECHANNELS = 127; // Maximum pattern channels. -const CHANNELINDEX MAX_CHANNELS = 256; // Maximum number of mixing channels. +const CHANNELINDEX MAX_BASECHANNELS = 127; // Maximum pattern channels. +const CHANNELINDEX MAX_CHANNELS = 256; // Maximum number of mixing channels. -#define FREQ_FRACBITS 4 // Number of fractional bits in return value of CSoundFile::GetFreqFromPeriod() +#define FREQ_FRACBITS 4 // Number of fractional bits in return value of CSoundFile::GetFreqFromPeriod() // String lengths (including trailing null char) -#define MAX_SAMPLENAME 32 -#define MAX_SAMPLEFILENAME 22 -#define MAX_INSTRUMENTNAME 32 -#define MAX_INSTRUMENTFILENAME 32 -#define MAX_PATTERNNAME 32 -#define MAX_CHANNELNAME 20 +#define MAX_SAMPLENAME 32 +#define MAX_SAMPLEFILENAME 22 +#define MAX_INSTRUMENTNAME 32 +#define MAX_INSTRUMENTFILENAME 32 +#define MAX_PATTERNNAME 32 +#define MAX_CHANNELNAME 20 enum MODTYPE { - MOD_TYPE_NONE = 0x00, - MOD_TYPE_MOD = 0x01, - MOD_TYPE_S3M = 0x02, - MOD_TYPE_XM = 0x04, - MOD_TYPE_MED = 0x08, - MOD_TYPE_MTM = 0x10, - MOD_TYPE_IT = 0x20, - MOD_TYPE_669 = 0x40, - MOD_TYPE_ULT = 0x80, - MOD_TYPE_STM = 0x100, - MOD_TYPE_FAR = 0x200, - MOD_TYPE_DTM = 0x400, - MOD_TYPE_AMF = 0x800, - MOD_TYPE_AMS = 0x1000, - MOD_TYPE_DSM = 0x2000, - MOD_TYPE_MDL = 0x4000, - MOD_TYPE_OKT = 0x8000, - MOD_TYPE_MID = 0x10000, - MOD_TYPE_DMF = 0x20000, - MOD_TYPE_PTM = 0x40000, - MOD_TYPE_DBM = 0x80000, - MOD_TYPE_MT2 = 0x100000, - MOD_TYPE_AMF0 = 0x200000, - MOD_TYPE_PSM = 0x400000, - MOD_TYPE_J2B = 0x800000, - MOD_TYPE_MPT = 0x1000000, - MOD_TYPE_IMF = 0x2000000, - MOD_TYPE_AMS2 = 0x4000000, - MOD_TYPE_DIGI = 0x8000000, - MOD_TYPE_STP = 0x10000000, - MOD_TYPE_PLM = 0x20000000, - MOD_TYPE_SFX = 0x40000000, + MOD_TYPE_NONE = 0x00, + MOD_TYPE_MOD = 0x01, + MOD_TYPE_S3M = 0x02, + MOD_TYPE_XM = 0x04, + MOD_TYPE_MED = 0x08, + MOD_TYPE_MTM = 0x10, + MOD_TYPE_IT = 0x20, + MOD_TYPE_669 = 0x40, + MOD_TYPE_ULT = 0x80, + MOD_TYPE_STM = 0x100, + MOD_TYPE_FAR = 0x200, + MOD_TYPE_DTM = 0x400, + MOD_TYPE_AMF = 0x800, + MOD_TYPE_AMS = 0x1000, + MOD_TYPE_DSM = 0x2000, + MOD_TYPE_MDL = 0x4000, + MOD_TYPE_OKT = 0x8000, + MOD_TYPE_MID = 0x10000, + MOD_TYPE_DMF = 0x20000, + MOD_TYPE_PTM = 0x40000, + MOD_TYPE_DBM = 0x80000, + MOD_TYPE_MT2 = 0x100000, + MOD_TYPE_AMF0 = 0x200000, + MOD_TYPE_PSM = 0x400000, + MOD_TYPE_J2B = 0x800000, + MOD_TYPE_MPT = 0x1000000, + MOD_TYPE_IMF = 0x2000000, + MOD_TYPE_DIGI = 0x4000000, + MOD_TYPE_STP = 0x8000000, + MOD_TYPE_PLM = 0x10000000, + MOD_TYPE_SFX = 0x20000000, }; DECLARE_FLAGSET(MODTYPE) @@ -105,8 +104,6 @@ DECLARE_FLAGSET(MODTYPE) enum MODCONTAINERTYPE { MOD_CONTAINERTYPE_NONE = 0x0, - MOD_CONTAINERTYPE_MO3 = 0x1, - MOD_CONTAINERTYPE_GDM = 0x2, MOD_CONTAINERTYPE_UMX = 0x3, MOD_CONTAINERTYPE_XPK = 0x4, MOD_CONTAINERTYPE_PP20 = 0x5, @@ -120,81 +117,82 @@ enum MODCONTAINERTYPE enum ChannelFlags { // Sample Flags - CHN_16BIT = 0x01, // 16-bit sample - CHN_LOOP = 0x02, // looped sample - CHN_PINGPONGLOOP = 0x04, // bidi-looped sample - CHN_SUSTAINLOOP = 0x08, // sample with sustain loop - CHN_PINGPONGSUSTAIN = 0x10, // sample with bidi sustain loop - CHN_PANNING = 0x20, // sample with forced panning - CHN_STEREO = 0x40, // stereo sample - CHN_REVERSE = 0x80, // start sample playback from sample / loop end (Velvet Studio feature) - this is intentionally the same flag as CHN_PINGPONGFLAG. + CHN_16BIT = 0x01, // 16-bit sample + CHN_LOOP = 0x02, // Looped sample + CHN_PINGPONGLOOP = 0x04, // Bidi-looped sample + CHN_SUSTAINLOOP = 0x08, // Sample with sustain loop + CHN_PINGPONGSUSTAIN = 0x10, // Sample with bidi sustain loop + CHN_PANNING = 0x20, // Sample with forced panning + CHN_STEREO = 0x40, // Stereo sample + CHN_REVERSE = 0x80, // Start sample playback from sample / loop end (Velvet Studio feature) + CHN_SURROUND = 0x100, // Use surround channel + CHN_ADLIB = 0x200, // Adlib / OPL instrument is active on this channel + // Channel Flags - CHN_PINGPONGFLAG = 0x80, // when flag is on, sample is processed backwards - CHN_MUTE = 0x100, // muted channel - CHN_KEYOFF = 0x200, // exit sustain - CHN_NOTEFADE = 0x400, // fade note (instrument mode) - CHN_SURROUND = 0x800, // use surround channel - CHN_WRAPPED_LOOP = 0x1000, // loop just wrapped around to loop start (required for correct interpolation around loop points) - CHN_AMIGAFILTER = 0x2000, // Apply Amiga low-pass filter - CHN_FILTER = 0x4000, // Apply resonant filter on sample - CHN_VOLUMERAMP = 0x8000, // Apply volume ramping - CHN_VIBRATO = 0x10000, // Apply vibrato - CHN_TREMOLO = 0x20000, // Apply tremolo - //CHN_PANBRELLO = 0x40000, // Apply panbrello - CHN_PORTAMENTO = 0x80000, // Apply portamento - CHN_GLISSANDO = 0x100000, // Glissando (force portamento to semitones) mode - CHN_FASTVOLRAMP = 0x200000, // Force usage of global ramping settings instead of ramping over the complete render buffer length - CHN_EXTRALOUD = 0x400000, // Force sample to play at 0dB - CHN_REVERB = 0x800000, // Apply reverb on this channel - CHN_NOREVERB = 0x1000000, // Disable reverb on this channel - CHN_SOLO = 0x2000000, // solo channel -> CODE#0012 -> DESC="midi keyboard split" -! NEW_FEATURE#0012 - CHN_NOFX = 0x4000000, // dry channel -> CODE#0015 -> DESC="channels management dlg" -! NEW_FEATURE#0015 - CHN_SYNCMUTE = 0x8000000, // keep sample sync on mute + CHN_PINGPONGFLAG = 0x80, // When flag is on, sample is processed backwards - this is intentionally the same flag as CHN_REVERSE. + CHN_MUTE = 0x400, // Muted channel + CHN_KEYOFF = 0x800, // Exit sustain + CHN_NOTEFADE = 0x1000, // Fade note (instrument mode) + CHN_WRAPPED_LOOP = 0x2000, // Loop just wrapped around to loop start (required for correct interpolation around loop points) + CHN_AMIGAFILTER = 0x4000, // Apply Amiga low-pass filter + CHN_FILTER = 0x8000, // Apply resonant filter on sample + CHN_VOLUMERAMP = 0x10000, // Apply volume ramping + CHN_VIBRATO = 0x20000, // Apply vibrato + CHN_TREMOLO = 0x40000, // Apply tremolo + CHN_PORTAMENTO = 0x80000, // Apply portamento + CHN_GLISSANDO = 0x100000, // Glissando (force portamento to semitones) mode + CHN_FASTVOLRAMP = 0x200000, // Force usage of global ramping settings instead of ramping over the complete render buffer length + CHN_EXTRALOUD = 0x400000, // Force sample to play at 0dB + CHN_REVERB = 0x800000, // Apply reverb on this channel + CHN_NOREVERB = 0x1000000, // Disable reverb on this channel + CHN_SOLO = 0x2000000, // Solo channel + CHN_NOFX = 0x4000000, // Dry channel (no plugins) + CHN_SYNCMUTE = 0x8000000, // Keep sample sync on mute // Sample flags (only present in ModSample::uFlags, may overlap with CHN_CHANNELFLAGS) - SMP_MODIFIED = 0x1000, // Sample data has been edited in the tracker - SMP_KEEPONDISK = 0x2000, // Sample is not saved to file, data is restored from original sample file - SMP_NODEFAULTVOLUME = 0x4000, // Ignore default volume setting + SMP_MODIFIED = 0x2000, // Sample data has been edited in the tracker + SMP_KEEPONDISK = 0x4000, // Sample is not saved to file, data is restored from original sample file + SMP_NODEFAULTVOLUME = 0x8000, // Ignore default volume setting }; DECLARE_FLAGSET(ChannelFlags) -#define CHN_SAMPLEFLAGS (CHN_16BIT | CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN | CHN_PANNING | CHN_STEREO | CHN_PINGPONGFLAG | CHN_REVERSE) -#define CHN_CHANNELFLAGS (~CHN_SAMPLEFLAGS) +#define CHN_SAMPLEFLAGS (CHN_16BIT | CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN | CHN_PANNING | CHN_STEREO | CHN_PINGPONGFLAG | CHN_REVERSE | CHN_SURROUND | CHN_ADLIB) +#define CHN_CHANNELFLAGS (~CHN_SAMPLEFLAGS | CHN_SURROUND) // Sample flags fit into the first 16 bits, and with the current memory layout, storing them as a 16-bit integer packs struct ModSample nicely. typedef FlagSet SampleFlags; // Instrument envelope-specific flags -enum EnvelopeFlags +enum EnvelopeFlags : uint8 { - ENV_ENABLED = 0x01, // env is enabled - ENV_LOOP = 0x02, // env loop - ENV_SUSTAIN = 0x04, // env sustain - ENV_CARRY = 0x08, // env carry - ENV_FILTER = 0x10, // filter env enabled (this has to be combined with ENV_ENABLED in the pitch envelope's flags) + ENV_ENABLED = 0x01, // env is enabled + ENV_LOOP = 0x02, // env loop + ENV_SUSTAIN = 0x04, // env sustain + ENV_CARRY = 0x08, // env carry + ENV_FILTER = 0x10, // filter env enabled (this has to be combined with ENV_ENABLED in the pitch envelope's flags) }; DECLARE_FLAGSET(EnvelopeFlags) // Envelope value boundaries -#define ENVELOPE_MIN 0 // Vertical min value of a point -#define ENVELOPE_MID 32 // Vertical middle line -#define ENVELOPE_MAX 64 // Vertical max value of a point -#define MAX_ENVPOINTS 240 // Maximum length of each instrument envelope +#define ENVELOPE_MIN 0 // Vertical min value of a point +#define ENVELOPE_MID 32 // Vertical middle line +#define ENVELOPE_MAX 64 // Vertical max value of a point +#define MAX_ENVPOINTS 240 // Maximum length of each instrument envelope // Instrument-specific flags -enum InstrumentFlags +enum InstrumentFlags : uint8 { - INS_SETPANNING = 0x01, // Panning enabled - INS_MUTE = 0x02, // Instrument is muted + INS_SETPANNING = 0x01, // Panning enabled + INS_MUTE = 0x02, // Instrument is muted }; DECLARE_FLAGSET(InstrumentFlags) // envelope types in instrument editor -enum EnvelopeType +enum EnvelopeType : uint8 { ENV_VOLUME = 0, ENV_PANNING, @@ -204,57 +202,65 @@ enum EnvelopeType }; // Filter Modes -#define FLTMODE_UNCHANGED 0xFF -#define FLTMODE_LOWPASS 0 -#define FLTMODE_HIGHPASS 1 +enum InstrFilterMode : uint8 +{ + FLTMODE_UNCHANGED = 0xFF, + FLTMODE_LOWPASS = 0, + FLTMODE_HIGHPASS = 1, +}; // NNA types (New Note Action) -#define NNA_NOTECUT 0 -#define NNA_CONTINUE 1 -#define NNA_NOTEOFF 2 -#define NNA_NOTEFADE 3 +enum NewNoteAction : uint8 +{ + NNA_NOTECUT = 0, + NNA_CONTINUE = 1, + NNA_NOTEOFF = 2, + NNA_NOTEFADE = 3, +}; // DCT types (Duplicate Check Types) -#define DCT_NONE 0 -#define DCT_NOTE 1 -#define DCT_SAMPLE 2 -#define DCT_INSTRUMENT 3 -#define DCT_PLUGIN 4 +enum DuplicateCheckType : uint8 +{ + DCT_NONE = 0, + DCT_NOTE = 1, + DCT_SAMPLE = 2, + DCT_INSTRUMENT = 3, + DCT_PLUGIN = 4, +}; // DNA types (Duplicate Note Action) -#define DNA_NOTECUT 0 -#define DNA_NOTEOFF 1 -#define DNA_NOTEFADE 2 +enum DuplicateNoteAction : uint8 +{ + DNA_NOTECUT = 0, + DNA_NOTEOFF = 1, + DNA_NOTEFADE = 2, +}; // Module flags - contains both song configuration and playback state... Use SONG_FILE_FLAGS and SONG_PLAY_FLAGS distinguish between the two. enum SongFlags { - //SONG_EMBEDMIDICFG = 0x0001, // Embed macros in file - SONG_FASTVOLSLIDES = 0x0002, // Old Scream Tracker 3.0 volume slides - SONG_ITOLDEFFECTS = 0x0004, // Old Impulse Tracker effect implementations - SONG_ITCOMPATGXX = 0x0008, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects) - SONG_LINEARSLIDES = 0x0010, // Linear slides vs. Amiga slides - SONG_PATTERNLOOP = 0x0020, // Loop current pattern (pattern editor) - SONG_STEP = 0x0040, // Song is in "step" mode (pattern editor) - SONG_PAUSED = 0x0080, // Song is paused (no tick processing, just rendering audio) - SONG_FADINGSONG = 0x0100, // Song is fading out - SONG_ENDREACHED = 0x0200, // Song is finished - //SONG_GLOBALFADE = 0x0400, // Song is fading out - //SONG_CPUVERYHIGH = 0x0800, // High CPU usage - SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row - SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note) - SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels - SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz) - SONG_AMIGALIMITS = 0x10000, // Enforce amiga frequency limits - SONG_S3MOLDVIBRATO = 0x20000, // ScreamTracker 2 vibrato in S3M files - //SONG_ITPEMBEDIH = 0x40000, // Embed instrument headers in project file - SONG_BREAKTOROW = 0x80000, // Break to row command encountered (internal flag, do not touch) - SONG_POSJUMP = 0x100000, // Position jump encountered (internal flag, do not touch) - SONG_PT_MODE = 0x200000, // ProTracker 1/2 playback mode - SONG_PLAYALLSONGS = 0x400000, // Play all subsongs consecutively (libopenmpt) - SONG_ISAMIGA = 0x800000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler + SONG_FASTVOLSLIDES = 0x0002, // Old Scream Tracker 3.0 volume slides + SONG_ITOLDEFFECTS = 0x0004, // Old Impulse Tracker effect implementations + SONG_ITCOMPATGXX = 0x0008, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects) + SONG_LINEARSLIDES = 0x0010, // Linear slides vs. Amiga slides + SONG_PATTERNLOOP = 0x0020, // Loop current pattern (pattern editor) + SONG_STEP = 0x0040, // Song is in "step" mode (pattern editor) + SONG_PAUSED = 0x0080, // Song is paused (no tick processing, just rendering audio) + SONG_FADINGSONG = 0x0100, // Song is fading out + SONG_ENDREACHED = 0x0200, // Song is finished + SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row + SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note) + SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels + SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz) + SONG_AMIGALIMITS = 0x10000, // Enforce amiga frequency limits + SONG_S3MOLDVIBRATO = 0x20000, // ScreamTracker 2 vibrato in S3M files + SONG_BREAKTOROW = 0x80000, // Break to row command encountered (internal flag, do not touch) + SONG_POSJUMP = 0x100000, // Position jump encountered (internal flag, do not touch) + SONG_PT_MODE = 0x200000, // ProTracker 1/2 playback mode + SONG_PLAYALLSONGS = 0x400000, // Play all subsongs consecutively (libopenmpt) + SONG_ISAMIGA = 0x800000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler }; DECLARE_FLAGSET(SongFlags) @@ -263,48 +269,81 @@ DECLARE_FLAGSET(SongFlags) // Global Options (Renderer) #ifndef NO_AGC -#define SNDDSP_AGC 0x40 // automatic gain control +#define SNDDSP_AGC 0x40 // Automatic gain control #endif // ~NO_AGC #ifndef NO_DSP -#define SNDDSP_MEGABASS 0x02 // bass expansion -#define SNDDSP_SURROUND 0x08 // surround mix +#define SNDDSP_MEGABASS 0x02 // Bass expansion +#define SNDDSP_SURROUND 0x08 // Surround mix #endif // NO_DSP #ifndef NO_REVERB -#define SNDDSP_REVERB 0x20 // apply reverb +#define SNDDSP_REVERB 0x20 // Apply reverb #endif // NO_REVERB #ifndef NO_EQ -#define SNDDSP_EQ 0x80 // apply EQ +#define SNDDSP_EQ 0x80 // Apply EQ #endif // NO_EQ -#define SNDMIX_SOFTPANNING 0x10 // soft panning mode (this is forced with mixmode RC3 and later) +#define SNDMIX_SOFTPANNING 0x10 // Soft panning mode (this is forced with mixmode RC3 and later) // Misc Flags (can safely be turned on or off) -#define SNDMIX_MAXDEFAULTPAN 0x80000 // Used by the MOD loader (currently unused) -#define SNDMIX_MUTECHNMODE 0x100000 // Notes are not played on muted channels +#define SNDMIX_MAXDEFAULTPAN 0x80000 // Currently unused (should be used by Amiga MOD loaders) +#define SNDMIX_MUTECHNMODE 0x100000 // Notes are not played on muted channels #define MAX_GLOBAL_VOLUME 256u // Resampling modes -enum ResamplingMode +enum ResamplingMode : uint8 { // ATTENTION: Do not change ANY of these values, as they get written out to files in per instrument interpolation settings // and old files have these exact values in them which should not change meaning. - SRCMODE_NEAREST = 0, - SRCMODE_LINEAR = 1, - SRCMODE_SPLINE = 2, - SRCMODE_POLYPHASE = 3, - SRCMODE_FIRFILTER = 4, - SRCMODE_DEFAULT = 5, + SRCMODE_NEAREST = 0, // 1 tap, no AA + SRCMODE_LINEAR = 1, // 2 tap, no AA + SRCMODE_CUBIC = 2, // 4 tap, no AA + SRCMODE_SINC8 = 4, // 8 tap, no AA (yes, index 4) (XMMS-ModPlug) + SRCMODE_SINC8LP = 3, // 8 tap, with AA (yes, index 3) (Polyphase) - SRCMODE_AMIGA = 0xFF, // Not explicitely user-selectable + SRCMODE_DEFAULT = 5, // Only used for instrument settings, not used inside the mixer + + SRCMODE_AMIGA = 0xFF, // Not explicitely user-selectable }; -static inline bool IsKnownResamplingMode(int mode) +namespace Resampling { - return (mode >= 0) && (mode < SRCMODE_DEFAULT); + +static inline std::array AllModes() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP } }; } + +static inline std::array AllModesWithDefault() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP, SRCMODE_DEFAULT } }; } + +static MPT_CONSTEXPR11_FUN ResamplingMode Default() noexcept { return SRCMODE_SINC8LP; } + +static MPT_CONSTEXPR11_FUN bool IsKnownMode(int mode) noexcept { return (mode >= 0) && (mode < SRCMODE_DEFAULT); } + +static MPT_CONSTEXPR11_FUN ResamplingMode ToKnownMode(int mode) noexcept +{ + return Resampling::IsKnownMode(mode) ? static_cast(mode) + : (mode == SRCMODE_AMIGA) ? SRCMODE_LINEAR + : Resampling::Default(); } +static MPT_CONSTEXPR11_FUN int Length(ResamplingMode mode) noexcept +{ + return mode == SRCMODE_NEAREST ? 1 + : mode == SRCMODE_LINEAR ? 2 + : mode == SRCMODE_CUBIC ? 4 + : mode == SRCMODE_SINC8 ? 8 + : mode == SRCMODE_SINC8LP ? 8 + : 0; +} + +static MPT_CONSTEXPR11_FUN bool HasAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP); } + +static MPT_CONSTEXPR11_FUN ResamplingMode AddAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8) ? SRCMODE_SINC8LP : mode; } + +static MPT_CONSTEXPR11_FUN ResamplingMode RemoveAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP) ? SRCMODE_SINC8 : mode; } + +} + + // Release node defines #define ENV_RELEASE_NODE_UNSET 0xFF @@ -326,14 +365,14 @@ enum PluginMutePriority RespectMutes, }; -//Plugin velocity handling options +// Plugin velocity handling options enum PLUGVELOCITYHANDLING { PLUGIN_VELOCITYHANDLING_CHANNEL = 0, PLUGIN_VELOCITYHANDLING_VOLUME }; -//Plugin volumecommand handling options +// Plugin volumecommand handling options enum PLUGVOLUMEHANDLING { PLUGIN_VOLUMEHANDLING_MIDI = 0, @@ -343,7 +382,7 @@ enum PLUGVOLUMEHANDLING PLUGIN_VOLUMEHANDLING_MAX, }; -enum MidiChannel +enum MidiChannel : uint8 { MidiNoChannel = 0, MidiFirstChannel = 1, @@ -353,7 +392,7 @@ enum MidiChannel // Vibrato Types -enum VibratoType +enum VibratoType : uint8 { VIB_SINE = 0, VIB_SQUARE, @@ -367,105 +406,113 @@ enum VibratoType // Note: The index of every flag has to be fixed, so do not remove flags. Always add new flags at the end! enum PlayBehaviour { - MSF_COMPATIBLE_PLAY, // No-op - only used during loading (Old general compatibility flag for IT/MPT/XM) - kMPTOldSwingBehaviour, // MPT 1.16 swing behaviour (IT/MPT, deprecated) - kMIDICCBugEmulation, // Emulate broken volume MIDI CC behaviour (IT/MPT/XM, deprecated) - kOldMIDIPitchBends, // Old VST MIDI pitch bend behaviour (IT/MPT/XM, deprecated) - kFT2VolumeRamping, // Smooth volume ramping like in FT2 (XM) - kMODVBlankTiming, // F21 and above set speed instead of tempo - kSlidesAtSpeed1, // Execute normal slides at speed 1 as if they were fine slides - kHertzInLinearMode, // Compute note frequency in hertz rather than periods - kTempoClamp, // Clamp tempo to 32-255 range. - kPerChannelGlobalVolSlide, // Global volume slide memory is per-channel - kPanOverride, // Panning commands override surround and random pan variation + MSF_COMPATIBLE_PLAY, // No-op - only used during loading (Old general compatibility flag for IT/MPT/XM) + kMPTOldSwingBehaviour, // MPT 1.16 swing behaviour (IT/MPT, deprecated) + kMIDICCBugEmulation, // Emulate broken volume MIDI CC behaviour (IT/MPT/XM, deprecated) + kOldMIDIPitchBends, // Old VST MIDI pitch bend behaviour (IT/MPT/XM, deprecated) + kFT2VolumeRamping, // Smooth volume ramping like in FT2 (XM) + kMODVBlankTiming, // F21 and above set speed instead of tempo + kSlidesAtSpeed1, // Execute normal slides at speed 1 as if they were fine slides + kHertzInLinearMode, // Compute note frequency in hertz rather than periods + kTempoClamp, // Clamp tempo to 32-255 range. + kPerChannelGlobalVolSlide, // Global volume slide memory is per-channel + kPanOverride, // Panning commands override surround and random pan variation - kITInstrWithoutNote, // Avoid instrument handling if there is no note - kITVolColFinePortamento, // Volume column portamento never does fine portamento - kITArpeggio, // IT arpeggio algorithm - kITOutOfRangeDelay, // Out-of-range delay command behaviour in IT - kITPortaMemoryShare, // Gxx shares memory with Exx and Fxx - kITPatternLoopTargetReset, // After finishing a pattern loop, set the pattern loop target to the next row - kITFT2PatternLoop, // Nested pattern loop behaviour - kITPingPongNoReset, // Don't reset ping pong direction with instrument numbers - kITEnvelopeReset, // IT envelope reset behaviour - kITClearOldNoteAfterCut, // Forget the previous note after cutting it - kITVibratoTremoloPanbrello, // More IT-like Hxx / hx, Rxx, Yxx and autovibrato handling, including more precise LUTs - kITTremor, // Ixx behaves like in IT - kITRetrigger, // Qxx behaves like in IT - kITMultiSampleBehaviour, // Properly update C-5 frequency when changing in multisampled instrument - kITPortaTargetReached, // Clear portamento target after it has been reached - kITPatternLoopBreak, // Don't reset loop count on pattern break. - kITOffset, // IT-style Oxx edge case handling - kITSwingBehaviour, // IT's swing behaviour - kITNNAReset, // NNA is reset on every note change, not every instrument change - kITSCxStopsSample, // SCx really stops the sample and does not just mute it - kITEnvelopePositionHandling, // IT-style envelope position advance + enable/disable behaviour - kITPortamentoInstrument, // No sample changes during portamento with Compatible Gxx enabled, instrument envelope reset with portamento - kITPingPongMode, // Don't repeat last sample point in ping pong loop, like IT's software mixer - kITRealNoteMapping, // Use triggered note rather than translated note for PPS and other effects - kITHighOffsetNoRetrig, // SAx should not apply an offset effect to a note next to it - kITFilterBehaviour, // User IT's filter coefficients (unless extended filter range is used) - kITNoSurroundPan, // Panning and surround are mutually exclusive - kITShortSampleRetrig, // Don't retrigger already stopped channels - kITPortaNoNote, // Don't apply any portamento if no previous note is playing - kITDontResetNoteOffOnPorta, // Only reset note-off status on portamento in IT Compatible Gxx mode - kITVolColMemory, // IT volume column effects share their memory with the effect column - kITPortamentoSwapResetsPos, // Portamento with sample swap plays the new sample from the beginning - kITEmptyNoteMapSlot, // IT ignores instrument note map entries with no note completely - kITFirstTickHandling, // IT-style first tick handling - kITSampleAndHoldPanbrello, // IT-style sample&hold panbrello waveform - kITClearPortaTarget, // New notes reset portamento target in IT - kITPanbrelloHold, // Don't reset panbrello effect until next note or panning effect - kITPanningReset, // Sample and instrument panning is only applied on note change, not instrument change - kITPatternLoopWithJumps, // Bxx on the same row as SBx terminates the loop in IT - kITInstrWithNoteOff, // Instrument number with note-off recalls default volume + kITInstrWithoutNote, // Avoid instrument handling if there is no note + kITVolColFinePortamento, // Volume column portamento never does fine portamento + kITArpeggio, // IT arpeggio algorithm + kITOutOfRangeDelay, // Out-of-range delay command behaviour in IT + kITPortaMemoryShare, // Gxx shares memory with Exx and Fxx + kITPatternLoopTargetReset, // After finishing a pattern loop, set the pattern loop target to the next row + kITFT2PatternLoop, // Nested pattern loop behaviour + kITPingPongNoReset, // Don't reset ping pong direction with instrument numbers + kITEnvelopeReset, // IT envelope reset behaviour + kITClearOldNoteAfterCut, // Forget the previous note after cutting it + kITVibratoTremoloPanbrello, // More IT-like Hxx / hx, Rxx, Yxx and autovibrato handling, including more precise LUTs + kITTremor, // Ixx behaves like in IT + kITRetrigger, // Qxx behaves like in IT + kITMultiSampleBehaviour, // Properly update C-5 frequency when changing in multisampled instrument + kITPortaTargetReached, // Clear portamento target after it has been reached + kITPatternLoopBreak, // Don't reset loop count on pattern break. + kITOffset, // IT-style Oxx edge case handling + kITSwingBehaviour, // IT's swing behaviour + kITNNAReset, // NNA is reset on every note change, not every instrument change + kITSCxStopsSample, // SCx really stops the sample and does not just mute it + kITEnvelopePositionHandling, // IT-style envelope position advance + enable/disable behaviour + kITPortamentoInstrument, // No sample changes during portamento with Compatible Gxx enabled, instrument envelope reset with portamento + kITPingPongMode, // Don't repeat last sample point in ping pong loop, like IT's software mixer + kITRealNoteMapping, // Use triggered note rather than translated note for PPS and other effects + kITHighOffsetNoRetrig, // SAx should not apply an offset effect to a note next to it + kITFilterBehaviour, // User IT's filter coefficients (unless extended filter range is used) + kITNoSurroundPan, // Panning and surround are mutually exclusive + kITShortSampleRetrig, // Don't retrigger already stopped channels + kITPortaNoNote, // Don't apply any portamento if no previous note is playing + kITDontResetNoteOffOnPorta, // Only reset note-off status on portamento in IT Compatible Gxx mode + kITVolColMemory, // IT volume column effects share their memory with the effect column + kITPortamentoSwapResetsPos, // Portamento with sample swap plays the new sample from the beginning + kITEmptyNoteMapSlot, // IT ignores instrument note map entries with no note completely + kITFirstTickHandling, // IT-style first tick handling + kITSampleAndHoldPanbrello, // IT-style sample&hold panbrello waveform + kITClearPortaTarget, // New notes reset portamento target in IT + kITPanbrelloHold, // Don't reset panbrello effect until next note or panning effect + kITPanningReset, // Sample and instrument panning is only applied on note change, not instrument change + kITPatternLoopWithJumps, // Bxx on the same row as SBx terminates the loop in IT + kITInstrWithNoteOff, // Instrument number with note-off recalls default volume - kFT2Arpeggio, // FT2 arpeggio algorithm - kFT2Retrigger, // Rxx behaves like in FT2 - kFT2VolColVibrato, // Vibrato depth in volume column does not actually execute the vibrato effect - kFT2PortaNoNote, // Don't play portamento-ed note if no previous note is playing - kFT2KeyOff, // FT2-style Kxx handling - kFT2PanSlide, // Volume-column pan slides should be handled like fine slides - kFT2OffsetOutOfRange, // FT2-style 9xx edge case handling - kFT2RestrictXCommand, // Don't allow MPT extensions to Xxx command in XM - kFT2RetrigWithNoteDelay, // Retrigger envelopes if there is a note delay with no note - kFT2SetPanEnvPos, // Lxx only sets the pan env position if the volume envelope's sustain flag is set - kFT2PortaIgnoreInstr, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself. - kFT2VolColMemory, // No volume column memory in FT2 - kFT2LoopE60Restart, // Next pattern starts on the same row as the last E60 command - kFT2ProcessSilentChannels, // Keep processing silent channels for later 3xx pickup - kFT2ReloadSampleSettings, // Reload sample settings even if a note-off is placed next to an instrument number - kFT2PortaDelay, // Portamento with note delay next to it is ignored in FT2 - kFT2Transpose, // Out-of-range transposed notes in FT2 - kFT2PatternLoopWithJumps, // Bxx or Dxx on the same row as E6x terminates the loop in FT2 - kFT2PortaTargetNoReset, // Portamento target is not reset with new notes in FT2 - kFT2EnvelopeEscape, // FT2 sustain point at end of envelope - kFT2Tremor, // Txx behaves like in FT2 - kFT2OutOfRangeDelay, // Out-of-range delay command behaviour in FT2 - kFT2Periods, // Use FT2's broken period handling - kFT2PanWithDelayedNoteOff, // Pan command with delayed note-off - kFT2VolColDelay, // FT2-style volume column handling if there is a note delay - kFT2FinetunePrecision, // Only take the upper 4 bits of sample finetune. + kFT2Arpeggio, // FT2 arpeggio algorithm + kFT2Retrigger, // Rxx behaves like in FT2 + kFT2VolColVibrato, // Vibrato depth in volume column does not actually execute the vibrato effect + kFT2PortaNoNote, // Don't play portamento-ed note if no previous note is playing + kFT2KeyOff, // FT2-style Kxx handling + kFT2PanSlide, // Volume-column pan slides should be handled like fine slides + kFT2OffsetOutOfRange, // FT2-style 9xx edge case handling + kFT2RestrictXCommand, // Don't allow MPT extensions to Xxx command in XM + kFT2RetrigWithNoteDelay, // Retrigger envelopes if there is a note delay with no note + kFT2SetPanEnvPos, // Lxx only sets the pan env position if the volume envelope's sustain flag is set + kFT2PortaIgnoreInstr, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself. + kFT2VolColMemory, // No volume column memory in FT2 + kFT2LoopE60Restart, // Next pattern starts on the same row as the last E60 command + kFT2ProcessSilentChannels, // Keep processing silent channels for later 3xx pickup + kFT2ReloadSampleSettings, // Reload sample settings even if a note-off is placed next to an instrument number + kFT2PortaDelay, // Portamento with note delay next to it is ignored in FT2 + kFT2Transpose, // Out-of-range transposed notes in FT2 + kFT2PatternLoopWithJumps, // Bxx or Dxx on the same row as E6x terminates the loop in FT2 + kFT2PortaTargetNoReset, // Portamento target is not reset with new notes in FT2 + kFT2EnvelopeEscape, // FT2 sustain point at end of envelope + kFT2Tremor, // Txx behaves like in FT2 + kFT2OutOfRangeDelay, // Out-of-range delay command behaviour in FT2 + kFT2Periods, // Use FT2's broken period handling + kFT2PanWithDelayedNoteOff, // Pan command with delayed note-off + kFT2VolColDelay, // FT2-style volume column handling if there is a note delay + kFT2FinetunePrecision, // Only take the upper 4 bits of sample finetune. - kST3NoMutedChannels, // Don't process any effects on muted S3M channels - kST3EffectMemory, // Most effects share the same memory in ST3 - kST3PortaSampleChange, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself. - kST3VibratoMemory, // Do not remember vibrato type in effect memory - kST3LimitPeriod, // Cut note instead of limiting final period (ModPlug Tracker style) - KST3PortaAfterArpeggio, // Portamento after arpeggio continues at the note where the arpeggio left off + kST3NoMutedChannels, // Don't process any effects on muted S3M channels + kST3EffectMemory, // Most effects share the same memory in ST3 + kST3PortaSampleChange, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself. + kST3VibratoMemory, // Do not remember vibrato type in effect memory + kST3LimitPeriod, // Cut note instead of limiting final period (ModPlug Tracker style) + KST3PortaAfterArpeggio, // Portamento after arpeggio continues at the note where the arpeggio left off - kMODOneShotLoops, // Allow ProTracker-like oneshot loops - kMODIgnorePanning, // Do not process any panning commands - kMODSampleSwap, // On-the-fly sample swapping + kMODOneShotLoops, // Allow ProTracker-like oneshot loops + kMODIgnorePanning, // Do not process any panning commands + kMODSampleSwap, // On-the-fly sample swapping - kFT2NoteOffFlags, // Set and reset the correct fade/key-off flags with note-off and instrument number after note-off - kITMultiSampleInstrumentNumber, // After portamento to different sample within multi-sampled instrument, lone instrument numbers in patterns always recall the new sample's default settings - kRowDelayWithNoteDelay, // Retrigger note delays on every reptition of a row - kFT2TremoloRampWaveform, // FT2-compatible tremolo ramp down / triangle waveform - kFT2PortaUpDownMemory, // Portamento up and down have separate memory + kFT2NoteOffFlags, // Set and reset the correct fade/key-off flags with note-off and instrument number after note-off + kITMultiSampleInstrumentNumber, // After portamento to different sample within multi-sampled instrument, lone instrument numbers in patterns always recall the new sample's default settings + kRowDelayWithNoteDelay, // Retrigger note delays on every reptition of a row + kFT2TremoloRampWaveform, // FT2-compatible tremolo ramp down / triangle waveform + kFT2PortaUpDownMemory, // Portamento up and down have separate memory - kMODOutOfRangeNoteDelay, // ProTracker behaviour for out-of-range note delays - kMODTempoOnSecondTick, // ProTracker sets tempo after the first tick + kMODOutOfRangeNoteDelay, // ProTracker behaviour for out-of-range note delays + kMODTempoOnSecondTick, // ProTracker sets tempo after the first tick + + kFT2PanSustainRelease, // If the sustain point of a panning envelope is reached before key-off, FT2 does not escape it anymore + kLegacyReleaseNode, // Legacy release node volume processing + kOPLBeatingOscillators, // Emulate beating FM oscillators from CDFM / Composer 670 + kST3OffsetWithoutInstrument, // Note without instrument uses same offset as previous note + kReleaseNodePastSustainBug, // OpenMPT 1.23.01.02 / r4009 broke release nodes past the sustain point, fixed in OpenMPT 1.28 + kFT2NoteDelayWithoutInstr, // Sometime between OpenMPT 1.18.03.00 and 1.19.01.00, delayed instrument-less notes in XM started recalling the default sample volume and panning + kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off // Add new play behaviours here. @@ -494,13 +541,12 @@ struct SamplePosition typedef uint64 unsigned_value_t; protected: - value_t v; + value_t v = 0; public: - static const uint32 fractMax = 0xFFFFFFFFu; - static MPT_FORCEINLINE uint32 GetFractMax() { return fractMax; } + enum : uint32 { fractMax = 0xFFFFFFFFu }; - SamplePosition() : v(0) { } + SamplePosition() { } explicit SamplePosition(value_t pos) : v(pos) { } SamplePosition(int32 intPart, uint32 fractPart) : v((static_cast(intPart) * (1ll<<32)) | fractPart) { } static SamplePosition Ratio(uint32 dividend, uint32 divisor) { return SamplePosition((static_cast(dividend) << 32) / divisor); } @@ -574,11 +620,10 @@ protected: MPT_CONSTEXPR11_FUN FPInt(T rawValue) : v(rawValue) { } public: - static const size_t fractFact = FFact; + enum : size_t { fractFact = FFact }; typedef T store_t; MPT_CONSTEXPR11_FUN FPInt() : v(0) { } - MPT_CONSTEXPR11_FUN FPInt(const FPInt &other) : v(other.v) { } MPT_CONSTEXPR11_FUN FPInt(T intPart, T fractPart) : v((intPart * fractFact) + (fractPart % fractFact)) { } explicit MPT_CONSTEXPR11_FUN FPInt(float f) : v(static_cast(f * float(fractFact))) { } explicit MPT_CONSTEXPR11_FUN FPInt(double f) : v(static_cast(f * double(fractFact))) { } @@ -611,4 +656,6 @@ public: typedef FPInt<10000, uint32> TEMPO; +typedef std::array OPLPatch; + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp index 98ebfbc2d..21f735259 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp @@ -33,14 +33,14 @@ uint8 CSoundFile::FrequencyToCutOff(double frequency) const // <4.8737671609324025> double cutoff = (std::log(frequency) - 4.8737671609324025) * (m_SongFlags[SONG_EXFILTERRANGE] ? (20.0 / M_LN2) : (24.0 / M_LN2)); Limit(cutoff, 0.0, 127.0); - return Util::Round(cutoff); + return mpt::saturate_round(cutoff); } -uint32 CSoundFile::CutOffToFrequency(uint32 nCutOff, int flt_modifier) const +uint32 CSoundFile::CutOffToFrequency(uint32 nCutOff, int envModifier) const { MPT_ASSERT(nCutOff < 128); - float computedCutoff = static_cast(nCutOff * (flt_modifier + 256)); // 0...127*512 + float computedCutoff = static_cast(nCutOff * (envModifier + 256)); // 0...127*512 float Fc; if(GetType() != MOD_TYPE_IMF) { @@ -51,50 +51,50 @@ uint32 CSoundFile::CutOffToFrequency(uint32 nCutOff, int flt_modifier) const // The first half of the sentence contradicts the second, though. Fc = 125.0f * std::pow(2.0f, computedCutoff * 6.0f / (127.0f * 512.0f)); } - int freq = Util::Round(Fc); + int freq = mpt::saturate_round(Fc); Limit(freq, 120, 20000); if(freq * 2 > (int)m_MixerSettings.gdwMixingFreq) freq = m_MixerSettings.gdwMixingFreq / 2; return static_cast(freq); } -// Simple 2-poles resonant filter -void CSoundFile::SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modifier) const +// Simple 2-poles resonant filter. Returns computed cutoff in range [0, 254] or -1 if filter is not applied. +int CSoundFile::SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier) const { - int cutoff = (int)pChn->nCutOff + (int)pChn->nCutSwing; - int resonance = (int)(pChn->nResonance & 0x7F) + (int)pChn->nResSwing; + int cutoff = static_cast(chn.nCutOff) + chn.nCutSwing; + int resonance = static_cast(chn.nResonance & 0x7F) + chn.nResSwing; Limit(cutoff, 0, 127); Limit(resonance, 0, 127); if(!m_playBehaviour[kMPTOldSwingBehaviour]) { - pChn->nCutOff = (uint8)cutoff; - pChn->nCutSwing = 0; - pChn->nResonance = (uint8)resonance; - pChn->nResSwing = 0; + chn.nCutOff = (uint8)cutoff; + chn.nCutSwing = 0; + chn.nResonance = (uint8)resonance; + chn.nResSwing = 0; } - // flt_modifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation. - const int computedCutoff = cutoff * (flt_modifier + 256) / 256; + // envModifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation. + const int computedCutoff = cutoff * (envModifier + 256) / 256; // Filtering is only ever done in IT if either cutoff is not full or if resonance is set. if(m_playBehaviour[kITFilterBehaviour] && resonance == 0 && computedCutoff >= 254) { - if(pChn->rowCommand.IsNote() && !pChn->rowCommand.IsPortamento() && !pChn->nMasterChn && m_SongFlags[SONG_FIRSTTICK]) + if(chn.rowCommand.IsNote() && !chn.rowCommand.IsPortamento() && !chn.nMasterChn && m_SongFlags[SONG_FIRSTTICK]) { // Z7F next to a note disables the filter, however in other cases this should not happen. // Test cases: filter-reset.it, filter-reset-carry.it, filter-nna.it - pChn->dwFlags.reset(CHN_FILTER); + chn.dwFlags.reset(CHN_FILTER); } - return; + return -1; } - pChn->dwFlags.set(CHN_FILTER); + chn.dwFlags.set(CHN_FILTER); // 2 * damping factor const float dmpfac = std::pow(10.0f, -resonance * ((24.0f / 128.0f) / 20.0f)); - const float fc = CutOffToFrequency(cutoff, flt_modifier) * (2.0f * (float)M_PI); + const float fc = CutOffToFrequency(cutoff, envModifier) * (2.0f * (float)M_PI); float d, e; if(m_playBehaviour[kITFilterBehaviour] && !m_SongFlags[SONG_EXFILTERRANGE]) { @@ -117,34 +117,34 @@ void CSoundFile::SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modif float fb1 = -e / (1.0f + d + e); #if defined(MPT_INTMIXER) -#define FILTER_CONVERT(x) Util::Round((x) * (1 << MIXING_FILTER_PRECISION)) +#define FILTER_CONVERT(x) mpt::saturate_round((x) * (1 << MIXING_FILTER_PRECISION)) #else #define FILTER_CONVERT(x) (x) #endif - switch(pChn->nFilterMode) + switch(chn.nFilterMode) { case FLTMODE_HIGHPASS: - pChn->nFilter_A0 = FILTER_CONVERT(1.0f - fg); - pChn->nFilter_B0 = FILTER_CONVERT(fb0); - pChn->nFilter_B1 = FILTER_CONVERT(fb1); + chn.nFilter_A0 = FILTER_CONVERT(1.0f - fg); + chn.nFilter_B0 = FILTER_CONVERT(fb0); + chn.nFilter_B1 = FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER - pChn->nFilter_HP = -1; + chn.nFilter_HP = -1; #else - pChn->nFilter_HP = 1.0f; + chn.nFilter_HP = 1.0f; #endif // MPT_INTMIXER break; default: - pChn->nFilter_A0 = FILTER_CONVERT(fg); - pChn->nFilter_B0 = FILTER_CONVERT(fb0); - pChn->nFilter_B1 = FILTER_CONVERT(fb1); + chn.nFilter_A0 = FILTER_CONVERT(fg); + chn.nFilter_B0 = FILTER_CONVERT(fb0); + chn.nFilter_B1 = FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER - if(pChn->nFilter_A0 == 0) - pChn->nFilter_A0 = 1; // Prevent silence at low filter cutoff and very high sampling rate - pChn->nFilter_HP = 0; + if(chn.nFilter_A0 == 0) + chn.nFilter_A0 = 1; // Prevent silence at low filter cutoff and very high sampling rate + chn.nFilter_HP = 0; #else - pChn->nFilter_HP = 0; + chn.nFilter_HP = 0; #endif // MPT_INTMIXER break; } @@ -152,10 +152,11 @@ void CSoundFile::SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modif if (bReset) { - pChn->nFilter_Y[0][0] = pChn->nFilter_Y[0][1] = 0; - pChn->nFilter_Y[1][0] = pChn->nFilter_Y[1][1] = 0; + chn.nFilter_Y[0][0] = chn.nFilter_Y[0][1] = 0; + chn.nFilter_Y[1][0] = chn.nFilter_Y[1][1] = 0; } + return computedCutoff; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index c00b92e5b..08d26fdf2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -24,6 +24,7 @@ #include "Tables.h" #include "modsmp_ctrl.h" // For updating the loop wraparound data with the invert loop effect #include "plugins/PlugInterface.h" +#include "OPL.h" OPENMPT_NAMESPACE_BEGIN @@ -31,9 +32,9 @@ OPENMPT_NAMESPACE_BEGIN #ifdef MODPLUG_TRACKER #define GLOBALVOL_7BIT_FORMATS_EXT (MOD_TYPE_MT2) #else -#define GLOBALVOL_7BIT_FORMATS_EXT Enum::value_type() +#define GLOBALVOL_7BIT_FORMATS_EXT (MOD_TYPE_NONE) #endif // MODPLUG_TRACKER -#define GLOBALVOL_7BIT_FORMATS (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_AMS2 | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT) +#define GLOBALVOL_7BIT_FORMATS (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT) // Compensate frequency slide LUTs depending on whether we are handling periods or frequency - "up" and "down" in function name are seen from frequency perspective. @@ -124,12 +125,12 @@ public: { chn.isFirstTick = false; const ModCommand &p = *sndFile.Patterns[state->m_nPattern].GetpModCommand(state->m_nRow, channel); - if(p.command == CMD_TONEPORTAMENTO) sndFile.TonePortamento(&chn, p.param); - else if(p.command == CMD_TONEPORTAVOL) sndFile.TonePortamento(&chn, 0); + if(p.command == CMD_TONEPORTAMENTO) sndFile.TonePortamento(chn, p.param); + else if(p.command == CMD_TONEPORTAVOL) sndFile.TonePortamento(chn, 0); if(p.volcmd == VOLCMD_TONEPORTAMENTO) { uint32 param = p.vol; - if(sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_AMS2 | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) + if(sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) { param = ImpulseTrackerPortaVolCmd[param & 0x0F]; } else @@ -137,25 +138,25 @@ public: // Close enough. Do not bother with idiosyncratic FT2 behaviour here. param <<= 4; } - sndFile.TonePortamento(&chn, param); + sndFile.TonePortamento(chn, param); } updateInc = true; } int period = chn.nPeriod; - if(itEnvMode) sndFile.IncrementEnvelopePositions(&chn); + if(itEnvMode) sndFile.IncrementEnvelopePositions(chn); if(updatePitchEnv) { - sndFile.ProcessPitchFilterEnvelope(&chn, period); + sndFile.ProcessPitchFilterEnvelope(chn, period); updateInc = true; } - if(!itEnvMode) sndFile.IncrementEnvelopePositions(&chn); + if(!itEnvMode) sndFile.IncrementEnvelopePositions(chn); int vol = 0; - sndFile.ProcessInstrumentFade(&chn, vol); + sndFile.ProcessInstrumentFade(chn, vol); if(updateInc || chnSettings[channel].incChanged) { - chn.increment = sndFile.GetChannelIncrement(&chn, period, 0); + chn.increment = sndFile.GetChannelIncrement(chn, period, 0); chnSettings[channel].incChanged = false; inc = chn.increment * tickDuration; if(chn.dwFlags[CHN_PINGPONGFLAG]) inc.Negate(); @@ -261,7 +262,6 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod forbiddenCommands.set(CMD_NOTESLIDEUP); forbiddenCommands.set(CMD_NOTESLIDEUPRETRIG); forbiddenCommands.set(CMD_NOTESLIDEDOWN); forbiddenCommands.set(CMD_NOTESLIDEDOWNRETRIG); forbiddenVolCommands.set(VOLCMD_PORTAUP); forbiddenVolCommands.set(VOLCMD_PORTADOWN); - forbiddenVolCommands.set(VOLCMD_VOLSLIDEUP); forbiddenVolCommands.set(VOLCMD_VOLSLIDEDOWN); // Optimize away channels for which it's pointless to adjust sample positions for(CHANNELINDEX i = 0; i < GetNumChannels(); i++) @@ -446,12 +446,11 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } } - ModChannel *pChn = playState.Chn; - // For various effects, we need to know first how many ticks there are in this row. const ModCommand *p = Patterns[playState.m_nPattern].GetpModCommand(playState.m_nRow, 0); for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, p++) { + ModChannel &chn = playState.Chn[nChn]; if(m_playBehaviour[kST3NoMutedChannels] && ChnSettings[nChn].dwFlags[CHN_MUTE]) // not even effects are processed on muted S3M channels continue; if(p->IsPcNote()) @@ -462,10 +461,10 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod memory.plugParams[std::make_pair(p->instr, p->GetValueVolCol())] = p->GetValueEffectCol(); } #endif // NO_PLUGINS - pChn[nChn].rowCommand.Clear(); + chn.rowCommand.Clear(); continue; } - pChn[nChn].rowCommand = *p; + chn.rowCommand = *p; switch(p->command) { case CMD_SPEED: @@ -510,47 +509,66 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod const uint32 numTicks = (playState.m_nMusicSpeed + tickDelay) * rowDelay; const uint32 nonRowTicks = numTicks - rowDelay; - for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); pChn++, nChn++) if(!pChn->rowCommand.IsEmpty()) + for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { + ModChannel &chn = playState.Chn[nChn]; + if(chn.rowCommand.IsEmpty()) + continue; if(m_playBehaviour[kST3NoMutedChannels] && ChnSettings[nChn].dwFlags[CHN_MUTE]) // not even effects are processed on muted S3M channels continue; - ModCommand::COMMAND command = pChn->rowCommand.command; - ModCommand::PARAM param = pChn->rowCommand.param; - ModCommand::NOTE note = pChn->rowCommand.note; + ModCommand::COMMAND command = chn.rowCommand.command; + ModCommand::PARAM param = chn.rowCommand.param; + ModCommand::NOTE note = chn.rowCommand.note; - if (pChn->rowCommand.instr) + if (chn.rowCommand.instr) { - pChn->nNewIns = pChn->rowCommand.instr; - pChn->nLastNote = NOTE_NONE; + chn.nNewIns = chn.rowCommand.instr; + chn.nLastNote = NOTE_NONE; memory.chnSettings[nChn].vol = 0xFF; } - if (pChn->rowCommand.IsNote()) pChn->nLastNote = note; + if (chn.rowCommand.IsNote()) chn.nLastNote = note; // Update channel panning - if(pChn->rowCommand.IsNote() || pChn->rowCommand.instr) + if(chn.rowCommand.IsNote() || chn.rowCommand.instr) { SAMPLEINDEX smp = 0; if(GetNumInstruments()) { ModInstrument *pIns; - if(pChn->nNewIns <= GetNumInstruments() && (pIns = Instruments[pChn->nNewIns]) != nullptr) + if(chn.nNewIns <= GetNumInstruments() && (pIns = Instruments[chn.nNewIns]) != nullptr) { if(pIns->dwFlags[INS_SETPANNING]) - pChn->nPan = pIns->nPan; + chn.nPan = pIns->nPan; if(ModCommand::IsNote(note)) smp = pIns->Keyboard[note - NOTE_MIN]; } } else { - smp = pChn->nNewIns; + smp = chn.nNewIns; } - if(smp > 0 && smp <= GetNumSamples() && Samples[smp].uFlags[CHN_PANNING]) + if(smp > 0 && smp <= GetNumSamples()) { - pChn->nPan = Samples[smp].nPan; + if(Samples[smp].uFlags[CHN_PANNING]) + chn.nPan = Samples[smp].nPan; + if(Samples[smp].uFlags[CHN_ADLIB]) + { + memory.state->Chn[nChn].Stop(); + memory.chnSettings[nChn].ticksToRender = 0; + } } } - if (pChn->rowCommand.volcmd == VOLCMD_VOLUME) { memory.chnSettings[nChn].vol = pChn->rowCommand.vol; } + switch(chn.rowCommand.volcmd) + { + case VOLCMD_VOLUME: + memory.chnSettings[nChn].vol = chn.rowCommand.vol; + break; + case VOLCMD_VOLSLIDEUP: + case VOLCMD_VOLSLIDEDOWN: + if(chn.rowCommand.vol != 0) + chn.nOldVolParam = chn.rowCommand.vol; + break; + } switch(command) { @@ -566,8 +584,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if (adjustMode & eAdjust) { - pChn->nPatternLoopCount = 0; - pChn->nPatternLoop = 0; + chn.nPatternLoopCount = 0; + chn.nPatternLoop = 0; } break; // Pattern Break @@ -585,8 +603,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } if(adjustMode & eAdjust) { - pChn->nPatternLoopCount = 0; - pChn->nPatternLoop = 0; + chn.nPatternLoopCount = 0; + chn.nPatternLoop = 0; } } } @@ -598,7 +616,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod TEMPO tempo(CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn), 0); if ((adjustMode & eAdjust) && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))) { - if (tempo.GetInt()) pChn->nOldTempo = static_cast(tempo.GetInt()); else tempo.Set(pChn->nOldTempo); + if (tempo.GetInt()) chn.nOldTempo = static_cast(tempo.GetInt()); else tempo.Set(chn.nOldTempo); } if (tempo.GetInt() >= 0x20) playState.m_nMusicTempo = tempo; @@ -633,13 +651,13 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case 0x90: if(param <= 0x91) { - pChn->dwFlags.set(CHN_SURROUND, param == 0x91); + chn.dwFlags.set(CHN_SURROUND, param == 0x91); } break; case 0xA0: // High sample offset - pChn->nOldHiOffset = param & 0x0F; + chn.nOldHiOffset = param & 0x0F; break; case 0xB0: @@ -668,7 +686,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case 0xF0: // Active macro - pChn->nActiveMacro = param & 0x0F; + chn.nActiveMacro = param & 0x0F; break; } break; @@ -693,14 +711,14 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case 0xF0: // Active macro - pChn->nActiveMacro = param & 0x0F; + chn.nActiveMacro = param & 0x0F; break; } break; case CMD_XFINEPORTAUPDOWN: // ignore high offset in compatible mode - if(((param & 0xF0) == 0xA0) && !m_playBehaviour[kFT2RestrictXCommand]) pChn->nOldHiOffset = param & 0x0F; + if(((param & 0xF0) == 0xA0) && !m_playBehaviour[kFT2RestrictXCommand]) chn.nOldHiOffset = param & 0x0F; break; } @@ -715,8 +733,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) - pChn->nOldPortaDown = param; - pChn->nOldPortaUp = param; + chn.nOldPortaDown = param; + chn.nOldPortaUp = param; } break; case CMD_PORTAMENTODOWN: @@ -725,22 +743,22 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) - pChn->nOldPortaUp = param; - pChn->nOldPortaDown = param; + chn.nOldPortaUp = param; + chn.nOldPortaDown = param; } break; // Tone-Portamento case CMD_TONEPORTAMENTO: - if (param) pChn->nPortamentoSlide = param << 2; + if (param) chn.nPortamentoSlide = param << 2; break; // Offset case CMD_OFFSET: - if (param) pChn->oldOffset = param << 8; + if (param) chn.oldOffset = param << 8; break; // Volume Slide case CMD_VOLUMESLIDE: case CMD_TONEPORTAVOL: - if (param) pChn->nOldVolumeSlide = param; + if (param) chn.nOldVolumeSlide = param; break; // Set Volume case CMD_VOLUME: @@ -763,7 +781,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(m_playBehaviour[kPerChannelGlobalVolSlide]) { // IT compatibility 16. Global volume slide params are stored per channel (FT2/IT) - if (param) pChn->nOldGlobalVolSlide = param; else param = pChn->nOldGlobalVolSlide; + if (param) chn.nOldGlobalVolSlide = param; else param = chn.nOldGlobalVolSlide; } else { if (param) playState.Chn[0].nOldGlobalVolSlide = param; else param = playState.Chn[0].nOldGlobalVolSlide; @@ -793,12 +811,12 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod Limit(playState.m_nGlobalVolume, 0, 256); break; case CMD_CHANNELVOLUME: - if (param <= 64) pChn->nGlobalVol = param; + if (param <= 64) chn.nGlobalVol = param; break; case CMD_CHANNELVOLSLIDE: { - if (param) pChn->nOldChnVolSlide = param; else param = pChn->nOldChnVolSlide; - int32 volume = pChn->nGlobalVol; + if (param) chn.nOldChnVolSlide = param; else param = chn.nOldChnVolSlide; + int32 volume = chn.nGlobalVol; if((param & 0x0F) == 0x0F && (param & 0xF0)) volume += (param >> 4); // Fine Up else if((param & 0xF0) == 0xF0 && (param & 0x0F)) @@ -808,67 +826,67 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod else // Up volume += ((param & 0xF0) >> 4) * nonRowTicks; Limit(volume, 0, 64); - pChn->nGlobalVol = volume; + chn.nGlobalVol = volume; } break; case CMD_PANNING8: - Panning(pChn, param, Pan8bit); + Panning(chn, param, Pan8bit); break; case CMD_MODCMDEX: if(param < 0x10) { // LED filter - for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) + for(CHANNELINDEX channel = 0; channel < GetNumChannels(); channel++) { - playState.Chn[chn].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); + playState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); } } MPT_FALLTHROUGH; case CMD_S3MCMDEX: if((param & 0xF0) == 0x80) { - Panning(pChn, (param & 0x0F), Pan4bit); + Panning(chn, (param & 0x0F), Pan4bit); } break; case CMD_VIBRATOVOL: - if (param) pChn->nOldVolumeSlide = param; + if (param) chn.nOldVolumeSlide = param; param = 0; MPT_FALLTHROUGH; case CMD_VIBRATO: - Vibrato(pChn, param); + Vibrato(chn, param); break; case CMD_FINEVIBRATO: - FineVibrato(pChn, param); + FineVibrato(chn, param); break; case CMD_TREMOLO: - Tremolo(pChn, param); + Tremolo(chn, param); break; case CMD_PANBRELLO: - Panbrello(pChn, param); + Panbrello(chn, param); break; } - switch(pChn->rowCommand.volcmd) + switch(chn.rowCommand.volcmd) { case VOLCMD_PANNING: - Panning(pChn, pChn->rowCommand.vol, Pan6bit); + Panning(chn, chn.rowCommand.vol, Pan6bit); break; case VOLCMD_VIBRATOSPEED: // FT2 does not automatically enable vibrato with the "set vibrato speed" command if(m_playBehaviour[kFT2VolColVibrato]) - pChn->nVibratoSpeed = pChn->rowCommand.vol & 0x0F; + chn.nVibratoSpeed = chn.rowCommand.vol & 0x0F; else - Vibrato(pChn, pChn->rowCommand.vol << 4); + Vibrato(chn, chn.rowCommand.vol << 4); break; case VOLCMD_VIBRATODEPTH: - Vibrato(pChn, pChn->rowCommand.vol); + Vibrato(chn, chn.rowCommand.vol); break; } // Process vibrato / tremolo / panbrello - switch(pChn->rowCommand.command) + switch(chn.rowCommand.command) { case CMD_VIBRATO: case CMD_FINEVIBRATO: @@ -876,10 +894,10 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(adjustMode & eAdjust) { uint32 vibTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks; - uint32 inc = pChn->nVibratoSpeed * vibTicks; + uint32 inc = chn.nVibratoSpeed * vibTicks; if(m_playBehaviour[kITVibratoTremoloPanbrello]) inc *= 4; - pChn->nVibratoPos += static_cast(inc); + chn.nVibratoPos += static_cast(inc); } break; @@ -887,10 +905,10 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(adjustMode & eAdjust) { uint32 tremTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks; - uint32 inc = pChn->nTremoloSpeed * tremTicks; + uint32 inc = chn.nTremoloSpeed * tremTicks; if(m_playBehaviour[kITVibratoTremoloPanbrello]) inc *= 4; - pChn->nTremoloPos += static_cast(inc); + chn.nTremoloPos += static_cast(inc); } break; @@ -898,8 +916,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(adjustMode & eAdjust) { // Panbrello effect is permanent in compatible mode, so actually apply panbrello for the last tick of this row - pChn->nPanbrelloPos += static_cast(pChn->nPanbrelloSpeed * (numTicks - 1)); - ProcessPanbrello(pChn); + chn.nPanbrelloPos += static_cast(chn.nPanbrelloSpeed * (numTicks - 1)); + ProcessPanbrello(chn); } break; } @@ -925,30 +943,30 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(adjustSamplePos) { // Super experimental and dirty sample seeking - pChn = playState.Chn; - for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); pChn++, nChn++) + for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { if(memory.chnSettings[nChn].ticksToRender == GetLengthMemory::IGNORE_CHANNEL) continue; - uint32 startTick = 0; - const ModCommand &m = pChn->rowCommand; + ModChannel &chn = playState.Chn[nChn]; + const ModCommand &m = chn.rowCommand; uint32 paramHi = m.param >> 4, paramLo = m.param & 0x0F; + uint32 startTick = 0; bool porta = m.command == CMD_TONEPORTAMENTO || m.command == CMD_TONEPORTAVOL || m.volcmd == VOLCMD_TONEPORTAMENTO; bool stopNote = patternLoopStartedOnThisRow; // It's too much trouble to keep those pattern loops in sync... - if(m.instr) pChn->proTrackerOffset = 0; + if(m.instr) chn.prevNoteOffset = 0; if(m.IsNote()) { if(porta && memory.chnSettings[nChn].incChanged) { // If there's a portamento, the current channel increment mustn't be 0 in NoteChange() - pChn->increment = GetChannelIncrement(pChn, pChn->nPeriod, 0); + chn.increment = GetChannelIncrement(chn, chn.nPeriod, 0); } - int32 setPan = pChn->nPan; - pChn->nNewNote = pChn->nLastNote; - if(pChn->nNewIns != 0) InstrumentChange(pChn, pChn->nNewIns, porta); - NoteChange(pChn, m.note, porta); + int32 setPan = chn.nPan; + chn.nNewNote = chn.nLastNote; + if(chn.nNewIns != 0) InstrumentChange(chn, chn.nNewIns, porta); + NoteChange(chn, m.note, porta); memory.chnSettings[nChn].incChanged = true; if((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xD0 && paramLo < numTicks) @@ -969,7 +987,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod || ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && paramHi == 0x8) || m.volcmd == VOLCMD_PANNING) { - pChn->nPan = setPan; + chn.nPan = setPan; } if(m.command == CMD_OFFSET) @@ -979,28 +997,28 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(!isExtended) { offset <<= 8; - if(offset == 0) offset = pChn->oldOffset; - offset += static_cast(pChn->nOldHiOffset) << 16; + if(offset == 0) offset = chn.oldOffset; + offset += static_cast(chn.nOldHiOffset) << 16; } - SampleOffset(*pChn, offset); + SampleOffset(chn, offset); } else if(m.command == CMD_OFFSETPERCENTAGE) { - SampleOffset(*pChn, Util::muldiv_unsigned(pChn->nLength, m.param, 255)); - } else if(m.command == CMD_REVERSEOFFSET && pChn->pModSample != nullptr) + SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 255)); + } else if(m.command == CMD_REVERSEOFFSET && chn.pModSample != nullptr) { memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far - ReverseSampleOffset(*pChn, m.param); + ReverseSampleOffset(chn, m.param); startTick = playState.m_nMusicSpeed - 1; } else if(m.volcmd == VOLCMD_OFFSET) { - if(m.vol <= CountOf(pChn->pModSample->cues) && pChn->pModSample != nullptr) + if(m.vol <= CountOf(chn.pModSample->cues) && chn.pModSample != nullptr) { SmpLength offset; if(m.vol == 0) - offset = pChn->oldOffset; + offset = chn.oldOffset; else - offset = pChn->oldOffset = pChn->pModSample->cues[m.vol - 1]; - SampleOffset(*pChn, offset); + offset = chn.oldOffset = chn.pModSample->cues[m.vol - 1]; + SampleOffset(chn, offset); } } } @@ -1014,13 +1032,13 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(m.command == CMD_VOLUME) { - pChn->nVolume = m.param * 4; + chn.nVolume = m.param * 4; } else if(m.volcmd == VOLCMD_VOLUME) { - pChn->nVolume = m.vol * 4; + chn.nVolume = m.vol * 4; } - if(pChn->pModSample && !stopNote) + if(chn.pModSample && !stopNote) { // Check if we don't want to emulate some effect and thus stop processing. if(m.command < MAX_EFFECTS) @@ -1048,7 +1066,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(stopNote) { - pChn->Stop(); + chn.Stop(); memory.chnSettings[nChn].ticksToRender = 0; } else { @@ -1066,8 +1084,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { for(uint32 i = 0; i < numTicks; i++) { - pChn->isFirstTick = (i == 0); - VolumeSlide(pChn, m.param); + chn.isFirstTick = (i == 0); + VolumeSlide(chn, m.param); } } break; @@ -1075,11 +1093,11 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case CMD_MODCMDEX: if((m.param & 0x0F) || (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) { - pChn->isFirstTick = true; + chn.isFirstTick = true; switch(m.param & 0xF0) { - case 0xA0: FineVolumeUp(pChn, m.param & 0x0F, false); break; - case 0xB0: FineVolumeDown(pChn, m.param & 0x0F, false); break; + case 0xA0: FineVolumeUp(chn, m.param & 0x0F, false); break; + case 0xB0: FineVolumeDown(chn, m.param & 0x0F, false); break; } } break; @@ -1089,15 +1107,15 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { // Play forward memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far - pChn->dwFlags.reset(CHN_PINGPONGFLAG); + chn.dwFlags.reset(CHN_PINGPONGFLAG); } else if(m.param == 0x9F) { // Reverse memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far - pChn->dwFlags.set(CHN_PINGPONGFLAG); - if(!pChn->position.GetInt() && pChn->nLength && (m.IsNote() || !pChn->dwFlags[CHN_LOOP])) + chn.dwFlags.set(CHN_PINGPONGFLAG); + if(!chn.position.GetInt() && chn.nLength && (m.IsNote() || !chn.dwFlags[CHN_LOOP])) { - pChn->position.Set(pChn->nLength - 1, SamplePosition::fractMax); + chn.position.Set(chn.nLength - 1, SamplePosition::fractMax); } } else if((m.param & 0xF0) == 0x70) { @@ -1106,11 +1124,32 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } break; } - pChn->isFirstTick = true; + chn.isFirstTick = true; switch(m.volcmd) { - case VOLCMD_FINEVOLUP: FineVolumeUp(pChn, m.vol, m_playBehaviour[kITVolColMemory]); break; - case VOLCMD_FINEVOLDOWN: FineVolumeDown(pChn, m.vol, m_playBehaviour[kITVolColMemory]); break; + case VOLCMD_FINEVOLUP: FineVolumeUp(chn, m.vol, m_playBehaviour[kITVolColMemory]); break; + case VOLCMD_FINEVOLDOWN: FineVolumeDown(chn, m.vol, m_playBehaviour[kITVolColMemory]); break; + case VOLCMD_VOLSLIDEUP: + case VOLCMD_VOLSLIDEDOWN: + { + // IT Compatibility: Volume column volume slides have their own memory + // Test case: VolColMemory.it + ModCommand::VOL vol = m.vol; + if(vol == 0 && m_playBehaviour[kITVolColMemory]) + { + vol = chn.nOldVolParam; + if(vol == 0) + break; + } + if(m.volcmd == VOLCMD_VOLSLIDEUP) + vol <<= 4; + for(uint32 i = 0; i < numTicks; i++) + { + chn.isFirstTick = (i == 0); + VolumeSlide(chn, vol); + } + } + break; } if(porta) @@ -1140,11 +1179,11 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod // This is really just a simple estimation for nested pattern loops. It should handle cases correctly where all parallel loops start and end on the same row. // If one of them starts or ends "in between", it will most likely calculate a wrong duration. // For S3M files, it's also way off. - pChn = playState.Chn; - for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++) + for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { - ModCommand::COMMAND command = pChn->rowCommand.command; - ModCommand::PARAM param = pChn->rowCommand.param; + ModChannel &chn = playState.Chn[nChn]; + ModCommand::COMMAND command = chn.rowCommand.command; + ModCommand::PARAM param = chn.rowCommand.param; if((command == CMD_S3MCMDEX && param >= 0xB1 && param <= 0xBF) || (command == CMD_MODCMDEX && param >= 0x61 && param <= 0x6F)) { @@ -1156,7 +1195,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod for(const auto &i : startTimes) { memory.elapsedTime += (memory.elapsedTime - i.first) * (double)(i.second - 1); - for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++) + for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { if(memory.chnSettings[nChn].patLoop == i.first) { @@ -1174,10 +1213,10 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(GetType() == MOD_TYPE_IT) { // IT pattern loop start row update - at the end of a pattern loop, set pattern loop start to next row (for upcoming pattern loops with missing SB0) - pChn = playState.Chn; - for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++) + for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { - if((pChn->rowCommand.command == CMD_S3MCMDEX && pChn->rowCommand.param >= 0xB1 && pChn->rowCommand.param <= 0xBF)) + ModChannel &chn = playState.Chn[nChn]; + if((chn.rowCommand.command == CMD_S3MCMDEX && chn.rowCommand.param >= 0xB1 && chn.rowCommand.param <= 0xBF)) { memory.chnSettings[nChn].patLoop = memory.elapsedTime; memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount; @@ -1229,6 +1268,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod m_PlayState.Chn[n].nVolume = std::min(memory.chnSettings[n].vol, uint8(64)) * 4; } } + if(m_opl != nullptr) m_opl->Reset(); #ifndef NO_PLUGINS // If there were any PC events, update plugin parameters to their latest value. @@ -1271,7 +1311,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { Order.SetSequence(sequence); } - visitedSongRows.Set(visitedRows); + visitedSongRows = std::move(visitedRows); } return results; @@ -1283,11 +1323,11 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod // Effects // Change sample or instrument number. -void CSoundFile::InstrumentChange(ModChannel *pChn, 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 ModSample *pSmp = &Samples[instr]; - ModCommand::NOTE note = pChn->nNewNote; + ModCommand::NOTE note = chn.nNewNote; if(note == NOTE_NONE && m_playBehaviour[kITInstrWithoutNote]) return; @@ -1298,7 +1338,7 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b // Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it if(pIns->Keyboard[note - NOTE_MIN] == 0 && m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel()) { - pChn->pModInstrument = pIns; + chn.pModInstrument = pIns; return; } @@ -1314,8 +1354,8 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b // Impulse Tracker ignores empty slots. // We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended. // Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it - pChn->pModInstrument = nullptr; - pChn->nNewIns = 0; + chn.pModInstrument = nullptr; + chn.nNewIns = 0; return; } pSmp = nullptr; @@ -1324,29 +1364,29 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b bool returnAfterVolumeAdjust = false; // instrumentChanged is used for IT carry-on env option - bool instrumentChanged = (pIns != pChn->pModInstrument); - const bool sampleChanged = (pChn->pModSample != nullptr) && (pSmp != pChn->pModSample); + bool instrumentChanged = (pIns != chn.pModInstrument); + const bool sampleChanged = (chn.pModSample != nullptr) && (pSmp != chn.pModSample); const bool newTuning = (GetType() == MOD_TYPE_MPT && pIns && pIns->pTuning); // Playback behavior change for MPT: With portamento don't change sample if it is in // the same instrument as previous sample. - if(bPorta && newTuning && pIns == pChn->pModInstrument && sampleChanged) + if(bPorta && newTuning && pIns == chn.pModInstrument && sampleChanged) return; if(sampleChanged && bPorta) { // IT compatibility: No sample change (also within multi-sample instruments) during portamento when using Compatible Gxx. // Test case: PortaInsNumCompat.it, PortaSampleCompat.it, PortaCutCompat.it - if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && !pChn->increment.IsZero()) + if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && !chn.increment.IsZero()) { - pSmp = pChn->pModSample; + pSmp = chn.pModSample; } // Special XM hack (also applies to MOD / S3M, except when playing IT-style S3Ms, such as k_vision.s3m) // Test case: PortaSmpChange.mod, PortaSmpChange.s3m if((!instrumentChanged && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && pIns) || (GetType() == MOD_TYPE_PLM) - || (GetType() == MOD_TYPE_MOD && pChn->IsSamplePlaying()) + || (GetType() == MOD_TYPE_MOD && chn.IsSamplePlaying()) || m_playBehaviour[kST3PortaSampleChange]) { // FT2 doesn't change the sample in this case, @@ -1361,14 +1401,14 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b // In the above example, no sample change happens on the second row. In the third row, sample 1 keeps playing but with the // volume and panning properties of sample 2. // Test case: InstrAfterMultisamplePorta.it - if(m_nInstruments && !instrumentChanged && sampleChanged && pChn->pCurrentSample != nullptr && m_playBehaviour[kITMultiSampleInstrumentNumber] && !pChn->rowCommand.IsNote()) + if(m_nInstruments && !instrumentChanged && sampleChanged && chn.pCurrentSample != nullptr && m_playBehaviour[kITMultiSampleInstrumentNumber] && !chn.rowCommand.IsNote()) { returnAfterVolumeAdjust = true; } // IT Compatibility: Envelope pickup after SCx cut (but don't do this when working with plugins, or else envelope carry stops working) // Test case: cut-carry.it - if(!pChn->IsSamplePlaying() && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (!pIns || !pIns->HasValidMIDIChannel())) + if(!chn.IsSamplePlaying() && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (!pIns || !pIns->HasValidMIDIChannel())) { instrumentChanged = true; } @@ -1376,29 +1416,29 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b // FT2 compatibility: new instrument + portamento = ignore new instrument number, but reload old instrument settings (the world of XM is upside down...) // And this does *not* happen if volume column portamento is used together with note delay... (handled in ProcessEffects(), where all the other note delay stuff is.) // Test case: porta-delay.xm - if(instrumentChanged && bPorta && m_playBehaviour[kFT2PortaIgnoreInstr] && (pChn->pModInstrument != nullptr || pChn->pModSample != nullptr)) + if(instrumentChanged && bPorta && m_playBehaviour[kFT2PortaIgnoreInstr] && (chn.pModInstrument != nullptr || chn.pModSample != nullptr)) { - pIns = pChn->pModInstrument; - pSmp = pChn->pModSample; + pIns = chn.pModInstrument; + pSmp = chn.pModSample; instrumentChanged = false; } else { - pChn->pModInstrument = pIns; + chn.pModInstrument = pIns; } // Update Volume - if (bUpdVol && (!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M)) || ((pSmp != nullptr && pSmp->pSample != nullptr) || pChn->HasMIDIOutput()))) + if (bUpdVol && (!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M)) || ((pSmp != nullptr && pSmp->HasSampleData()) || chn.HasMIDIOutput()))) { if(pSmp) { if(!pSmp->uFlags[SMP_NODEFAULTVOLUME]) - pChn->nVolume = pSmp->nVolume; + chn.nVolume = pSmp->nVolume; } else if(pIns && pIns->nMixPlug) { - pChn->nVolume = pChn->GetVSTVolume(); + chn.nVolume = chn.GetVSTVolume(); } else { - pChn->nVolume = 0; + chn.nVolume = 0; } } @@ -1406,21 +1446,21 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b { // ProTracker applies new instrument's finetune but keeps the old sample playing. // Test case: PortaSwapPT.mod - pChn->nFineTune = pSmp->nFineTune; + chn.nFineTune = pSmp->nFineTune; } if(returnAfterVolumeAdjust) return; // Instrument adjust - pChn->nNewIns = 0; + chn.nNewIns = 0; // IT Compatiblity: NNA is reset on every note change, not every instrument change (fixes s7xinsnum.it). if (pIns && ((!m_playBehaviour[kITNNAReset] && pSmp) || pIns->nMixPlug || instrumentChanged)) - pChn->nNNA = pIns->nNNA; + chn.nNNA = pIns->nNNA; // Update volume - pChn->UpdateInstrumentVolume(pSmp, pIns); + chn.UpdateInstrumentVolume(pSmp, pIns); // Update panning // FT2 compatibility: Only reset panning on instrument numbers, not notes (bUpdVol condition) @@ -1429,7 +1469,7 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b // Test case: PanReset.it if((bUpdVol || !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) && !m_playBehaviour[kITPanningReset]) { - ApplyInstrumentPanning(pChn, pIns, pSmp); + ApplyInstrumentPanning(chn, pIns, pSmp); } // Reset envelopes @@ -1451,132 +1491,132 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b if(m_playBehaviour[kITEnvelopeReset]) { const bool insNumber = (instr != 0); - reset = (!pChn->nLength + reset = (!chn.nLength || (insNumber && bPorta && m_SongFlags[SONG_ITCOMPATGXX]) - || (insNumber && !bPorta && pChn->dwFlags[CHN_NOTEFADE | CHN_KEYOFF] && m_SongFlags[SONG_ITOLDEFFECTS])); + || (insNumber && !bPorta && chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] && m_SongFlags[SONG_ITOLDEFFECTS])); // NOTE: IT2.14 with SB/GUS/etc. output is different. We are going after IT's WAV writer here. // For SB/GUS/etc. emulation, envelope carry should only apply when the NNA isn't set to "Note Cut". // Test case: CarryNNA.it - resetAlways = (!pChn->nFadeOutVol || instrumentChanged || pChn->dwFlags[CHN_KEYOFF]); + resetAlways = (!chn.nFadeOutVol || instrumentChanged || chn.dwFlags[CHN_KEYOFF]); } else { reset = (!bPorta || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) || m_SongFlags[SONG_ITCOMPATGXX] - || !pChn->nLength || (pChn->dwFlags[CHN_NOTEFADE] && !pChn->nFadeOutVol)); - resetAlways = !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) || instrumentChanged || pIns == nullptr || pChn->dwFlags[CHN_KEYOFF | CHN_NOTEFADE]; + || !chn.nLength || (chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol)); + resetAlways = !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) || instrumentChanged || pIns == nullptr || chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]; } if(reset) { - pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.dwFlags.set(CHN_FASTVOLRAMP); if(pIns != nullptr) { if(resetAlways) { - pChn->ResetEnvelopes(); + chn.ResetEnvelopes(); } else { - if(!pIns->VolEnv.dwFlags[ENV_CARRY]) pChn->VolEnv.Reset(); - if(!pIns->PanEnv.dwFlags[ENV_CARRY]) pChn->PanEnv.Reset(); - if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) pChn->PitchEnv.Reset(); + if(!pIns->VolEnv.dwFlags[ENV_CARRY]) chn.VolEnv.Reset(); + if(!pIns->PanEnv.dwFlags[ENV_CARRY]) chn.PanEnv.Reset(); + if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) chn.PitchEnv.Reset(); } } // IT Compatibility: Autovibrato reset if(!m_playBehaviour[kITVibratoTremoloPanbrello]) { - pChn->nAutoVibDepth = 0; - pChn->nAutoVibPos = 0; + chn.nAutoVibDepth = 0; + chn.nAutoVibPos = 0; } } else if(pIns != nullptr && !pIns->VolEnv.dwFlags[ENV_ENABLED]) { if(m_playBehaviour[kITPortamentoInstrument]) { - pChn->VolEnv.Reset(); + chn.VolEnv.Reset(); } else { - pChn->ResetEnvelopes(); + chn.ResetEnvelopes(); } } } // Invalid sample ? if(pSmp == nullptr && (pIns == nullptr || !pIns->HasValidMIDIChannel())) { - pChn->pModSample = nullptr; - pChn->nInsVol = 0; + chn.pModSample = nullptr; + chn.nInsVol = 0; return; } // Tone-Portamento doesn't reset the pingpong direction flag - if(bPorta && pSmp == pChn->pModSample && pSmp != nullptr) + if(bPorta && pSmp == chn.pModSample && pSmp != nullptr) { // If channel length is 0, we cut a previous sample using SCx. In that case, we have to update sample length, loop points, etc... - if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && pChn->nLength != 0) return; - pChn->dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); - pChn->dwFlags = (pChn->dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); - } else //if(!instrumentChanged || pChn->rowCommand.instr != 0 || !IsCompatibleMode(TRK_FASTTRACKER2)) // SampleChange.xm? + if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && chn.nLength != 0) return; + chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); + chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); + } else //if(!instrumentChanged || chn.rowCommand.instr != 0 || !IsCompatibleMode(TRK_FASTTRACKER2)) // SampleChange.xm? { - pChn->dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); + chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); // IT compatibility tentative fix: Don't change bidi loop direction when // no sample nor instrument is changed. - if((m_playBehaviour[kITPingPongNoReset] || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) && pSmp == pChn->pModSample && !instrumentChanged) - pChn->dwFlags = (pChn->dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); + if((m_playBehaviour[kITPingPongNoReset] || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) && pSmp == chn.pModSample && !instrumentChanged) + chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); else - pChn->dwFlags = (pChn->dwFlags & CHN_CHANNELFLAGS); + chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS); if(pIns) { // Copy envelope flags (we actually only need the "enabled" and "pitch" flag) - pChn->VolEnv.flags = pIns->VolEnv.dwFlags; - pChn->PanEnv.flags = pIns->PanEnv.dwFlags; - pChn->PitchEnv.flags = pIns->PitchEnv.dwFlags; + chn.VolEnv.flags = pIns->VolEnv.dwFlags; + chn.PanEnv.flags = pIns->PanEnv.dwFlags; + chn.PitchEnv.flags = pIns->PitchEnv.dwFlags; // A cutoff frequency of 0 should not be reset just because the filter envelope is enabled. // Test case: FilterEnvReset.it if((pIns->PitchEnv.dwFlags & (ENV_ENABLED | ENV_FILTER)) == (ENV_ENABLED | ENV_FILTER) && !m_playBehaviour[kITFilterBehaviour]) { - if(!pChn->nCutOff) pChn->nCutOff = 0x7F; + if(!chn.nCutOff) chn.nCutOff = 0x7F; } - if(pIns->IsCutoffEnabled()) pChn->nCutOff = pIns->GetCutoff(); - if(pIns->IsResonanceEnabled()) pChn->nResonance = pIns->GetResonance(); + if(pIns->IsCutoffEnabled()) chn.nCutOff = pIns->GetCutoff(); + if(pIns->IsResonanceEnabled()) chn.nResonance = pIns->GetResonance(); } } if(pSmp == nullptr) { - pChn->pModSample = nullptr; - pChn->nLength = 0; + chn.pModSample = nullptr; + chn.nLength = 0; return; } - if(bPorta && pChn->nLength == 0 && (m_playBehaviour[kFT2PortaNoNote] || m_playBehaviour[kITPortaNoNote])) + if(bPorta && chn.nLength == 0 && (m_playBehaviour[kFT2PortaNoNote] || m_playBehaviour[kITPortaNoNote])) { // IT/FT2 compatibility: If the note just stopped on the previous tick, prevent it from restarting. // Test cases: PortaJustStoppedNote.xm, PortaJustStoppedNote.it - pChn->increment.Set(0); + chn.increment.Set(0); } - pChn->pModSample = pSmp; - pChn->nLength = pSmp->nLength; - pChn->nLoopStart = pSmp->nLoopStart; - pChn->nLoopEnd = pSmp->nLoopEnd; + chn.pModSample = pSmp; + chn.nLength = pSmp->nLength; + chn.nLoopStart = pSmp->nLoopStart; + chn.nLoopEnd = pSmp->nLoopEnd; // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) - if(m_playBehaviour[kMODOneShotLoops] && pChn->nLoopStart == 0) pChn->nLoopEnd = pSmp->nLength; - pChn->dwFlags |= (pSmp->uFlags & (CHN_SAMPLEFLAGS | CHN_SURROUND)); + if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) chn.nLoopEnd = pSmp->nLength; + chn.dwFlags |= (pSmp->uFlags & CHN_SAMPLEFLAGS); // IT Compatibility: Autovibrato reset if(m_playBehaviour[kITVibratoTremoloPanbrello]) { - pChn->nAutoVibDepth = 0; - pChn->nAutoVibPos = 0; + chn.nAutoVibDepth = 0; + chn.nAutoVibPos = 0; } if(newTuning) { - pChn->nC5Speed = pSmp->nC5Speed; - pChn->m_CalculateFreq = true; - pChn->nFineTune = 0; + chn.nC5Speed = pSmp->nC5Speed; + chn.m_CalculateFreq = true; + chn.nFineTune = 0; } else if(!bPorta || sampleChanged || !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) { // Don't reset finetune changed by "set finetune" command. @@ -1584,12 +1624,12 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b // But *do* change the finetune if we switch to a different sample, to fix // Miranda`s axe by Jamson (jam007.xm) - this file doesn't use compatible play mode, // so we may want to use IsCompatibleMode instead if further problems arise. - pChn->nC5Speed = pSmp->nC5Speed; - pChn->nFineTune = pSmp->nFineTune; + chn.nC5Speed = pSmp->nC5Speed; + chn.nFineTune = pSmp->nFineTune; } - pChn->nTranspose = pSmp->RelativeTone; + chn.nTranspose = pSmp->RelativeTone; // FT2 compatibility: Don't reset portamento target with new instrument numbers. // Test case: Porta-Pickup.xm @@ -1597,36 +1637,36 @@ void CSoundFile::InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta, b // Test case: PortaTarget.mod if(!m_playBehaviour[kFT2PortaTargetNoReset] && GetType() != MOD_TYPE_MOD) { - pChn->nPortamentoDest = 0; + chn.nPortamentoDest = 0; } - pChn->m_PortamentoFineSteps = 0; + chn.m_PortamentoFineSteps = 0; - if(pChn->dwFlags[CHN_SUSTAINLOOP]) + if(chn.dwFlags[CHN_SUSTAINLOOP]) { - pChn->nLoopStart = pSmp->nSustainStart; - pChn->nLoopEnd = pSmp->nSustainEnd; - if(pChn->dwFlags[CHN_PINGPONGSUSTAIN]) pChn->dwFlags.set(CHN_PINGPONGLOOP); - pChn->dwFlags.set(CHN_LOOP); + chn.nLoopStart = pSmp->nSustainStart; + chn.nLoopEnd = pSmp->nSustainEnd; + if(chn.dwFlags[CHN_PINGPONGSUSTAIN]) chn.dwFlags.set(CHN_PINGPONGLOOP); + chn.dwFlags.set(CHN_LOOP); } - if(pChn->dwFlags[CHN_LOOP] && pChn->nLoopEnd < pChn->nLength) pChn->nLength = pChn->nLoopEnd; + if(chn.dwFlags[CHN_LOOP] && chn.nLoopEnd < chn.nLength) chn.nLength = chn.nLoopEnd; // Fix sample position on instrument change. This is needed for IT "on the fly" sample change. // XXX is this actually called? In ProcessEffects(), a note-on effect is emulated if there's an on the fly sample change! - if(pChn->position.GetUInt() >= pChn->nLength) + if(chn.position.GetUInt() >= chn.nLength) { if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { - pChn->position.Set(0); + chn.position.Set(0); } } } -void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bResetEnv, bool bManual) const +void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetEnv, bool bManual, CHANNELINDEX channelHint) const { if (note < NOTE_MIN) return; - const ModSample *pSmp = pChn->pModSample; - const ModInstrument *pIns = pChn->pModInstrument; + const ModSample *pSmp = chn.pModSample; + const ModInstrument *pIns = chn.pModInstrument; const bool newTuning = (GetType() == MOD_TYPE_MPT && pIns != nullptr && pIns->pTuning); // save the note that's actually used, as it's necessary to properly calculate PPS and stuff @@ -1638,7 +1678,7 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset if((n) && (n < MAX_SAMPLES)) { pSmp = &Samples[n]; - } else if(m_playBehaviour[kITEmptyNoteMapSlot] && !pChn->HasMIDIOutput()) + } else if(m_playBehaviour[kITEmptyNoteMapSlot] && !chn.HasMIDIOutput()) { // Impulse Tracker ignores empty slots. // We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended. @@ -1653,41 +1693,49 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset // Key Off (+ Invalid Note for XM - TODO is this correct?) if(note == NOTE_KEYOFF || !(GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) { - KeyOff(pChn); + KeyOff(chn); } else // Invalid Note -> Note Fade { if(/*note == NOTE_FADE && */ GetNumInstruments()) - pChn->dwFlags.set(CHN_NOTEFADE); + chn.dwFlags.set(CHN_NOTEFADE); } // Note Cut if (note == NOTE_NOTECUT) { - pChn->dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); - // IT compatibility: Stopping sample playback by setting sample increment to 0 rather than volume - // Test case: NoteOffInstr.it - if ((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) || (m_nInstruments != 0 && !m_playBehaviour[kITInstrWithNoteOff])) pChn->nVolume = 0; - if(m_playBehaviour[kITInstrWithNoteOff]) pChn->increment.Set(0); - pChn->nFadeOutVol = 0; + if(chn.dwFlags[CHN_ADLIB] && GetType() == MOD_TYPE_S3M) + { + // OPL voices are not cut but enter the release portion of their envelope + // In S3M we can still modify the volume after note-off, in legacy MPTM mode we can't + chn.dwFlags.set(CHN_KEYOFF); + } else + { + chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); + // IT compatibility: Stopping sample playback by setting sample increment to 0 rather than volume + // Test case: NoteOffInstr.it + if ((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) || (m_nInstruments != 0 && !m_playBehaviour[kITInstrWithNoteOff])) chn.nVolume = 0; + if (m_playBehaviour[kITInstrWithNoteOff]) chn.increment.Set(0); + chn.nFadeOutVol = 0; + } } // IT compatibility tentative fix: Clear channel note memory. if(m_playBehaviour[kITClearOldNoteAfterCut]) { - pChn->nNote = pChn->nNewNote = NOTE_NONE; + chn.nNote = chn.nNewNote = NOTE_NONE; } return; } if(newTuning) { - if(!bPorta || pChn->nNote == NOTE_NONE) - pChn->nPortamentoDest = 0; + if(!bPorta || chn.nNote == NOTE_NONE) + chn.nPortamentoDest = 0; else { - pChn->nPortamentoDest = pIns->pTuning->GetStepDistance(pChn->nNote, pChn->m_PortamentoFineSteps, static_cast(note), 0); - //Here pChn->nPortamentoDest means 'steps to slide'. - pChn->m_PortamentoFineSteps = -pChn->nPortamentoDest; + chn.nPortamentoDest = pIns->pTuning->GetStepDistance(chn.nNote, chn.m_PortamentoFineSteps, static_cast(note), 0); + //Here chn.nPortamentoDest means 'steps to slide'. + chn.m_PortamentoFineSteps = -chn.nPortamentoDest; } } @@ -1695,22 +1743,22 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset { if(pSmp) { - pChn->nTranspose = pSmp->RelativeTone; - pChn->nFineTune = pSmp->nFineTune; + chn.nTranspose = pSmp->RelativeTone; + chn.nFineTune = pSmp->nFineTune; } } // IT Compatibility: Update multisample instruments frequency even if instrument is not specified (fixes the guitars in spx-shuttledeparture.it) // Test case: freqreset-noins.it if(!bPorta && pSmp && m_playBehaviour[kITMultiSampleBehaviour]) - pChn->nC5Speed = pSmp->nC5Speed; + chn.nC5Speed = pSmp->nC5Speed; - if(bPorta && !pChn->IsSamplePlaying()) + if(bPorta && !chn.IsSamplePlaying()) { if(m_playBehaviour[kFT2PortaNoNote]) { // FT2 Compatibility: Ignore notes with portamento if there was no note playing. // Test case: 3xx-no-old-samp.xm - pChn->nPeriod = 0; + chn.nPeriod = 0; return; } else if(m_playBehaviour[kITPortaNoNote]) { @@ -1720,9 +1768,9 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset } } - if(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED|MOD_TYPE_MOD)) + if(UseFinetuneAndTranspose()) { - note += pChn->nTranspose; + note += chn.nTranspose; // RealNote = PatternNote + RelativeTone; (0..118, 0 = C-0, 118 = A#9) Limit(note, NOTE_MIN + 11, NOTE_MIN + 130); // 119 possible notes } else @@ -1732,63 +1780,63 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset if(m_playBehaviour[kITRealNoteMapping]) { // need to memorize the original note for various effects (e.g. PPS) - pChn->nNote = static_cast(Clamp(realnote, NOTE_MIN, NOTE_MAX)); + chn.nNote = static_cast(Clamp(realnote, NOTE_MIN, NOTE_MAX)); } else { - pChn->nNote = static_cast(note); + chn.nNote = static_cast(note); } - pChn->m_CalculateFreq = true; + chn.m_CalculateFreq = true; if ((!bPorta) || (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) - pChn->nNewIns = 0; + chn.nNewIns = 0; - uint32 period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC5Speed); - pChn->nPanbrelloOffset = 0; + uint32 period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); + chn.nPanbrelloOffset = 0; // IT compatibility: Sample and instrument panning is only applied on note change, not instrument change // Test case: PanReset.it - if(m_playBehaviour[kITPanningReset]) ApplyInstrumentPanning(pChn, pIns, pSmp); + if(m_playBehaviour[kITPanningReset]) ApplyInstrumentPanning(chn, pIns, pSmp); if(bResetEnv && !bPorta) { - pChn->nVolSwing = pChn->nPanSwing = 0; - pChn->nResSwing = pChn->nCutSwing = 0; + chn.nVolSwing = chn.nPanSwing = 0; + chn.nResSwing = chn.nCutSwing = 0; if(pIns) { // IT Compatiblity: NNA is reset on every note change, not every instrument change (fixes spx-farspacedance.it). - if(m_playBehaviour[kITNNAReset]) pChn->nNNA = pIns->nNNA; + if(m_playBehaviour[kITNNAReset]) chn.nNNA = pIns->nNNA; - if(!pIns->VolEnv.dwFlags[ENV_CARRY]) pChn->VolEnv.Reset(); - if(!pIns->PanEnv.dwFlags[ENV_CARRY]) pChn->PanEnv.Reset(); - if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) pChn->PitchEnv.Reset(); + if(!pIns->VolEnv.dwFlags[ENV_CARRY]) chn.VolEnv.Reset(); + if(!pIns->PanEnv.dwFlags[ENV_CARRY]) chn.PanEnv.Reset(); + if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) chn.PitchEnv.Reset(); // Volume Swing if(pIns->nVolSwing) { - pChn->nVolSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nVolSwing) / 64 + 1) * (m_playBehaviour[kITSwingBehaviour] ? pChn->nInsVol : ((pChn->nVolume + 1) / 2)) / 199); + chn.nVolSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nVolSwing) / 64 + 1) * (m_playBehaviour[kITSwingBehaviour] ? chn.nInsVol : ((chn.nVolume + 1) / 2)) / 199); } // Pan Swing if(pIns->nPanSwing) { - pChn->nPanSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nPanSwing * 4) / 128)); + chn.nPanSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nPanSwing * 4) / 128)); if(!m_playBehaviour[kITSwingBehaviour]) { - pChn->nRestorePanOnNewNote = static_cast(pChn->nPan + 1); + chn.nRestorePanOnNewNote = static_cast(chn.nPan + 1); } } // Cutoff Swing if(pIns->nCutSwing) { int32 d = ((int32)pIns->nCutSwing * (int32)(static_cast(mpt::random(AccessPRNG())) + 1)) / 128; - pChn->nCutSwing = static_cast((d * pChn->nCutOff + 1) / 128); - pChn->nRestoreCutoffOnNewNote = pChn->nCutOff + 1; + chn.nCutSwing = static_cast((d * chn.nCutOff + 1) / 128); + chn.nRestoreCutoffOnNewNote = chn.nCutOff + 1; } // Resonance Swing if(pIns->nResSwing) { int32 d = ((int32)pIns->nResSwing * (int32)(static_cast(mpt::random(AccessPRNG())) + 1)) / 128; - pChn->nResSwing = static_cast((d * pChn->nResonance + 1) / 128); - pChn->nRestoreResonanceOnNewNote = pChn->nResonance + 1; + chn.nResSwing = static_cast((d * chn.nResonance + 1) / 128); + chn.nRestoreResonanceOnNewNote = chn.nResonance + 1; } } } @@ -1796,7 +1844,7 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset if(!pSmp) return; if(period) { - if((!bPorta) || (!pChn->nPeriod)) pChn->nPeriod = period; + if((!bPorta) || (!chn.nPeriod)) chn.nPeriod = period; if(!newTuning) { // FT2 compatibility: Don't reset portamento target with new notes. @@ -1807,66 +1855,66 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset // Test case: PortaReset.it if(bPorta || !(m_playBehaviour[kFT2PortaTargetNoReset] || m_playBehaviour[kITClearPortaTarget] || GetType() == MOD_TYPE_MOD)) { - pChn->nPortamentoDest = period; + chn.nPortamentoDest = period; } } - if(!bPorta || (!pChn->nLength && !(GetType() & MOD_TYPE_S3M))) + if(!bPorta || (!chn.nLength && !(GetType() & MOD_TYPE_S3M))) { - pChn->pModSample = pSmp; - pChn->nLength = pSmp->nLength; - pChn->nLoopEnd = pSmp->nLength; - pChn->nLoopStart = 0; - pChn->position.Set(0); - if(m_SongFlags[SONG_PT_MODE] && !pChn->rowCommand.instr) + chn.pModSample = pSmp; + chn.nLength = pSmp->nLength; + chn.nLoopEnd = pSmp->nLength; + chn.nLoopStart = 0; + chn.position.Set(0); + if((m_SongFlags[SONG_PT_MODE] || m_playBehaviour[kST3OffsetWithoutInstrument]) && !chn.rowCommand.instr) { - pChn->position.SetInt(std::min(pChn->proTrackerOffset, pChn->nLength - 1)); + chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - 1)); } else { - pChn->proTrackerOffset = 0; + chn.prevNoteOffset = 0; } - pChn->dwFlags = (pChn->dwFlags & CHN_CHANNELFLAGS) | (pSmp->uFlags & (CHN_SAMPLEFLAGS | CHN_SURROUND)); - pChn->dwFlags.reset(CHN_PORTAMENTO); - if(pChn->dwFlags[CHN_SUSTAINLOOP]) + chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | (pSmp->uFlags & CHN_SAMPLEFLAGS); + chn.dwFlags.reset(CHN_PORTAMENTO); + if(chn.dwFlags[CHN_SUSTAINLOOP]) { - pChn->nLoopStart = pSmp->nSustainStart; - pChn->nLoopEnd = pSmp->nSustainEnd; - pChn->dwFlags.set(CHN_PINGPONGLOOP, pChn->dwFlags[CHN_PINGPONGSUSTAIN]); - pChn->dwFlags.set(CHN_LOOP); - if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; - } else if(pChn->dwFlags[CHN_LOOP]) + chn.nLoopStart = pSmp->nSustainStart; + chn.nLoopEnd = pSmp->nSustainEnd; + chn.dwFlags.set(CHN_PINGPONGLOOP, chn.dwFlags[CHN_PINGPONGSUSTAIN]); + chn.dwFlags.set(CHN_LOOP); + if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd; + } else if(chn.dwFlags[CHN_LOOP]) { - pChn->nLoopStart = pSmp->nLoopStart; - pChn->nLoopEnd = pSmp->nLoopEnd; - if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; + chn.nLoopStart = pSmp->nLoopStart; + chn.nLoopEnd = pSmp->nLoopEnd; + if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd; } // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) - if(m_playBehaviour[kMODOneShotLoops] && pChn->nLoopStart == 0) pChn->nLoopEnd = pChn->nLength = pSmp->nLength; + if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) chn.nLoopEnd = chn.nLength = pSmp->nLength; - if(pChn->dwFlags[CHN_REVERSE]) + if(chn.dwFlags[CHN_REVERSE]) { - pChn->dwFlags.set(CHN_PINGPONGFLAG); - pChn->position.SetInt(pChn->nLength - 1); + chn.dwFlags.set(CHN_PINGPONGFLAG); + chn.position.SetInt(chn.nLength - 1); } // Handle "retrigger" waveform type - if(pChn->nVibratoType < 4) + if(chn.nVibratoType < 4) { // IT Compatibilty: Slightly different waveform offsets (why does MPT have two different offsets here with IT old effects enabled and disabled?) if(!m_playBehaviour[kITVibratoTremoloPanbrello] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) - pChn->nVibratoPos = 0x10; + chn.nVibratoPos = 0x10; else if(GetType() == MOD_TYPE_MTM) - pChn->nVibratoPos = 0x20; + chn.nVibratoPos = 0x20; else if(!(GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM))) - pChn->nVibratoPos = 0; + chn.nVibratoPos = 0; } // IT Compatibility: No "retrigger" waveform here - if(!m_playBehaviour[kITVibratoTremoloPanbrello] && pChn->nTremoloType < 4) + if(!m_playBehaviour[kITVibratoTremoloPanbrello] && chn.nTremoloType < 4) { - pChn->nTremoloPos = 0; + chn.nTremoloPos = 0; } } - if(pChn->position.GetUInt() >= pChn->nLength) pChn->position.SetInt(pChn->nLoopStart); + if(chn.position.GetUInt() >= chn.nLength) chn.position.SetInt(chn.nLoopStart); } else { bPorta = false; @@ -1874,44 +1922,44 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset if (!bPorta || (!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM))) - || (pChn->dwFlags[CHN_NOTEFADE] && !pChn->nFadeOutVol) - || (m_SongFlags[SONG_ITCOMPATGXX] && pChn->rowCommand.instr != 0)) + || (chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol) + || (m_SongFlags[SONG_ITCOMPATGXX] && chn.rowCommand.instr != 0)) { - if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) && pChn->dwFlags[CHN_NOTEFADE] && !pChn->nFadeOutVol) + if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) && chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol) { - pChn->ResetEnvelopes(); + chn.ResetEnvelopes(); // IT Compatibility: Autovibrato reset if(!m_playBehaviour[kITVibratoTremoloPanbrello]) { - pChn->nAutoVibDepth = 0; - pChn->nAutoVibPos = 0; + chn.nAutoVibDepth = 0; + chn.nAutoVibPos = 0; } - pChn->dwFlags.reset(CHN_NOTEFADE); - pChn->nFadeOutVol = 65536; + chn.dwFlags.reset(CHN_NOTEFADE); + chn.nFadeOutVol = 65536; } - if ((!bPorta) || (!m_SongFlags[SONG_ITCOMPATGXX]) || (pChn->rowCommand.instr)) + if ((!bPorta) || (!m_SongFlags[SONG_ITCOMPATGXX]) || (chn.rowCommand.instr)) { - if ((!(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (pChn->rowCommand.instr)) + if ((!(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (chn.rowCommand.instr)) { - pChn->dwFlags.reset(CHN_NOTEFADE); - pChn->nFadeOutVol = 65536; + chn.dwFlags.reset(CHN_NOTEFADE); + chn.nFadeOutVol = 65536; } } } // IT compatibility: Don't reset key-off flag on porta notes unless Compat Gxx is enabled // Test case: Off-Porta.it, Off-Porta-CompatGxx.it - if(m_playBehaviour[kITDontResetNoteOffOnPorta] && bPorta && (!m_SongFlags[SONG_ITCOMPATGXX] || pChn->rowCommand.instr == 0)) - pChn->dwFlags.reset(CHN_EXTRALOUD); + if(m_playBehaviour[kITDontResetNoteOffOnPorta] && bPorta && (!m_SongFlags[SONG_ITCOMPATGXX] || chn.rowCommand.instr == 0)) + chn.dwFlags.reset(CHN_EXTRALOUD); else - pChn->dwFlags.reset(CHN_EXTRALOUD | CHN_KEYOFF); + chn.dwFlags.reset(CHN_EXTRALOUD | CHN_KEYOFF); // Enable Ramping if(!bPorta) { - pChn->nLeftVU = pChn->nRightVU = 0xFF; - pChn->dwFlags.reset(CHN_FILTER); - pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.nLeftVU = chn.nRightVU = 0xFF; + chn.dwFlags.reset(CHN_FILTER); + chn.dwFlags.set(CHN_FASTVOLRAMP); // IT compatibility 15. Retrigger is reset in RetrigNote (Tremor doesn't store anything here, so we just don't reset this as well) if(!m_playBehaviour[kITRetrigger] && !m_playBehaviour[kITTremor]) @@ -1919,64 +1967,66 @@ void CSoundFile::NoteChange(ModChannel *pChn, int note, bool bPorta, bool bReset // FT2 compatibility: Retrigger is reset in RetrigNote, tremor in ProcessEffects if(!m_playBehaviour[kFT2Retrigger] && !m_playBehaviour[kFT2Tremor]) { - pChn->nRetrigCount = 0; - pChn->nTremorCount = 0; + chn.nRetrigCount = 0; + chn.nTremorCount = 0; } } if(bResetEnv) { - pChn->nAutoVibDepth = 0; - pChn->nAutoVibPos = 0; + chn.nAutoVibDepth = 0; + chn.nAutoVibPos = 0; } - pChn->rightVol = pChn->leftVol = 0; + chn.rightVol = chn.leftVol = 0; bool useFilter = !m_SongFlags[SONG_MPTFILTERMODE]; // Setup Initial Filter for this note if(pIns) { if(pIns->IsResonanceEnabled()) { - pChn->nResonance = pIns->GetResonance(); + chn.nResonance = pIns->GetResonance(); useFilter = true; } if(pIns->IsCutoffEnabled()) { - pChn->nCutOff = pIns->GetCutoff(); + chn.nCutOff = pIns->GetCutoff(); useFilter = true; } if(useFilter && (pIns->nFilterMode != FLTMODE_UNCHANGED)) { - pChn->nFilterMode = pIns->nFilterMode; + chn.nFilterMode = pIns->nFilterMode; } } else { - pChn->nVolSwing = pChn->nPanSwing = 0; - pChn->nCutSwing = pChn->nResSwing = 0; + chn.nVolSwing = chn.nPanSwing = 0; + chn.nCutSwing = chn.nResSwing = 0; } - if((pChn->nCutOff < 0x7F || m_playBehaviour[kITFilterBehaviour]) && useFilter) + if((chn.nCutOff < 0x7F || m_playBehaviour[kITFilterBehaviour]) && useFilter) { - SetupChannelFilter(pChn, true); + int cutoff = SetupChannelFilter(chn, true); + if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->Volume(channelHint, chn.nCutOff / 2u, true); } } // Special case for MPT - if (bManual) pChn->dwFlags.reset(CHN_MUTE); - if((pChn->dwFlags[CHN_MUTE] && (m_MixerSettings.MixerFlags & SNDMIX_MUTECHNMODE)) - || (pChn->pModSample != nullptr && pChn->pModSample->uFlags[CHN_MUTE] && !bManual) - || (pChn->pModInstrument != nullptr && pChn->pModInstrument->dwFlags[INS_MUTE] && !bManual)) + if (bManual) chn.dwFlags.reset(CHN_MUTE); + if((chn.dwFlags[CHN_MUTE] && (m_MixerSettings.MixerFlags & SNDMIX_MUTECHNMODE)) + || (chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_MUTE] && !bManual) + || (chn.pModInstrument != nullptr && chn.pModInstrument->dwFlags[INS_MUTE] && !bManual)) { - if (!bManual) pChn->nPeriod = 0; + if (!bManual) chn.nPeriod = 0; } // Reset the Amiga resampler for this channel if(!bPorta) { - pChn->paulaState.Reset(); + chn.paulaState.Reset(); } } // Apply sample or instrumernt panning -void CSoundFile::ApplyInstrumentPanning(ModChannel *pChn, const ModInstrument *instr, const ModSample *smp) const +void CSoundFile::ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *instr, const ModSample *smp) const { int32 newPan = int32_min; // Default instrument panning @@ -1988,12 +2038,12 @@ void CSoundFile::ApplyInstrumentPanning(ModChannel *pChn, const ModInstrument *i if(newPan != int32_min) { - pChn->nPan = newPan; + chn.nPan = newPan; // IT compatibility: Sample and instrument panning overrides channel surround status. // Test case: SmpInsPanSurround.it if(m_playBehaviour[kPanOverride] && !m_SongFlags[SONG_SURROUNDPAN]) { - pChn->dwFlags.reset(CHN_SURROUND); + chn.dwFlags.reset(CHN_SURROUND); } } } @@ -2001,31 +2051,44 @@ void CSoundFile::ApplyInstrumentPanning(ModChannel *pChn, const ModInstrument *i CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const { - const ModChannel *pChn = &m_PlayState.Chn[nChn]; - // Check for empty channel - const ModChannel *pi = &m_PlayState.Chn[m_nChannels]; - for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++, pi++) if (!pi->nLength) return i; - if (!pChn->nFadeOutVol) return 0; + for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) + { + const ModChannel &c = m_PlayState.Chn[i]; + // No sample and no plugin playing + if(!c.nLength && !c.HasMIDIOutput()) + return i; + // Plugin channel with already released note + if(!c.nLength && c.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]) + return i; + } + + uint32 vol = 0x800000; + if(nChn < MAX_CHANNELS) + { + const ModChannel &srcChn = m_PlayState.Chn[nChn]; + if(!srcChn.nFadeOutVol && srcChn.nLength) return 0; + vol = (srcChn.nRealVolume << 9) | srcChn.nVolume; + } // All channels are used: check for lowest volume CHANNELINDEX result = 0; - uint32 vol = (1u << (14 + 9)) / 4u; // 25% - uint32 envpos = uint32_max; - const ModChannel *pj = &m_PlayState.Chn[m_nChannels]; - for (CHANNELINDEX j = m_nChannels; j < MAX_CHANNELS; j++, pj++) + uint32 envpos = 0; + for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { - if (!pj->nFadeOutVol) return j; + const ModChannel &c = m_PlayState.Chn[i]; + if(c.nLength && !c.nFadeOutVol) + return i; // Use a combination of real volume [14 bit] (which includes volume envelopes, but also potentially global volume) and note volume [9 bit]. // Rationale: We need volume envelopes in case e.g. all NNA channels are playing at full volume but are looping on a 0-volume envelope node. // But if global volume is not applied to master and the global volume temporarily drops to 0, we would kill arbitrary channels. Hence, add the note volume as well. - uint32 v = (pj->nRealVolume << 9) | pj->nVolume; - if(pj->dwFlags[CHN_LOOP]) v >>= 1; - if ((v < vol) || ((v == vol) && (pj->VolEnv.nEnvPosition > envpos))) + uint32 v = (c.nRealVolume << 9) | c.nVolume; + if(c.dwFlags[CHN_LOOP]) v /= 2; + if((v < vol) || ((v == vol) && (c.VolEnv.nEnvPosition > envpos))) { - envpos = pj->VolEnv.nEnvPosition; + envpos = c.VolEnv.nEnvPosition; vol = v; - result = j; + result = i; } } return result; @@ -2067,6 +2130,10 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo srcChn.position.Set(0); srcChn.nROfs = srcChn.nLOfs = 0; srcChn.rightVol = srcChn.leftVol = 0; + if(srcChn.dwFlags[CHN_ADLIB] && m_opl) + { + m_opl->NoteCut(nChn); + } return nnaChn; } if(instr > GetNumInstruments()) instr = 0; @@ -2103,6 +2170,8 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo // Duplicate Check Type switch(chn.pModInstrument->nDCT) { + case DCT_NONE: + break; // Note case DCT_NOTE: if(note && chn.nNote == note && pIns == chn.pModInstrument) bOk = true; @@ -2150,16 +2219,22 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo { // Cut case DNA_NOTECUT: - KeyOff(&chn); + KeyOff(chn); chn.nVolume = 0; + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteCut(i); break; // Note Off case DNA_NOTEOFF: - KeyOff(&chn); + KeyOff(chn); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteOff(i); break; // Note Fade case DNA_NOTEFADE: chn.dwFlags.set(CHN_NOTEFADE); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteOff(i); break; } if(!chn.nVolume) @@ -2186,7 +2261,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo { // apply NNA to this plugin iff it is currently playing a note on this tracker channel // (and if it is playing a note, we know that would be the last note played on this chan). - applyNNAtoPlug = pPlugin->IsNotePlaying(srcChn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]), GetBestMidiChannel(nChn), nChn); + applyNNAtoPlug = pPlugin->IsNotePlaying(srcChn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]), nChn); } } } @@ -2218,6 +2293,8 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo SendMIDINote(nChn, NOTE_KEYOFF, 0); srcChn.nArpeggioLastNote = NOTE_NONE; break; + case NNA_CONTINUE: + break; } } #endif // NO_PLUGINS @@ -2226,14 +2303,24 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo switch(srcChn.nNNA) { case NNA_NOTEOFF: - KeyOff(&chn); + KeyOff(chn); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteOff(nChn); break; case NNA_NOTECUT: chn.nFadeOutVol = 0; chn.dwFlags.set(CHN_NOTEFADE); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteCut(nChn); break; case NNA_NOTEFADE: chn.dwFlags.set(CHN_NOTEFADE); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteOff(nChn); + break; + case NNA_CONTINUE: + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->MoveChannel(nChn, nnaChn); break; } if(!chn.nVolume) @@ -2253,31 +2340,31 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo bool CSoundFile::ProcessEffects() { - ModChannel *pChn = m_PlayState.Chn; ROWINDEX nBreakRow = ROWINDEX_INVALID; // Is changed if a break to row command is encountered ROWINDEX nPatLoopRow = ROWINDEX_INVALID; // Is changed if a pattern loop jump-back is executed ORDERINDEX nPosJump = ORDERINDEX_INVALID; - for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, pChn++) + for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { + ModChannel &chn = m_PlayState.Chn[nChn]; const uint32 tickCount = m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay); - uint32 instr = pChn->rowCommand.instr; - ModCommand::VOLCMD volcmd = pChn->rowCommand.volcmd; - uint32 vol = pChn->rowCommand.vol; - ModCommand::COMMAND cmd = pChn->rowCommand.command; - uint32 param = pChn->rowCommand.param; - bool bPorta = pChn->rowCommand.IsPortamento(); + uint32 instr = chn.rowCommand.instr; + ModCommand::VOLCMD volcmd = chn.rowCommand.volcmd; + uint32 vol = chn.rowCommand.vol; + ModCommand::COMMAND cmd = chn.rowCommand.command; + uint32 param = chn.rowCommand.param; + bool bPorta = chn.rowCommand.IsPortamento(); uint32 nStartTick = 0; - pChn->isFirstTick = m_SongFlags[SONG_FIRSTTICK]; + chn.isFirstTick = m_SongFlags[SONG_FIRSTTICK]; // Process parameter control note. - if(pChn->rowCommand.note == NOTE_PC) + if(chn.rowCommand.note == NOTE_PC) { #ifndef NO_PLUGINS - const PLUGINDEX plug = pChn->rowCommand.instr; - const PlugParamIndex plugparam = pChn->rowCommand.GetValueVolCol(); - const PlugParamValue value = pChn->rowCommand.GetValueEffectCol() / PlugParamValue(ModCommand::maxColumnValue); + const PLUGINDEX plug = chn.rowCommand.instr; + const PlugParamIndex plugparam = chn.rowCommand.GetValueVolCol(); + const PlugParamValue value = chn.rowCommand.GetValueEffectCol() / PlugParamValue(ModCommand::maxColumnValue); if(plug > 0 && plug <= MAX_MIXPLUGINS && m_MixPlugins[plug - 1].pMixPlugin) m_MixPlugins[plug-1].pMixPlugin->SetParameter(plugparam, value); @@ -2290,40 +2377,40 @@ bool CSoundFile::ProcessEffects() // the need for parameter control. The condition cmd == 0 // is to make sure that m_nPlugParamValueStep != 0 because // of NOTE_PCS, not because of macro. - if(pChn->rowCommand.note == NOTE_PCS || (cmd == CMD_NONE && pChn->m_plugParamValueStep != 0)) + if(chn.rowCommand.note == NOTE_PCS || (cmd == CMD_NONE && chn.m_plugParamValueStep != 0)) { #ifndef NO_PLUGINS const bool isFirstTick = m_SongFlags[SONG_FIRSTTICK]; if(isFirstTick) - pChn->m_RowPlug = pChn->rowCommand.instr; - const PLUGINDEX plugin = pChn->m_RowPlug; + chn.m_RowPlug = chn.rowCommand.instr; + const PLUGINDEX plugin = chn.m_RowPlug; const bool hasValidPlug = (plugin > 0 && plugin <= MAX_MIXPLUGINS && m_MixPlugins[plugin - 1].pMixPlugin); if(hasValidPlug) { if(isFirstTick) - pChn->m_RowPlugParam = ModCommand::GetValueVolCol(pChn->rowCommand.volcmd, pChn->rowCommand.vol); - const PlugParamIndex plugparam = pChn->m_RowPlugParam; + chn.m_RowPlugParam = ModCommand::GetValueVolCol(chn.rowCommand.volcmd, chn.rowCommand.vol); + const PlugParamIndex plugparam = chn.m_RowPlugParam; if(isFirstTick) { - PlugParamValue targetvalue = ModCommand::GetValueEffectCol(pChn->rowCommand.command, pChn->rowCommand.param) / PlugParamValue(ModCommand::maxColumnValue); - pChn->m_plugParamTargetValue = targetvalue; - pChn->m_plugParamValueStep = (targetvalue - m_MixPlugins[plugin-1].pMixPlugin->GetParameter(plugparam)) / float(GetNumTicksOnCurrentRow()); + PlugParamValue targetvalue = ModCommand::GetValueEffectCol(chn.rowCommand.command, chn.rowCommand.param) / PlugParamValue(ModCommand::maxColumnValue); + chn.m_plugParamTargetValue = targetvalue; + chn.m_plugParamValueStep = (targetvalue - m_MixPlugins[plugin-1].pMixPlugin->GetParameter(plugparam)) / float(GetNumTicksOnCurrentRow()); } if(m_PlayState.m_nTickCount + 1 == GetNumTicksOnCurrentRow()) { // On last tick, set parameter exactly to target value. - m_MixPlugins[plugin - 1].pMixPlugin->SetParameter(plugparam, pChn->m_plugParamTargetValue); + m_MixPlugins[plugin - 1].pMixPlugin->SetParameter(plugparam, chn.m_plugParamTargetValue); } else - m_MixPlugins[plugin - 1].pMixPlugin->ModifyParameter(plugparam, pChn->m_plugParamValueStep); + m_MixPlugins[plugin - 1].pMixPlugin->ModifyParameter(plugparam, chn.m_plugParamValueStep); } #endif // NO_PLUGINS } // Apart from changing parameters, parameter control notes are intended to be 'invisible'. // To achieve this, clearing the note data so that rest of the process sees the row as empty row. - if(ModCommand::IsPcNote(pChn->rowCommand.note)) + if(ModCommand::IsPcNote(chn.rowCommand.note)) { - pChn->ClearRowCmd(); + chn.ClearRowCmd(); instr = 0; volcmd = VOLCMD_NONE; vol = 0; @@ -2335,7 +2422,7 @@ bool CSoundFile::ProcessEffects() // Process Invert Loop (MOD Effect, called every row if it's active) if(!m_SongFlags[SONG_FIRSTTICK]) { - InvertLoop(&m_PlayState.Chn[nChn]); + InvertLoop(m_PlayState.Chn[nChn]); } else { if(instr) m_PlayState.Chn[nChn].nEFxOffset = 0; @@ -2351,9 +2438,9 @@ bool CSoundFile::ProcessEffects() } else if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX)) { if ((!param) && (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) - param = pChn->nOldCmdEx; + param = chn.nOldCmdEx; else - pChn->nOldCmdEx = static_cast(param); + chn.nOldCmdEx = static_cast(param); // Note Delay ? if ((param & 0xF0) == 0xD0) @@ -2373,7 +2460,7 @@ bool CSoundFile::ProcessEffects() // Additional test case: tickdelay.it if(instr) { - pChn->nNewIns = static_cast(instr); + chn.nNewIns = static_cast(instr); } continue; } @@ -2384,7 +2471,7 @@ bool CSoundFile::ProcessEffects() || ((param & 0xF0) == 0xB0 && cmd == CMD_S3MCMDEX)) && !(m_playBehaviour[kST3NoMutedChannels] && ChnSettings[nChn].dwFlags[CHN_MUTE])) // not even effects are processed on muted S3M channels { - ROWINDEX nloop = PatternLoop(pChn, param & 0x0F); + ROWINDEX nloop = PatternLoop(chn, param & 0x0F); if (nloop != ROWINDEX_INVALID) { // FT2 compatibility: E6x overwrites jump targets of Dxx effects that are located left of the E6x effect. @@ -2402,8 +2489,8 @@ bool CSoundFile::ProcessEffects() // ST3 doesn't have per-channel pattern loop memory, so spam all changes to other channels as well. for (CHANNELINDEX i = 0; i < GetNumChannels(); i++) { - m_PlayState.Chn[i].nPatternLoop = pChn->nPatternLoop; - m_PlayState.Chn[i].nPatternLoopCount = pChn->nPatternLoopCount; + m_PlayState.Chn[i].nPatternLoop = chn.nPatternLoop; + m_PlayState.Chn[i].nPatternLoopCount = chn.nPatternLoopCount; } } } else if ((param & 0xF0) == 0xE0) @@ -2434,11 +2521,11 @@ bool CSoundFile::ProcessEffects() param = 0x90 | (param & 0x0F); } - if(nStartTick != 0 && pChn->rowCommand.note == NOTE_KEYOFF && pChn->rowCommand.volcmd == VOLCMD_PANNING && m_playBehaviour[kFT2PanWithDelayedNoteOff]) + if(nStartTick != 0 && chn.rowCommand.note == NOTE_KEYOFF && chn.rowCommand.volcmd == VOLCMD_PANNING && m_playBehaviour[kFT2PanWithDelayedNoteOff]) { // FT2 compatibility: If there's a delayed note off, panning commands are ignored. WTF! // Test case: PanOff.xm - pChn->rowCommand.volcmd = VOLCMD_NONE; + chn.rowCommand.volcmd = VOLCMD_NONE; } bool triggerNote = (m_PlayState.m_nTickCount == nStartTick); // Can be delayed by a note delay effect @@ -2460,7 +2547,7 @@ bool CSoundFile::ProcessEffects() // Test case: SlideDelay.it if(m_playBehaviour[kITFirstTickHandling]) { - pChn->isFirstTick = tickCount == nStartTick; + chn.isFirstTick = tickCount == nStartTick; } // FT2 compatibility: Note + portamento + note delay = no portamento @@ -2474,16 +2561,16 @@ bool CSoundFile::ProcessEffects() { // Instrument number resets the stacked ProTracker offset. // Test case: ptoffset.mod - pChn->proTrackerOffset = 0; + chn.prevNoteOffset = 0; // ProTracker compatibility: Sample properties are always loaded on the first tick, even when there is a note delay. // Test case: InstrDelay.mod - if(!triggerNote && pChn->IsSamplePlaying()) + if(!triggerNote && chn.IsSamplePlaying()) { - pChn->nNewIns = static_cast(instr); + chn.nNewIns = static_cast(instr); if(instr <= GetNumSamples()) { - pChn->nVolume = Samples[instr].nVolume; - pChn->nFineTune = Samples[instr].nFineTune; + chn.nVolume = Samples[instr].nVolume; + chn.nFineTune = Samples[instr].nFineTune; } } } @@ -2491,14 +2578,14 @@ bool CSoundFile::ProcessEffects() // Handles note/instrument/volume changes if(triggerNote) { - ModCommand::NOTE note = pChn->rowCommand.note; - if(instr) pChn->nNewIns = static_cast(instr); + ModCommand::NOTE note = chn.rowCommand.note; + if(instr) chn.nNewIns = static_cast(instr); if(ModCommand::IsNote(note) && m_playBehaviour[kFT2Transpose]) { // Notes that exceed FT2's limit are completely ignored. // Test case: NoteLimit.xm - int transpose = pChn->nTranspose; + int transpose = chn.nTranspose; if(instr && !bPorta) { // Refresh transpose @@ -2531,7 +2618,7 @@ bool CSoundFile::ProcessEffects() { // IT compatibility: Invalid instrument numbers do nothing, but they are remembered for upcoming notes and do not trigger a note in that case. // Test case: InstrumentNumberChange.it - INSTRUMENTINDEX instrToCheck = static_cast((instr != 0) ? instr : pChn->nOldIns); + INSTRUMENTINDEX instrToCheck = static_cast((instr != 0) ? instr : chn.nOldIns); if(instrToCheck != 0 && (instrToCheck > GetNumInstruments() || Instruments[instrToCheck] == nullptr)) { note = NOTE_NONE; @@ -2553,7 +2640,7 @@ bool CSoundFile::ProcessEffects() bool reloadSampleSettings = (m_playBehaviour[kFT2ReloadSampleSettings] && instr != 0); // ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it // Test case: PTSwapEmpty.mod - bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (m_playBehaviour[kMODSampleSwap] && !pChn->IsSamplePlaying() && pChn->pModSample != nullptr && pChn->pModSample->pSample == nullptr); + bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying() && chn.pModSample != nullptr && !chn.pModSample->HasSampleData()); // Now it's time for some FT2 crap... if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) @@ -2563,17 +2650,17 @@ bool CSoundFile::ProcessEffects() // Test case: NoteOffVolume.xm if(note == NOTE_KEYOFF && ((!instr && volcmd != VOLCMD_VOLUME && cmd != CMD_VOLUME) || !m_playBehaviour[kFT2KeyOff]) - && (pChn->pModInstrument == nullptr || !pChn->pModInstrument->VolEnv.dwFlags[ENV_ENABLED])) + && (chn.pModInstrument == nullptr || !chn.pModInstrument->VolEnv.dwFlags[ENV_ENABLED])) { - pChn->dwFlags.set(CHN_FASTVOLRAMP); - pChn->nVolume = 0; + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.nVolume = 0; note = NOTE_NONE; instr = 0; retrigEnv = false; // FT2 Compatbility: Start fading the note for notes with no delay. Only relevant when a volume command is encountered after the note-off. // Test case: NoteOffFadeNoEnv.xm if(m_SongFlags[SONG_FIRSTTICK] && m_playBehaviour[kFT2NoteOffFlags]) - pChn->dwFlags.set(CHN_NOTEFADE); + chn.dwFlags.set(CHN_NOTEFADE); } else if(m_playBehaviour[kFT2RetrigWithNoteDelay] && !m_SongFlags[SONG_FIRSTTICK]) { // FT2 Compatibility: Some special hacks for rogue note delays... (EDx with x > 0) @@ -2589,7 +2676,7 @@ bool CSoundFile::ProcessEffects() { // If there's a note delay but no real note, retrig the last note. // Test case: delay2.xm, delay3.xm - note = static_cast(pChn->nNote - pChn->nTranspose); + note = static_cast(chn.nNote - chn.nTranspose); } else if(note >= NOTE_MIN_SPECIAL) { // Gah! Even Note Off + Note Delay will cause envelopes to *retrigger*! How stupid is that? @@ -2598,9 +2685,9 @@ bool CSoundFile::ProcessEffects() note = NOTE_NONE; keepInstr = false; reloadSampleSettings = true; - } else + } else if(instr || !m_playBehaviour[kFT2NoteDelayWithoutInstr]) { - // Normal note + // Normal note (only if there is an instrument, test case: DelayVolume.xm) keepInstr = true; reloadSampleSettings = true; } @@ -2614,7 +2701,7 @@ bool CSoundFile::ProcessEffects() if(GetNumInstruments()) { - oldSample = pChn->pModSample; + oldSample = chn.pModSample; } else if (instr <= GetNumSamples()) { // Case: Only samples are used; no instruments. @@ -2624,11 +2711,11 @@ bool CSoundFile::ProcessEffects() if(oldSample != nullptr) { if(!oldSample->uFlags[SMP_NODEFAULTVOLUME]) - pChn->nVolume = oldSample->nVolume; + chn.nVolume = oldSample->nVolume; if(reloadSampleSettings) { // Also reload panning - pChn->nPan = oldSample->nPan; + chn.nPan = oldSample->nPan; } } } @@ -2637,7 +2724,7 @@ bool CSoundFile::ProcessEffects() // Test case: TremorInstr.xm, TremoRecover.xm if(m_playBehaviour[kFT2Tremor] && instr != 0) { - pChn->nTremorCount = 0x20; + chn.nTremorCount = 0x20; } if(retrigEnv) //Case: instrument with no note data. @@ -2647,32 +2734,32 @@ bool CSoundFile::ProcessEffects() { // IT compatibility: Completely retrigger note after sample end to also reset portamento. // Test case: PortaResetAfterRetrigger.it - bool triggerAfterSmpEnd = m_playBehaviour[kITMultiSampleInstrumentNumber] && !pChn->IsSamplePlaying(); + bool triggerAfterSmpEnd = m_playBehaviour[kITMultiSampleInstrumentNumber] && !chn.IsSamplePlaying(); if(GetNumInstruments()) { // Instrument mode - if(instr <= GetNumInstruments() && (pChn->pModInstrument != Instruments[instr] || triggerAfterSmpEnd)) - note = pChn->nNote; + if(instr <= GetNumInstruments() && (chn.pModInstrument != Instruments[instr] || triggerAfterSmpEnd)) + note = chn.nNote; } else { // Sample mode - if(instr < MAX_SAMPLES && (pChn->pModSample != &Samples[instr] || triggerAfterSmpEnd)) - note = pChn->nNote; + if(instr < MAX_SAMPLES && (chn.pModSample != &Samples[instr] || triggerAfterSmpEnd)) + note = chn.nNote; } } if (GetNumInstruments() && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) { - pChn->ResetEnvelopes(); - pChn->dwFlags.set(CHN_FASTVOLRAMP); - pChn->dwFlags.reset(CHN_NOTEFADE); - pChn->nAutoVibDepth = 0; - pChn->nAutoVibPos = 0; - pChn->nFadeOutVol = 65536; + chn.ResetEnvelopes(); + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.dwFlags.reset(CHN_NOTEFADE); + chn.nAutoVibDepth = 0; + chn.nAutoVibPos = 0; + chn.nFadeOutVol = 65536; // FT2 Compatbility: Reset key-off status with instrument number // Test case: NoteOffInstrChange.xm if(m_playBehaviour[kFT2NoteOffFlags]) - pChn->dwFlags.reset(CHN_KEYOFF); + chn.dwFlags.reset(CHN_KEYOFF); } if (!keepInstr) instr = 0; } @@ -2688,20 +2775,20 @@ bool CSoundFile::ProcessEffects() if(GetNumInstruments()) { smp = 0; - if(instr <= GetNumInstruments() && Instruments[instr] != nullptr && ModCommand::IsNote(pChn->nLastNote)) + if(instr <= GetNumInstruments() && Instruments[instr] != nullptr && ModCommand::IsNote(chn.nLastNote)) { - smp = Instruments[instr]->Keyboard[pChn->nLastNote - NOTE_MIN]; + smp = Instruments[instr]->Keyboard[chn.nLastNote - NOTE_MIN]; } } if(smp > 0 && smp <= GetNumSamples() && !Samples[smp].uFlags[SMP_NODEFAULTVOLUME]) - pChn->nVolume = Samples[smp].nVolume; + chn.nVolume = Samples[smp].nVolume; } instr = 0; } if(ModCommand::IsNote(note)) { - pChn->nNewNote = pChn->nLastNote = note; + chn.nNewNote = chn.nLastNote = note; // New Note Action ? if(!bPorta) @@ -2712,87 +2799,104 @@ bool CSoundFile::ProcessEffects() if(note) { - if(pChn->nRestorePanOnNewNote > 0) + if(chn.nRestorePanOnNewNote > 0) { - pChn->nPan = pChn->nRestorePanOnNewNote - 1; - pChn->nRestorePanOnNewNote = 0; + chn.nPan = chn.nRestorePanOnNewNote - 1; + chn.nRestorePanOnNewNote = 0; } - if(pChn->nRestoreResonanceOnNewNote > 0) + if(chn.nRestoreResonanceOnNewNote > 0) { - pChn->nResonance = pChn->nRestoreResonanceOnNewNote - 1; - pChn->nRestoreResonanceOnNewNote = 0; + chn.nResonance = chn.nRestoreResonanceOnNewNote - 1; + chn.nRestoreResonanceOnNewNote = 0; } - if(pChn->nRestoreCutoffOnNewNote > 0) + if(chn.nRestoreCutoffOnNewNote > 0) { - pChn->nCutOff = pChn->nRestoreCutoffOnNewNote - 1; - pChn->nRestoreCutoffOnNewNote = 0; + chn.nCutOff = chn.nRestoreCutoffOnNewNote - 1; + chn.nRestoreCutoffOnNewNote = 0; } } // Instrument Change ? if(instr) { - const ModSample *oldSample = pChn->pModSample; - //const ModInstrument *oldInstrument = pChn->pModInstrument; + const ModSample *oldSample = chn.pModSample; + //const ModInstrument *oldInstrument = chn.pModInstrument; + + InstrumentChange(chn, instr, bPorta, true); + + if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl) + { + m_opl->Patch(nChn, chn.pModSample->adlib); + } - InstrumentChange(pChn, instr, bPorta, true); // IT compatibility: Keep new instrument number for next instrument-less note even if sample playback is stopped // Test case: StoppedInstrSwap.it if(GetType() == MOD_TYPE_MOD) { // Test case: PortaSwapPT.mod - if(!bPorta || !m_playBehaviour[kMODSampleSwap]) pChn->nNewIns = 0; + if(!bPorta || !m_playBehaviour[kMODSampleSwap]) chn.nNewIns = 0; } else { - if(!m_playBehaviour[kITInstrWithNoteOff] || ModCommand::IsNote(note)) pChn->nNewIns = 0; + if(!m_playBehaviour[kITInstrWithNoteOff] || ModCommand::IsNote(note)) chn.nNewIns = 0; } if(m_playBehaviour[kITPortamentoSwapResetsPos]) { // Test cases: PortaInsNum.it, PortaSample.it - if(ModCommand::IsNote(note) && oldSample != pChn->pModSample) + if(ModCommand::IsNote(note) && oldSample != chn.pModSample) { - //const bool newInstrument = oldInstrument != pChn->pModInstrument && pChn->pModInstrument->Keyboard[pChn->nNewNote - NOTE_MIN] != 0; - pChn->position.Set(0); + //const bool newInstrument = oldInstrument != chn.pModInstrument && chn.pModInstrument->Keyboard[chn.nNewNote - NOTE_MIN] != 0; + chn.position.Set(0); } - } else if ((GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT) && oldSample != pChn->pModSample && ModCommand::IsNote(note))) + } else if ((GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT) && oldSample != chn.pModSample && ModCommand::IsNote(note))) { // Special IT case: portamento+note causes sample change -> ignore portamento bPorta = false; - } else if(m_playBehaviour[kMODSampleSwap] && pChn->increment.IsZero()) + } else if(m_playBehaviour[kMODSampleSwap] && chn.increment.IsZero()) { // If channel was paused and is resurrected by a lone instrument number, reset the sample position. // Test case: PTSwapEmpty.mod - pChn->position.Set(0); + chn.position.Set(0); } } // New Note ? - if (note) + if (note != NOTE_NONE) { - if ((!instr) && (pChn->nNewIns) && (note < 0x80)) + const bool instrChange = (!instr) && (chn.nNewIns) && ModCommand::IsNote(note); + if(instrChange) { - InstrumentChange(pChn, pChn->nNewIns, bPorta, false, !(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))); - pChn->nNewIns = 0; + InstrumentChange(chn, chn.nNewIns, bPorta, chn.pModSample == nullptr && chn.pModInstrument == nullptr, !(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))); + chn.nNewIns = 0; } - NoteChange(pChn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))); + if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl && (instrChange || !m_opl->IsActive(nChn))) + { + m_opl->Patch(nChn, chn.pModSample->adlib); + } + + NoteChange(chn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)), false, nChn); if ((bPorta) && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) { - pChn->dwFlags.set(CHN_FASTVOLRAMP); - pChn->ResetEnvelopes(); - pChn->nAutoVibDepth = 0; - pChn->nAutoVibPos = 0; + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.ResetEnvelopes(); + chn.nAutoVibDepth = 0; + chn.nAutoVibPos = 0; + } + if(chn.dwFlags[CHN_ADLIB] && m_opl + && ((note == NOTE_NOTECUT || note == NOTE_KEYOFF) || (note == NOTE_FADE && !m_playBehaviour[kOPLFlexibleNoteOff]))) + { + m_opl->NoteOff(nChn); } } // Tick-0 only volume commands if (volcmd == VOLCMD_VOLUME) { if (vol > 64) vol = 64; - pChn->nVolume = vol << 2; - pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.nVolume = vol << 2; + chn.dwFlags.set(CHN_FASTVOLRAMP); } else if (volcmd == VOLCMD_PANNING) { - Panning(pChn, vol, Pan6bit); + Panning(chn, vol, Pan6bit); } #ifndef NO_PLUGINS @@ -2820,14 +2924,14 @@ bool CSoundFile::ProcessEffects() // Test case: VolColDelay.xm, PortaDelay.xm if(m_playBehaviour[kFT2VolColDelay] && nStartTick != 0) { - doVolumeColumn = m_PlayState.m_nTickCount != 0 && (m_PlayState.m_nTickCount != nStartTick || (pChn->rowCommand.instr == 0 && volcmd != VOLCMD_TONEPORTAMENTO)); + doVolumeColumn = m_PlayState.m_nTickCount != 0 && (m_PlayState.m_nTickCount != nStartTick || (chn.rowCommand.instr == 0 && volcmd != VOLCMD_TONEPORTAMENTO)); } if(volcmd > VOLCMD_PANNING && doVolumeColumn) { if (volcmd == VOLCMD_TONEPORTAMENTO) { uint32 porta = 0; - if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_AMS2 | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) + if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) { porta = ImpulseTrackerPortaVolCmd[vol & 0x0F]; } else @@ -2850,7 +2954,7 @@ bool CSoundFile::ProcessEffects() porta = 0; } } - TonePortamento(pChn, porta); + TonePortamento(chn, porta); } else { // FT2 Compatibility: FT2 ignores some volume commands with parameter = 0. @@ -2867,7 +2971,7 @@ bool CSoundFile::ProcessEffects() // Test case: PanSlideZero.xm if(!m_SongFlags[SONG_FIRSTTICK]) { - pChn->nPan = 0; + chn.nPan = 0; } MPT_FALLTHROUGH; default: @@ -2879,7 +2983,7 @@ bool CSoundFile::ProcessEffects() { // IT Compatibility: Effects in the volume column don't have an unified memory. // Test case: VolColMemory.it - if(vol) pChn->nOldVolParam = static_cast(vol); else vol = pChn->nOldVolParam; + if(vol) chn.nOldVolParam = static_cast(vol); else vol = chn.nOldVolParam; } switch(volcmd) @@ -2890,14 +2994,14 @@ bool CSoundFile::ProcessEffects() // Test case: VolColMemory.it if(vol == 0 && m_playBehaviour[kITVolColMemory]) { - vol = pChn->nOldVolParam; + vol = chn.nOldVolParam; if(vol == 0) break; } else { - pChn->nOldVolParam = static_cast(vol); + chn.nOldVolParam = static_cast(vol); } - VolumeSlide(pChn, static_cast(volcmd == VOLCMD_VOLSLIDEUP ? (vol << 4) : vol)); + VolumeSlide(chn, static_cast(volcmd == VOLCMD_VOLSLIDEUP ? (vol << 4) : vol)); break; case VOLCMD_FINEVOLUP: @@ -2907,7 +3011,7 @@ bool CSoundFile::ProcessEffects() { // IT Compatibility: Volume column volume slides have their own memory // Test case: VolColMemory.it - FineVolumeUp(pChn, static_cast(vol), m_playBehaviour[kITVolColMemory]); + FineVolumeUp(chn, static_cast(vol), m_playBehaviour[kITVolColMemory]); } break; @@ -2918,28 +3022,28 @@ bool CSoundFile::ProcessEffects() { // IT Compatibility: Volume column volume slides have their own memory // Test case: VolColMemory.it - FineVolumeDown(pChn, static_cast(vol), m_playBehaviour[kITVolColMemory]); + FineVolumeDown(chn, static_cast(vol), m_playBehaviour[kITVolColMemory]); } break; case VOLCMD_VIBRATOSPEED: // FT2 does not automatically enable vibrato with the "set vibrato speed" command if(m_playBehaviour[kFT2VolColVibrato]) - pChn->nVibratoSpeed = vol & 0x0F; + chn.nVibratoSpeed = vol & 0x0F; else - Vibrato(pChn, vol << 4); + Vibrato(chn, vol << 4); break; case VOLCMD_VIBRATODEPTH: - Vibrato(pChn, vol); + Vibrato(chn, vol); break; case VOLCMD_PANSLIDELEFT: - PanningSlide(pChn, static_cast(vol), !m_playBehaviour[kFT2VolColMemory]); + PanningSlide(chn, static_cast(vol), !m_playBehaviour[kFT2VolColMemory]); break; case VOLCMD_PANSLIDERIGHT: - PanningSlide(pChn, static_cast(vol << 4), !m_playBehaviour[kFT2VolColMemory]); + PanningSlide(chn, static_cast(vol << 4), !m_playBehaviour[kFT2VolColMemory]); break; case VOLCMD_PORTAUP: @@ -2953,14 +3057,14 @@ bool CSoundFile::ProcessEffects() break; case VOLCMD_OFFSET: - if (triggerNote && pChn->pModSample && vol <= CountOf(pChn->pModSample->cues)) + if (triggerNote && chn.pModSample && vol <= CountOf(chn.pModSample->cues)) { SmpLength offset; if(vol == 0) - offset = pChn->oldOffset; + offset = chn.oldOffset; else - offset = pChn->oldOffset = pChn->pModSample->cues[vol - 1]; - SampleOffset(*pChn, offset); + offset = chn.oldOffset = chn.pModSample->cues[vol - 1]; + SampleOffset(chn, offset); } break; } @@ -2974,8 +3078,8 @@ bool CSoundFile::ProcessEffects() case CMD_VOLUME: if(m_SongFlags[SONG_FIRSTTICK]) { - pChn->nVolume = (param < 64) ? param * 4 : 256; - pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.nVolume = (param < 64) ? param * 4 : 256; + chn.dwFlags.set(CHN_FASTVOLRAMP); } break; @@ -2993,29 +3097,29 @@ bool CSoundFile::ProcessEffects() // Volume Slide case CMD_VOLUMESLIDE: - if (param || (GetType() != MOD_TYPE_MOD)) VolumeSlide(pChn, static_cast(param)); + if (param || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast(param)); break; // Tone-Portamento case CMD_TONEPORTAMENTO: - TonePortamento(pChn, param); + TonePortamento(chn, param); break; // Tone-Portamento + Volume Slide case CMD_TONEPORTAVOL: - if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(pChn, static_cast(param)); - TonePortamento(pChn, 0); + if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast(param)); + TonePortamento(chn, 0); break; // Vibrato case CMD_VIBRATO: - Vibrato(pChn, param); + Vibrato(chn, param); break; // Vibrato + Volume Slide case CMD_VIBRATOVOL: - if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(pChn, static_cast(param)); - Vibrato(pChn, 0); + if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast(param)); + Vibrato(chn, 0); break; // Set Speed @@ -3036,7 +3140,7 @@ bool CSoundFile::ProcessEffects() param = CalculateXParam(m_PlayState.m_nPattern, m_PlayState.m_nRow, nChn); if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)) { - if (param) pChn->nOldTempo = static_cast(param); else param = pChn->nOldTempo; + if (param) chn.nOldTempo = static_cast(param); else param = chn.nOldTempo; } TEMPO t(param, 0); LimitMax(t, GetModSpecifications().GetTempoMax()); @@ -3060,10 +3164,10 @@ bool CSoundFile::ProcessEffects() { // No X-param (normal behaviour) offset <<= 8; - if (offset) pChn->oldOffset = offset; else offset = pChn->oldOffset; - offset += static_cast(pChn->nOldHiOffset) << 16; + if (offset) chn.oldOffset = offset; else offset = chn.oldOffset; + offset += static_cast(chn.nOldHiOffset) << 16; } - SampleOffset(*pChn, offset); + SampleOffset(chn, offset); } break; @@ -3071,7 +3175,7 @@ bool CSoundFile::ProcessEffects() case CMD_OFFSETPERCENTAGE: if(triggerNote) { - SampleOffset(*pChn, Util::muldiv_unsigned(pChn->nLength, param, 255)); + SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, param, 255)); } break; @@ -3079,31 +3183,31 @@ bool CSoundFile::ProcessEffects() case CMD_ARPEGGIO: // IT compatibility 01. Don't ignore Arpeggio if no note is playing (also valid for ST3) if(m_PlayState.m_nTickCount) break; - if((!pChn->nPeriod || !pChn->nNote) - && (pChn->pModInstrument == nullptr || !pChn->pModInstrument->HasValidMIDIChannel()) // Plugin arpeggio + if((!chn.nPeriod || !chn.nNote) + && (chn.pModInstrument == nullptr || !chn.pModInstrument->HasValidMIDIChannel()) // Plugin arpeggio && !m_playBehaviour[kITArpeggio] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) break; if (!param && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD))) break; // Only important when editing MOD/XM files (000 effects are removed when loading files where this means "no effect") - pChn->nCommand = CMD_ARPEGGIO; - if (param) pChn->nArpeggio = static_cast(param); + chn.nCommand = CMD_ARPEGGIO; + if (param) chn.nArpeggio = static_cast(param); break; // Retrig case CMD_RETRIG: if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) { - if (!(param & 0xF0)) param |= pChn->nRetrigParam & 0xF0; - if (!(param & 0x0F)) param |= pChn->nRetrigParam & 0x0F; + if (!(param & 0xF0)) param |= chn.nRetrigParam & 0xF0; + if (!(param & 0x0F)) param |= chn.nRetrigParam & 0x0F; param |= 0x100; // increment retrig count on first row } // IT compatibility 15. Retrigger if(m_playBehaviour[kITRetrigger]) { - if (param) pChn->nRetrigParam = static_cast(param & 0xFF); - RetrigNote(nChn, pChn->nRetrigParam, (volcmd == VOLCMD_OFFSET) ? vol + 1 : 0); + if (param) chn.nRetrigParam = static_cast(param & 0xFF); + RetrigNote(nChn, chn.nRetrigParam, (volcmd == VOLCMD_OFFSET) ? vol + 1 : 0); } else { // XM Retrig - if (param) pChn->nRetrigParam = static_cast(param & 0xFF); else param = pChn->nRetrigParam; + if (param) chn.nRetrigParam = static_cast(param & 0xFF); else param = chn.nRetrigParam; RetrigNote(nChn, param, (volcmd == VOLCMD_OFFSET) ? vol + 1 : 0); } break; @@ -3124,15 +3228,15 @@ bool CSoundFile::ProcessEffects() if(param & 0xF0) param -= 0x10; if(param & 0x0F) param -= 0x01; } - pChn->nTremorCount |= 0x80; // set on/off flag + chn.nTremorCount |= 0x80; // set on/off flag } else if(m_playBehaviour[kFT2Tremor]) { // XM Tremor. Logic is being processed in sndmix.cpp - pChn->nTremorCount |= 0x80; // set on/off flag + chn.nTremorCount |= 0x80; // set on/off flag } - pChn->nCommand = CMD_TREMOR; - if (param) pChn->nTremorParam = static_cast(param); + chn.nCommand = CMD_TREMOR; + if (param) chn.nTremorParam = static_cast(param); break; @@ -3175,7 +3279,7 @@ bool CSoundFile::ProcessEffects() case CMD_GLOBALVOLSLIDE: //IT compatibility 16. Saving last global volume slide param per channel (FT2/IT) if(m_playBehaviour[kPerChannelGlobalVolSlide]) - GlobalVolSlide(static_cast(param), pChn->nOldGlobalVolSlide); + GlobalVolSlide(static_cast(param), chn.nOldGlobalVolSlide); else GlobalVolSlide(static_cast(param), m_PlayState.Chn[0].nOldGlobalVolSlide); break; @@ -3184,23 +3288,23 @@ bool CSoundFile::ProcessEffects() case CMD_PANNING8: if(m_SongFlags[SONG_FIRSTTICK]) { - Panning(pChn, param, Pan8bit); + Panning(chn, param, Pan8bit); } break; // Panning Slide case CMD_PANNINGSLIDE: - PanningSlide(pChn, static_cast(param)); + PanningSlide(chn, static_cast(param)); break; // Tremolo case CMD_TREMOLO: - Tremolo(pChn, param); + Tremolo(chn, param); break; // Fine Vibrato case CMD_FINEVIBRATO: - FineVibrato(pChn, param); + FineVibrato(chn, param); break; // MOD/XM Exx Extended Commands @@ -3212,7 +3316,7 @@ bool CSoundFile::ProcessEffects() case CMD_S3MCMDEX: if(m_playBehaviour[kST3EffectMemory] && param == 0) { - param = pChn->nArpeggio; // S00 uses the last non-zero effect parameter as memory, like other effects including Arpeggio, so we "borrow" our memory there. + param = chn.nArpeggio; // S00 uses the last non-zero effect parameter as memory, like other effects including Arpeggio, so we "borrow" our memory there. } ExtendedS3MCommands(nChn, static_cast(param)); break; @@ -3225,26 +3329,26 @@ bool CSoundFile::ProcessEffects() if (m_PlayState.m_nTickCount == param) { // XM: Key-Off + Sample == Note Cut - if(pChn->pModInstrument == nullptr || !pChn->pModInstrument->VolEnv.dwFlags[ENV_ENABLED]) + if(chn.pModInstrument == nullptr || !chn.pModInstrument->VolEnv.dwFlags[ENV_ENABLED]) { - if(param == 0 && (pChn->rowCommand.instr || pChn->rowCommand.volcmd != VOLCMD_NONE)) // FT2 is weird.... + if(param == 0 && (chn.rowCommand.instr || chn.rowCommand.volcmd != VOLCMD_NONE)) // FT2 is weird.... { - pChn->dwFlags.set(CHN_NOTEFADE); + chn.dwFlags.set(CHN_NOTEFADE); } else { - pChn->dwFlags.set(CHN_FASTVOLRAMP); - pChn->nVolume = 0; + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.nVolume = 0; } } - KeyOff(pChn); + KeyOff(chn); } } // This is how it's NOT supposed to sound... else { if(m_SongFlags[SONG_FIRSTTICK]) - KeyOff(pChn); + KeyOff(chn); } break; @@ -3252,8 +3356,8 @@ bool CSoundFile::ProcessEffects() case CMD_XFINEPORTAUPDOWN: switch(param & 0xF0) { - case 0x10: ExtraFinePortamentoUp(pChn, param & 0x0F); break; - case 0x20: ExtraFinePortamentoDown(pChn, param & 0x0F); break; + case 0x10: ExtraFinePortamentoUp(chn, param & 0x0F); break; + case 0x20: ExtraFinePortamentoDown(chn, param & 0x0F); break; // ModPlug XM Extensions (ignore in compatible mode) case 0x50: case 0x60: @@ -3270,33 +3374,33 @@ bool CSoundFile::ProcessEffects() if(!m_SongFlags[SONG_FIRSTTICK]) break; if (param <= 64) { - pChn->nGlobalVol = param; - pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.nGlobalVol = param; + chn.dwFlags.set(CHN_FASTVOLRAMP); } break; // Channel volume slide case CMD_CHANNELVOLSLIDE: - ChannelVolSlide(pChn, static_cast(param)); + ChannelVolSlide(chn, static_cast(param)); break; // Panbrello (IT) case CMD_PANBRELLO: - Panbrello(pChn, param); + Panbrello(chn, param); break; // Set Envelope Position case CMD_SETENVPOSITION: if(m_SongFlags[SONG_FIRSTTICK]) { - pChn->VolEnv.nEnvPosition = param; + chn.VolEnv.nEnvPosition = param; // FT2 compatibility: FT2 only sets the position of the panning envelope if the volume envelope's sustain flag is set // Test case: SetEnvPos.xm - if(!m_playBehaviour[kFT2SetPanEnvPos] || pChn->VolEnv.flags[ENV_SUSTAIN]) + if(!m_playBehaviour[kFT2SetPanEnvPos] || chn.VolEnv.flags[ENV_SUSTAIN]) { - pChn->PanEnv.nEnvPosition = param; - pChn->PitchEnv.nEnvPosition = param; + chn.PanEnv.nEnvPosition = param; + chn.PitchEnv.nEnvPosition = param; } } @@ -3340,12 +3444,12 @@ bool CSoundFile::ProcessEffects() case CMD_NOTESLIDEDOWNRETRIG: // Note that this command seems to be a bit buggy in Polytracker... Luckily, no tune seems to seriously use this // (Vic uses it e.g. in Spaceman or Perfect Reason to slide effect samples, noone will notice the difference :) - NoteSlide(pChn, param, cmd == CMD_NOTESLIDEUP || cmd == CMD_NOTESLIDEUPRETRIG, cmd == CMD_NOTESLIDEUPRETRIG || cmd == CMD_NOTESLIDEDOWNRETRIG); + NoteSlide(chn, param, cmd == CMD_NOTESLIDEUP || cmd == CMD_NOTESLIDEUPRETRIG, cmd == CMD_NOTESLIDEUPRETRIG || cmd == CMD_NOTESLIDEDOWNRETRIG); break; // PTM Reverse sample + offset (executed on every tick) case CMD_REVERSEOFFSET: - ReverseSampleOffset(*pChn, static_cast(param)); + ReverseSampleOffset(chn, static_cast(param)); break; #ifndef NO_PLUGINS @@ -3376,13 +3480,13 @@ bool CSoundFile::ProcessEffects() if(m_playBehaviour[kST3EffectMemory] && param != 0) { - UpdateS3MEffectMemory(pChn, static_cast(param)); + UpdateS3MEffectMemory(chn, static_cast(param)); } - if(pChn->rowCommand.instr) + if(chn.rowCommand.instr) { // Not necessarily consistent with actually playing instrument for IT compatibility - pChn->nOldIns = pChn->rowCommand.instr; + chn.nOldIns = chn.rowCommand.instr; } } // for(...) end @@ -3457,16 +3561,16 @@ bool CSoundFile::ProcessEffects() // Update the effect memory of all S3M effects that use the last non-zero effect parameter as memory (Dxy, Exx, Fxx, Ixy, Jxy, Kxy, Lxy, Qxy, Rxy, Sxy) // Test case: ParamMemory.s3m -void CSoundFile::UpdateS3MEffectMemory(ModChannel *pChn, ModCommand::PARAM param) const +void CSoundFile::UpdateS3MEffectMemory(ModChannel &chn, ModCommand::PARAM param) const { - pChn->nOldVolumeSlide = param; // Dxy / Kxy / Lxy - pChn->nOldPortaUp = param; // Exx / Fxx - pChn->nOldPortaDown = param; // Exx / Fxx - pChn->nTremorParam = param; // Ixy - pChn->nArpeggio = param; // Jxy - pChn->nRetrigParam = param; // Qxy - pChn->nTremoloDepth = (param & 0x0F) << 2; // Rxy - pChn->nTremoloSpeed = (param >> 4) & 0x0F; // Rxy + chn.nOldVolumeSlide = param; // Dxy / Kxy / Lxy + chn.nOldPortaUp = param; // Exx / Fxx + chn.nOldPortaDown = param; // Exx / Fxx + chn.nTremorParam = param; // Ixy + chn.nArpeggio = param; // Jxy + chn.nRetrigParam = param; // Qxy + chn.nTremoloDepth = (param & 0x0F) << 2; // Rxy + chn.nTremoloSpeed = (param >> 4) & 0x0F; // Rxy // Sxy is not handled here. } @@ -3535,18 +3639,18 @@ ROWINDEX CSoundFile::PatternBreak(PlayState &state, CHANNELINDEX chn, uint8 para void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular) { - ModChannel *pChn = &m_PlayState.Chn[nChn]; + ModChannel &chn = m_PlayState.Chn[nChn]; if(param) { // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) - pChn->nOldPortaDown = param; - pChn->nOldPortaUp = param; + chn.nOldPortaDown = param; + chn.nOldPortaUp = param; } else { - param = pChn->nOldPortaUp; + param = chn.nOldPortaUp; } const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM)); @@ -3554,20 +3658,20 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const // Process MIDI pitch bend for instrument plugins MidiPortamento(nChn, param, doFineSlides); - if(GetType() == MOD_TYPE_MPT && pChn->pModInstrument && pChn->pModInstrument->pTuning) + if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) { // Portamento for instruments with custom tuning if(param >= 0xF0 && !doFinePortamentoAsRegular) - PortamentoFineMPT(pChn, param - 0xF0); + PortamentoFineMPT(chn, param - 0xF0); else if(param >= 0xE0 && !doFinePortamentoAsRegular) - PortamentoExtraFineMPT(pChn, param - 0xE0); + PortamentoExtraFineMPT(chn, param - 0xE0); else - PortamentoMPT(pChn, param); + PortamentoMPT(chn, param); return; } else if(GetType() == MOD_TYPE_PLM) { // A normal portamento up or down makes a follow-up tone portamento go the same direction. - pChn->nPortamentoDest = 1; + chn.nPortamentoDest = 1; } if (doFineSlides && param >= 0xE0) @@ -3576,11 +3680,11 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const { if ((param & 0xF0) == 0xF0) { - FinePortamentoUp(pChn, param & 0x0F); + FinePortamentoUp(chn, param & 0x0F); return; } else if ((param & 0xF0) == 0xE0 && GetType() != MOD_TYPE_DBM) { - ExtraFinePortamentoUp(pChn, param & 0x0F); + ExtraFinePortamentoUp(chn, param & 0x0F); return; } } @@ -3591,27 +3695,27 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const } } // Regular Slide - if(!pChn->isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || GetType() == MOD_TYPE_669) + if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || GetType() == MOD_TYPE_669) { - DoFreqSlide(pChn, -int(param) * 4); + DoFreqSlide(chn, -int(param) * 4); } } void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular) { - ModChannel *pChn = &m_PlayState.Chn[nChn]; + ModChannel &chn = m_PlayState.Chn[nChn]; if(param) { // FT2 compatibility: Separate effect memory for all portamento commands // Test case: Porta-LinkMem.xm if(!m_playBehaviour[kFT2PortaUpDownMemory]) - pChn->nOldPortaUp = param; - pChn->nOldPortaDown = param; + chn.nOldPortaUp = param; + chn.nOldPortaDown = param; } else { - param = pChn->nOldPortaDown; + param = chn.nOldPortaDown; } const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM)); @@ -3619,20 +3723,20 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons // Process MIDI pitch bend for instrument plugins MidiPortamento(nChn, -static_cast(param), doFineSlides); - if(GetType() == MOD_TYPE_MPT && pChn->pModInstrument && pChn->pModInstrument->pTuning) + if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) { // Portamento for instruments with custom tuning if(param >= 0xF0 && !doFinePortamentoAsRegular) - PortamentoFineMPT(pChn, -static_cast(param - 0xF0)); + PortamentoFineMPT(chn, -static_cast(param - 0xF0)); else if(param >= 0xE0 && !doFinePortamentoAsRegular) - PortamentoExtraFineMPT(pChn, -static_cast(param - 0xE0)); + PortamentoExtraFineMPT(chn, -static_cast(param - 0xE0)); else - PortamentoMPT(pChn, -static_cast(param)); + PortamentoMPT(chn, -static_cast(param)); return; } else if(GetType() == MOD_TYPE_PLM) { // A normal portamento up or down makes a follow-up tone portamento go the same direction. - pChn->nPortamentoDest = 65535; + chn.nPortamentoDest = 65535; } if(doFineSlides && param >= 0xE0) @@ -3641,11 +3745,11 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons { if ((param & 0xF0) == 0xF0) { - FinePortamentoDown(pChn, param & 0x0F); + FinePortamentoDown(chn, param & 0x0F); return; } else if ((param & 0xF0) == 0xE0 && GetType() != MOD_TYPE_DBM) { - ExtraFinePortamentoDown(pChn, param & 0x0F); + ExtraFinePortamentoDown(chn, param & 0x0F); return; } } @@ -3656,9 +3760,9 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons } } - if(!pChn->isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || GetType() == MOD_TYPE_669) + if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || GetType() == MOD_TYPE_669) { - DoFreqSlide(pChn, int(param) * 4); + DoFreqSlide(chn, int(param) * 4); } } @@ -3704,50 +3808,50 @@ void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides) { pwd = m_PlayState.Chn[nChn].pModInstrument->midiPWD; } - plugin->MidiPitchBend(GetBestMidiChannel(nChn), pitchBend, pwd); + plugin->MidiPitchBend(pitchBend, pwd, nChn); } #endif // NO_PLUGINS } } -void CSoundFile::FinePortamentoUp(ModChannel *pChn, ModCommand::PARAM param) const +void CSoundFile::FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm - if(param) pChn->nOldFinePortaUpDown = (pChn->nOldFinePortaUpDown & 0x0F) | (param << 4); else param = (pChn->nOldFinePortaUpDown >> 4); + if(param) chn.nOldFinePortaUpDown = (chn.nOldFinePortaUpDown & 0x0F) | (param << 4); else param = (chn.nOldFinePortaUpDown >> 4); } else if(GetType() == MOD_TYPE_MT2) { - if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } - if(pChn->isFirstTick) + if(chn.isFirstTick) { - if ((pChn->nPeriod) && (param)) + if ((chn.nPeriod) && (param)) { if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { - const auto oldPeriod = pChn->nPeriod; - pChn->nPeriod = Util::muldivr(pChn->nPeriod, GetLinearSlideUpTable(this, param & 0x0F), 65536); - if(oldPeriod == pChn->nPeriod) + const auto oldPeriod = chn.nPeriod; + chn.nPeriod = Util::muldivr(chn.nPeriod, GetLinearSlideUpTable(this, param & 0x0F), 65536); + if(oldPeriod == chn.nPeriod) { - if(m_playBehaviour[kHertzInLinearMode] && pChn->nPeriod < Util::MaxValueOfType(pChn->nPeriod)) - pChn->nPeriod++; - else if(!m_playBehaviour[kHertzInLinearMode] && pChn->nPeriod > 1) - pChn->nPeriod--; + if(m_playBehaviour[kHertzInLinearMode] && chn.nPeriod < Util::MaxValueOfType(chn.nPeriod)) + chn.nPeriod++; + else if(!m_playBehaviour[kHertzInLinearMode] && chn.nPeriod > 1) + chn.nPeriod--; } } else { - pChn->nPeriod -= (int)(param * 4); - if (pChn->nPeriod < 1) + chn.nPeriod -= (int)(param * 4); + if (chn.nPeriod < 1) { - pChn->nPeriod = 1; + chn.nPeriod = 1; if(GetType() == MOD_TYPE_S3M) { - pChn->nFadeOutVol = 0; - pChn->dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); + chn.nFadeOutVol = 0; + chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); } } } @@ -3756,74 +3860,74 @@ void CSoundFile::FinePortamentoUp(ModChannel *pChn, ModCommand::PARAM param) con } -void CSoundFile::FinePortamentoDown(ModChannel *pChn, ModCommand::PARAM param) const +void CSoundFile::FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm - if(param) pChn->nOldFinePortaUpDown = (pChn->nOldFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (pChn->nOldFinePortaUpDown & 0x0F); + if(param) chn.nOldFinePortaUpDown = (chn.nOldFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (chn.nOldFinePortaUpDown & 0x0F); } else if(GetType() == MOD_TYPE_MT2) { - if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } - if(pChn->isFirstTick) + if(chn.isFirstTick) { - if ((pChn->nPeriod) && (param)) + if ((chn.nPeriod) && (param)) { if (m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { - const auto oldPeriod = pChn->nPeriod; - pChn->nPeriod = Util::muldivr(pChn->nPeriod, GetLinearSlideDownTable(this, param & 0x0F), 65536); - if(oldPeriod == pChn->nPeriod) + const auto oldPeriod = chn.nPeriod; + chn.nPeriod = Util::muldivr(chn.nPeriod, GetLinearSlideDownTable(this, param & 0x0F), 65536); + if(oldPeriod == chn.nPeriod) { - if(!m_playBehaviour[kHertzInLinearMode] && pChn->nPeriod < Util::MaxValueOfType(pChn->nPeriod)) - pChn->nPeriod++; - else if(m_playBehaviour[kHertzInLinearMode] && pChn->nPeriod > 1) - pChn->nPeriod--; + if(!m_playBehaviour[kHertzInLinearMode] && chn.nPeriod < Util::MaxValueOfType(chn.nPeriod)) + chn.nPeriod++; + else if(m_playBehaviour[kHertzInLinearMode] && chn.nPeriod > 1) + chn.nPeriod--; } } else { - pChn->nPeriod += (int)(param * 4); - if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; + chn.nPeriod += (int)(param * 4); + if (chn.nPeriod > 0xFFFF) chn.nPeriod = 0xFFFF; } } } } -void CSoundFile::ExtraFinePortamentoUp(ModChannel *pChn, ModCommand::PARAM param) const +void CSoundFile::ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm - if(param) pChn->nOldExtraFinePortaUpDown = (pChn->nOldExtraFinePortaUpDown & 0x0F) | (param << 4); else param = (pChn->nOldExtraFinePortaUpDown >> 4); + if(param) chn.nOldExtraFinePortaUpDown = (chn.nOldExtraFinePortaUpDown & 0x0F) | (param << 4); else param = (chn.nOldExtraFinePortaUpDown >> 4); } else if(GetType() == MOD_TYPE_MT2) { - if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } - if(pChn->isFirstTick) + if(chn.isFirstTick) { - if ((pChn->nPeriod) && (param)) + if ((chn.nPeriod) && (param)) { if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { - int oldPeriod = pChn->nPeriod; - pChn->nPeriod = Util::muldivr(pChn->nPeriod, GetFineLinearSlideUpTable(this, param & 0x0F), 65536); - if(oldPeriod == pChn->nPeriod) pChn->nPeriod++; + int oldPeriod = chn.nPeriod; + chn.nPeriod = Util::muldivr(chn.nPeriod, GetFineLinearSlideUpTable(this, param & 0x0F), 65536); + if(oldPeriod == chn.nPeriod) chn.nPeriod++; } else { - pChn->nPeriod -= (int)(param); - if (pChn->nPeriod < 1) + chn.nPeriod -= (int)(param); + if (chn.nPeriod < 1) { - pChn->nPeriod = 1; + chn.nPeriod = 1; if(GetType() == MOD_TYPE_S3M) { - pChn->nFadeOutVol = 0; - pChn->dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); + chn.nFadeOutVol = 0; + chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); } } } @@ -3832,31 +3936,31 @@ void CSoundFile::ExtraFinePortamentoUp(ModChannel *pChn, ModCommand::PARAM param } -void CSoundFile::ExtraFinePortamentoDown(ModChannel *pChn, ModCommand::PARAM param) const +void CSoundFile::ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked // Test case: Porta-LinkMem.xm - if(param) pChn->nOldExtraFinePortaUpDown = (pChn->nOldExtraFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (pChn->nOldExtraFinePortaUpDown & 0x0F); + if(param) chn.nOldExtraFinePortaUpDown = (chn.nOldExtraFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (chn.nOldExtraFinePortaUpDown & 0x0F); } else if(GetType() == MOD_TYPE_MT2) { - if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown; } - if(pChn->isFirstTick) + if(chn.isFirstTick) { - if ((pChn->nPeriod) && (param)) + if ((chn.nPeriod) && (param)) { if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { - int oldPeriod = pChn->nPeriod; - pChn->nPeriod = Util::muldivr(pChn->nPeriod, GetFineLinearSlideDownTable(this, param & 0x0F), 65536); - if(oldPeriod == pChn->nPeriod) pChn->nPeriod--; + int oldPeriod = chn.nPeriod; + chn.nPeriod = Util::muldivr(chn.nPeriod, GetFineLinearSlideDownTable(this, param & 0x0F), 65536); + if(oldPeriod == chn.nPeriod) chn.nPeriod--; } else { - pChn->nPeriod += (int)(param); - if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; + chn.nPeriod += (int)(param); + if (chn.nPeriod > 0xFFFF) chn.nPeriod = 0xFFFF; } } } @@ -3864,97 +3968,97 @@ void CSoundFile::ExtraFinePortamentoDown(ModChannel *pChn, ModCommand::PARAM par // Implemented for IMF compatibility, can't actually save this in any formats // Slide up / down every x ticks by y semitones -void CSoundFile::NoteSlide(ModChannel *pChn, uint32 param, bool slideUp, bool retrig) const +void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const { uint8 x, y; if(m_SongFlags[SONG_FIRSTTICK]) { x = param & 0xF0; if (x) - pChn->nNoteSlideSpeed = (x >> 4); + chn.nNoteSlideSpeed = (x >> 4); y = param & 0x0F; if (y) - pChn->nNoteSlideStep = y; - pChn->nNoteSlideCounter = pChn->nNoteSlideSpeed; + chn.nNoteSlideStep = y; + chn.nNoteSlideCounter = chn.nNoteSlideSpeed; } else { - if (--pChn->nNoteSlideCounter == 0) + if (--chn.nNoteSlideCounter == 0) { - pChn->nNoteSlideCounter = pChn->nNoteSlideSpeed; + chn.nNoteSlideCounter = chn.nNoteSlideSpeed; // update it - pChn->nPeriod = GetPeriodFromNote - ((slideUp ? 1 : -1) * pChn->nNoteSlideStep + GetNoteFromPeriod(pChn->nPeriod), 8363, 0); + chn.nPeriod = GetPeriodFromNote + ((slideUp ? 1 : -1) * chn.nNoteSlideStep + GetNoteFromPeriod(chn.nPeriod), 8363, 0); if(retrig) { - pChn->position.Set(0); + chn.position.Set(0); } } } } // Portamento Slide -void CSoundFile::TonePortamento(ModChannel *pChn, uint32 param) const +void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const { - pChn->dwFlags.set(CHN_PORTAMENTO); + chn.dwFlags.set(CHN_PORTAMENTO); //IT compatibility 03: Share effect memory with portamento up/down if((!m_SongFlags[SONG_ITCOMPATGXX] && m_playBehaviour[kITPortaMemoryShare]) || GetType() == MOD_TYPE_PLM) { - if(param == 0) param = pChn->nOldPortaUp; - pChn->nOldPortaUp = pChn->nOldPortaDown = static_cast(param); + if(param == 0) param = chn.nOldPortaUp; + chn.nOldPortaUp = chn.nOldPortaDown = static_cast(param); } - if(GetType() == MOD_TYPE_MPT && pChn->pModInstrument && pChn->pModInstrument->pTuning) + if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) { //Behavior: Param tells number of finesteps(or 'fullsteps'(notes) with glissando) //to slide per row(not per tick). - const int32 old_PortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? pChn->m_PortamentoTickSlide : 0; + const int32 old_PortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0; if(param) - pChn->nPortamentoSlide = param; + chn.nPortamentoSlide = param; else - if(pChn->nPortamentoSlide == 0) + if(chn.nPortamentoSlide == 0) return; - if((pChn->nPortamentoDest > 0 && pChn->nPortamentoSlide < 0) || - (pChn->nPortamentoDest < 0 && pChn->nPortamentoSlide > 0)) - pChn->nPortamentoSlide = -pChn->nPortamentoSlide; + if((chn.nPortamentoDest > 0 && chn.nPortamentoSlide < 0) || + (chn.nPortamentoDest < 0 && chn.nPortamentoSlide > 0)) + chn.nPortamentoSlide = -chn.nPortamentoSlide; - pChn->m_PortamentoTickSlide = static_cast((m_PlayState.m_nTickCount + 1.0) * pChn->nPortamentoSlide / m_PlayState.m_nMusicSpeed); + chn.m_PortamentoTickSlide = static_cast((m_PlayState.m_nTickCount + 1.0) * chn.nPortamentoSlide / m_PlayState.m_nMusicSpeed); - if(pChn->dwFlags[CHN_GLISSANDO]) + if(chn.dwFlags[CHN_GLISSANDO]) { - pChn->m_PortamentoTickSlide *= pChn->pModInstrument->pTuning->GetFineStepCount() + 1; + chn.m_PortamentoTickSlide *= chn.pModInstrument->pTuning->GetFineStepCount() + 1; //With glissando interpreting param as notes instead of finesteps. } - const int32 slide = pChn->m_PortamentoTickSlide - old_PortamentoTickSlide; + const int32 slide = chn.m_PortamentoTickSlide - old_PortamentoTickSlide; - if(mpt::abs(pChn->nPortamentoDest) <= mpt::abs(slide)) + if(mpt::abs(chn.nPortamentoDest) <= mpt::abs(slide)) { - if(pChn->nPortamentoDest != 0) + if(chn.nPortamentoDest != 0) { - pChn->m_PortamentoFineSteps += pChn->nPortamentoDest; - pChn->nPortamentoDest = 0; - pChn->m_CalculateFreq = true; + chn.m_PortamentoFineSteps += chn.nPortamentoDest; + chn.nPortamentoDest = 0; + chn.m_CalculateFreq = true; } } else { - pChn->m_PortamentoFineSteps += slide; - pChn->nPortamentoDest -= slide; - pChn->m_CalculateFreq = true; + chn.m_PortamentoFineSteps += slide; + chn.nPortamentoDest -= slide; + chn.m_CalculateFreq = true; } return; } //End candidate MPT behavior. - bool doPorta = !pChn->isFirstTick || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669)) || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]); + bool doPorta = !chn.isFirstTick || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669)) || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]); if(GetType() == MOD_TYPE_PLM && param >= 0xF0) { param -= 0xF0; - doPorta = pChn->isFirstTick; + doPorta = chn.isFirstTick; } if(param) @@ -3963,80 +4067,80 @@ void CSoundFile::TonePortamento(ModChannel *pChn, uint32 param) const { param *= 10; } - pChn->nPortamentoSlide = param * 4; + chn.nPortamentoSlide = param * 4; } - if(pChn->nPeriod && pChn->nPortamentoDest && doPorta) + if(chn.nPeriod && chn.nPortamentoDest && doPorta) { - if (pChn->nPeriod < pChn->nPortamentoDest) + if (chn.nPeriod < chn.nPortamentoDest) { - int32 delta = pChn->nPortamentoSlide; + int32 delta = chn.nPortamentoSlide; if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { - uint32 n = pChn->nPortamentoSlide / 4; + uint32 n = chn.nPortamentoSlide / 4; if (n > 255) n = 255; // Return (a*b+c/2)/c - no divide error // Table is 65536*2(n/192) - delta = Util::muldivr(pChn->nPeriod, LinearSlideUpTable[n], 65536) - pChn->nPeriod; + delta = Util::muldivr(chn.nPeriod, LinearSlideUpTable[n], 65536) - chn.nPeriod; if (delta < 1) delta = 1; } - pChn->nPeriod += delta; - if (pChn->nPeriod > pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; + chn.nPeriod += delta; + if (chn.nPeriod > chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest; } else - if (pChn->nPeriod > pChn->nPortamentoDest) + if (chn.nPeriod > chn.nPortamentoDest) { - int32 delta = -pChn->nPortamentoSlide; + int32 delta = -chn.nPortamentoSlide; if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { - uint32 n = pChn->nPortamentoSlide / 4; + uint32 n = chn.nPortamentoSlide / 4; if (n > 255) n = 255; - delta = Util::muldivr(pChn->nPeriod, LinearSlideDownTable[n], 65536) - pChn->nPeriod; + delta = Util::muldivr(chn.nPeriod, LinearSlideDownTable[n], 65536) - chn.nPeriod; if (delta > -1) delta = -1; } - pChn->nPeriod += delta; - if (pChn->nPeriod < pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; + chn.nPeriod += delta; + if (chn.nPeriod < chn.nPortamentoDest) chn.nPeriod = chn.nPortamentoDest; } } // IT compatibility 23. Portamento with no note // ProTracker also disables portamento once the target is reached. // Test case: PortaTarget.mod - if(pChn->nPeriod == pChn->nPortamentoDest && (m_playBehaviour[kITPortaTargetReached] || GetType() == MOD_TYPE_MOD)) - pChn->nPortamentoDest = 0; + if(chn.nPeriod == chn.nPortamentoDest && (m_playBehaviour[kITPortaTargetReached] || GetType() == MOD_TYPE_MOD)) + chn.nPortamentoDest = 0; } -void CSoundFile::Vibrato(ModChannel *p, uint32 param) const +void CSoundFile::Vibrato(ModChannel &chn, uint32 param) const { - if (param & 0x0F) p->nVibratoDepth = (param & 0x0F) * 4; - if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; - p->dwFlags.set(CHN_VIBRATO); + if (param & 0x0F) chn.nVibratoDepth = (param & 0x0F) * 4; + if (param & 0xF0) chn.nVibratoSpeed = (param >> 4) & 0x0F; + chn.dwFlags.set(CHN_VIBRATO); } -void CSoundFile::FineVibrato(ModChannel *p, uint32 param) const +void CSoundFile::FineVibrato(ModChannel &chn, uint32 param) const { - if (param & 0x0F) p->nVibratoDepth = param & 0x0F; - if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; - p->dwFlags.set(CHN_VIBRATO); + if (param & 0x0F) chn.nVibratoDepth = param & 0x0F; + if (param & 0xF0) chn.nVibratoSpeed = (param >> 4) & 0x0F; + chn.dwFlags.set(CHN_VIBRATO); // ST3 compatibility: Do not distinguish between vibrato types in effect memory // Test case: VibratoTypeChange.s3m if(m_playBehaviour[kST3VibratoMemory] && (param & 0x0F)) { - p->nVibratoDepth *= 4u; + chn.nVibratoDepth *= 4u; } } -void CSoundFile::Panbrello(ModChannel *p, uint32 param) const +void CSoundFile::Panbrello(ModChannel &chn, uint32 param) const { - if (param & 0x0F) p->nPanbrelloDepth = param & 0x0F; - if (param & 0xF0) p->nPanbrelloSpeed = (param >> 4) & 0x0F; + if (param & 0x0F) chn.nPanbrelloDepth = param & 0x0F; + if (param & 0xF0) chn.nPanbrelloSpeed = (param >> 4) & 0x0F; } -void CSoundFile::Panning(ModChannel *pChn, uint32 param, PanningType panBits) const +void CSoundFile::Panning(ModChannel &chn, uint32 param, PanningType panBits) const { // No panning in ProTracker mode if(m_playBehaviour[kMODIgnorePanning]) @@ -4046,54 +4150,54 @@ void CSoundFile::Panning(ModChannel *pChn, uint32 param, PanningType panBits) co // IT Compatibility (and other trackers as well): panning disables surround (unless panning in rear channels is enabled, which is not supported by the original trackers anyway) if (!m_SongFlags[SONG_SURROUNDPAN] && (panBits == Pan8bit || m_playBehaviour[kPanOverride])) { - pChn->dwFlags.reset(CHN_SURROUND); + chn.dwFlags.reset(CHN_SURROUND); } if(panBits == Pan4bit) { // 0...15 panning - pChn->nPan = (param * 256 + 8) / 15; + chn.nPan = (param * 256 + 8) / 15; } else if(panBits == Pan6bit) { // 0...64 panning if(param > 64) param = 64; - pChn->nPan = param * 4; + chn.nPan = param * 4; } else { if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_DSM | MOD_TYPE_AMF0 | MOD_TYPE_AMF | MOD_TYPE_MTM))) { // Real 8-bit panning - pChn->nPan = param; + chn.nPan = param; } else { // 7-bit panning + surround if(param <= 0x80) { - pChn->nPan = param << 1; + chn.nPan = param << 1; } else if(param == 0xA4) { - pChn->dwFlags.set(CHN_SURROUND); - pChn->nPan = 0x80; + chn.dwFlags.set(CHN_SURROUND); + chn.nPan = 0x80; } } } - pChn->dwFlags.set(CHN_FASTVOLRAMP); - pChn->nRestorePanOnNewNote = 0; + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.nRestorePanOnNewNote = 0; //IT compatibility 20. Set pan overrides random pan if(m_playBehaviour[kPanOverride]) { - pChn->nPanSwing = 0; - pChn->nPanbrelloOffset = 0; + chn.nPanSwing = 0; + chn.nPanbrelloOffset = 0; } } -void CSoundFile::VolumeSlide(ModChannel *pChn, ModCommand::PARAM param) +void CSoundFile::VolumeSlide(ModChannel &chn, ModCommand::PARAM param) { if (param) - pChn->nOldVolumeSlide = param; + chn.nOldVolumeSlide = param; else - param = pChn->nOldVolumeSlide; + param = chn.nOldVolumeSlide; if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM))) { @@ -4107,20 +4211,20 @@ void CSoundFile::VolumeSlide(ModChannel *pChn, ModCommand::PARAM param) } } - int newvolume = pChn->nVolume; + int newVolume = chn.nVolume; if(!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_AMF0 | MOD_TYPE_MED | MOD_TYPE_DIGI))) { if ((param & 0x0F) == 0x0F) //Fine upslide or slide -15 { if (param & 0xF0) //Fine upslide { - FineVolumeUp(pChn, (param >> 4), false); + FineVolumeUp(chn, (param >> 4), false); return; } else //Slide -15 { - if(pChn->isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES]) + if(chn.isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES]) { - newvolume -= 0x0F * 4; + newVolume -= 0x0F * 4; } } } else @@ -4128,47 +4232,47 @@ void CSoundFile::VolumeSlide(ModChannel *pChn, ModCommand::PARAM param) { if (param & 0x0F) //Fine downslide { - FineVolumeDown(pChn, (param & 0x0F), false); + FineVolumeDown(chn, (param & 0x0F), false); return; } else //Slide +15 { - if(pChn->isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES]) + if(chn.isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES]) { - newvolume += 0x0F * 4; + newVolume += 0x0F * 4; } } } } - if(!pChn->isFirstTick || m_SongFlags[SONG_FASTVOLSLIDES] || (m_PlayState.m_nMusicSpeed == 1 && GetType() == MOD_TYPE_DBM)) + if(!chn.isFirstTick || m_SongFlags[SONG_FASTVOLSLIDES] || (m_PlayState.m_nMusicSpeed == 1 && GetType() == MOD_TYPE_DBM)) { // IT compatibility: Ignore slide commands with both nibbles set. if (param & 0x0F) { if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0) - newvolume -= (int)((param & 0x0F) * 4); + newVolume -= (int)((param & 0x0F) * 4); } else { - newvolume += (int)((param & 0xF0) >> 2); + newVolume += (int)((param & 0xF0) >> 2); } - if (GetType() == MOD_TYPE_MOD) pChn->dwFlags.set(CHN_FASTVOLRAMP); + if (GetType() == MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP); } - newvolume = Clamp(newvolume, 0, 256); + newVolume = Clamp(newVolume, 0, 256); - pChn->nVolume = newvolume; + chn.nVolume = newVolume; } -void CSoundFile::PanningSlide(ModChannel *pChn, ModCommand::PARAM param, bool memory) +void CSoundFile::PanningSlide(ModChannel &chn, ModCommand::PARAM param, bool memory) { if(memory) { // FT2 compatibility: Use effect memory (lxx and rxx in XM shouldn't use effect memory). // Test case: PanSlideMem.xm if(param) - pChn->nOldPanSlide = param; + chn.nOldPanSlide = param; else - param = pChn->nOldPanSlide; + param = chn.nOldPanSlide; } if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) @@ -4230,74 +4334,74 @@ void CSoundFile::PanningSlide(ModChannel *pChn, ModCommand::PARAM param, bool me } if (nPanSlide) { - nPanSlide += pChn->nPan; + nPanSlide += chn.nPan; nPanSlide = Clamp(nPanSlide, 0, 256); - pChn->nPan = nPanSlide; - pChn->nRestorePanOnNewNote = 0; + chn.nPan = nPanSlide; + chn.nRestorePanOnNewNote = 0; } } -void CSoundFile::FineVolumeUp(ModChannel *pChn, ModCommand::PARAM param, bool volCol) const +void CSoundFile::FineVolumeUp(ModChannel &chn, ModCommand::PARAM param, bool volCol) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: EAx / EBx memory is not linked // Test case: FineVol-LinkMem.xm - if(param) pChn->nOldFineVolUpDown = (param << 4) | (pChn->nOldFineVolUpDown & 0x0F); else param = (pChn->nOldFineVolUpDown >> 4); + if(param) chn.nOldFineVolUpDown = (param << 4) | (chn.nOldFineVolUpDown & 0x0F); else param = (chn.nOldFineVolUpDown >> 4); } else if(volCol) { - if(param) pChn->nOldVolParam = param; else param = pChn->nOldVolParam; + if(param) chn.nOldVolParam = param; else param = chn.nOldVolParam; } else { - if(param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; + if(param) chn.nOldFineVolUpDown = param; else param = chn.nOldFineVolUpDown; } - if(pChn->isFirstTick) + if(chn.isFirstTick) { - pChn->nVolume += param * 4; - if(pChn->nVolume > 256) pChn->nVolume = 256; - if(GetType() & MOD_TYPE_MOD) pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.nVolume += param * 4; + if(chn.nVolume > 256) chn.nVolume = 256; + if(GetType() & MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP); } } -void CSoundFile::FineVolumeDown(ModChannel *pChn, ModCommand::PARAM param, bool volCol) const +void CSoundFile::FineVolumeDown(ModChannel &chn, ModCommand::PARAM param, bool volCol) const { if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: EAx / EBx memory is not linked // Test case: FineVol-LinkMem.xm - if(param) pChn->nOldFineVolUpDown = param | (pChn->nOldFineVolUpDown & 0xF0); else param = (pChn->nOldFineVolUpDown & 0x0F); + if(param) chn.nOldFineVolUpDown = param | (chn.nOldFineVolUpDown & 0xF0); else param = (chn.nOldFineVolUpDown & 0x0F); } else if(volCol) { - if(param) pChn->nOldVolParam = param; else param = pChn->nOldVolParam; + if(param) chn.nOldVolParam = param; else param = chn.nOldVolParam; } else { - if(param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; + if(param) chn.nOldFineVolUpDown = param; else param = chn.nOldFineVolUpDown; } - if(pChn->isFirstTick) + if(chn.isFirstTick) { - pChn->nVolume -= param * 4; - if(pChn->nVolume < 0) pChn->nVolume = 0; - if(GetType() & MOD_TYPE_MOD) pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.nVolume -= param * 4; + if(chn.nVolume < 0) chn.nVolume = 0; + if(GetType() & MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP); } } -void CSoundFile::Tremolo(ModChannel *pChn, uint32 param) const +void CSoundFile::Tremolo(ModChannel &chn, uint32 param) const { - if (param & 0x0F) pChn->nTremoloDepth = (param & 0x0F) << 2; - if (param & 0xF0) pChn->nTremoloSpeed = (param >> 4) & 0x0F; - pChn->dwFlags.set(CHN_TREMOLO); + if (param & 0x0F) chn.nTremoloDepth = (param & 0x0F) << 2; + if (param & 0xF0) chn.nTremoloSpeed = (param >> 4) & 0x0F; + chn.dwFlags.set(CHN_TREMOLO); } -void CSoundFile::ChannelVolSlide(ModChannel *pChn, ModCommand::PARAM param) const +void CSoundFile::ChannelVolSlide(ModChannel &chn, ModCommand::PARAM param) const { int32 nChnSlide = 0; - if (param) pChn->nOldChnVolSlide = param; else param = pChn->nOldChnVolSlide; + if (param) chn.nOldChnVolSlide = param; else param = chn.nOldChnVolSlide; if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { @@ -4321,35 +4425,35 @@ void CSoundFile::ChannelVolSlide(ModChannel *pChn, ModCommand::PARAM param) cons } if (nChnSlide) { - nChnSlide += pChn->nGlobalVol; + nChnSlide += chn.nGlobalVol; nChnSlide = Clamp(nChnSlide, 0, 64); - pChn->nGlobalVol = nChnSlide; + chn.nGlobalVol = nChnSlide; } } void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param) { - ModChannel *pChn = &m_PlayState.Chn[nChn]; + ModChannel &chn = m_PlayState.Chn[nChn]; uint8 command = param & 0xF0; param &= 0x0F; switch(command) { // E0x: Set Filter case 0x00: - for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) + for(CHANNELINDEX channel = 0; channel < GetNumChannels(); channel++) { - m_PlayState.Chn[chn].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); + m_PlayState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); } break; // E1x: Fine Portamento Up - case 0x10: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoUp(pChn, param); break; + case 0x10: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoUp(chn, param); break; // E2x: Fine Portamento Down - case 0x20: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoDown(pChn, param); break; + case 0x20: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoDown(chn, param); break; // E3x: Set Glissando Control - case 0x30: pChn->dwFlags.set(CHN_GLISSANDO, param != 0); break; + case 0x30: chn.dwFlags.set(CHN_GLISSANDO, param != 0); break; // E4x: Set Vibrato WaveForm - case 0x40: pChn->nVibratoType = param & 0x07; break; + case 0x40: chn.nVibratoType = param & 0x07; break; // E5x: Set FineTune case 0x50: if(!m_SongFlags[SONG_FIRSTTICK]) { @@ -4357,30 +4461,30 @@ void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param) } if(GetType() & (MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_AMF0 | MOD_TYPE_MED)) { - pChn->nFineTune = MOD2XMFineTune(param); - if(pChn->nPeriod && pChn->rowCommand.IsNote()) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC5Speed); - } else if(pChn->rowCommand.IsNote()) + chn.nFineTune = MOD2XMFineTune(param); + if(chn.nPeriod && chn.rowCommand.IsNote()) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); + } else if(chn.rowCommand.IsNote()) { - pChn->nFineTune = MOD2XMFineTune(param - 8); - if(pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC5Speed); + chn.nFineTune = MOD2XMFineTune(param - 8); + if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } break; // E6x: Pattern Loop // E7x: Set Tremolo WaveForm - case 0x70: pChn->nTremoloType = param & 0x07; break; + case 0x70: chn.nTremoloType = param & 0x07; break; // E8x: Set 4-bit Panning case 0x80: if(m_SongFlags[SONG_FIRSTTICK]) { - Panning(pChn, param, Pan4bit); + Panning(chn, param, Pan4bit); } break; // E9x: Retrig case 0x90: RetrigNote(nChn, param); break; // EAx: Fine Volume Up - case 0xA0: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeUp(pChn, param, false); break; + case 0xA0: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeUp(chn, param, false); break; // EBx: Fine Volume Down - case 0xB0: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeDown(pChn, param, false); break; + case 0xB0: if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeDown(chn, param, false); break; // ECx: Note Cut case 0xC0: NoteCut(nChn, param, false); break; // EDx: Note Delay @@ -4388,11 +4492,11 @@ void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param) case 0xF0: if(GetType() == MOD_TYPE_MOD) // MOD: Invert Loop { - pChn->nEFxSpeed = param; - if(m_SongFlags[SONG_FIRSTTICK]) InvertLoop(pChn); + chn.nEFxSpeed = param; + if(m_SongFlags[SONG_FIRSTTICK]) InvertLoop(chn); } else // XM: Set Active Midi Macro { - pChn->nActiveMacro = param; + chn.nActiveMacro = param; } break; } @@ -4401,50 +4505,50 @@ void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param) void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) { - ModChannel *pChn = &m_PlayState.Chn[nChn]; + ModChannel &chn = m_PlayState.Chn[nChn]; uint8 command = param & 0xF0; param &= 0x0F; switch(command) { // S0x: Set Filter // S1x: Set Glissando Control - case 0x10: pChn->dwFlags.set(CHN_GLISSANDO, param != 0); break; + case 0x10: chn.dwFlags.set(CHN_GLISSANDO, param != 0); break; // S2x: Set FineTune case 0x20: if(!m_SongFlags[SONG_FIRSTTICK]) break; if(GetType() != MOD_TYPE_669) { - pChn->nC5Speed = S3MFineTuneTable[param]; - pChn->nFineTune = MOD2XMFineTune(param); - if (pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC5Speed); - } else if(pChn->pModSample != nullptr) + chn.nC5Speed = S3MFineTuneTable[param]; + chn.nFineTune = MOD2XMFineTune(param); + if (chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); + } else if(chn.pModSample != nullptr) { - pChn->nC5Speed = pChn->pModSample->nC5Speed + param * 80; + chn.nC5Speed = chn.pModSample->nC5Speed + param * 80; } break; // S3x: Set Vibrato Waveform case 0x30: if(GetType() == MOD_TYPE_S3M) { - pChn->nVibratoType = param & 0x03; + chn.nVibratoType = param & 0x03; } else { // IT compatibility: Ignore waveform types > 3 if(m_playBehaviour[kITVibratoTremoloPanbrello]) - pChn->nVibratoType = (param < 0x04) ? param : 0; + chn.nVibratoType = (param < 0x04) ? param : 0; else - pChn->nVibratoType = param & 0x07; + chn.nVibratoType = param & 0x07; } break; // S4x: Set Tremolo Waveform case 0x40: if(GetType() == MOD_TYPE_S3M) { - pChn->nTremoloType = param & 0x03; + chn.nTremoloType = param & 0x03; } else { // IT compatibility: Ignore waveform types > 3 if(m_playBehaviour[kITVibratoTremoloPanbrello]) - pChn->nTremoloType = (param < 0x04) ? param : 0; + chn.nTremoloType = (param < 0x04) ? param : 0; else - pChn->nTremoloType = param & 0x07; + chn.nTremoloType = param & 0x07; } break; // S5x: Set Panbrello Waveform @@ -4452,11 +4556,11 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) // IT compatibility: Ignore waveform types > 3 if(m_playBehaviour[kITVibratoTremoloPanbrello]) { - pChn->nPanbrelloType = (param < 0x04) ? param : 0; - pChn->nPanbrelloPos = 0; + chn.nPanbrelloType = (param < 0x04) ? param : 0; + chn.nPanbrelloPos = 0; } else { - pChn->nPanbrelloType = param & 0x07; + chn.nPanbrelloType = param & 0x07; } break; // S6x: Pattern Delay for x frames @@ -4481,50 +4585,56 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) case 1: case 2: { - ModChannel *bkp = &m_PlayState.Chn[m_nChannels]; - for (CHANNELINDEX i=m_nChannels; inMasterChn == nChn+1) + ModChannel &bkChn = m_PlayState.Chn[i]; + if (bkChn.nMasterChn == nChn + 1) { if (param == 1) { - KeyOff(bkp); + KeyOff(bkChn); + if(bkChn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteOff(i); } else if (param == 2) { - bkp->dwFlags.set(CHN_NOTEFADE); + bkChn.dwFlags.set(CHN_NOTEFADE); + if(bkChn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteOff(i); } else { - bkp->dwFlags.set(CHN_NOTEFADE); - bkp->nFadeOutVol = 0; + bkChn.dwFlags.set(CHN_NOTEFADE); + bkChn.nFadeOutVol = 0; + if(bkChn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteCut(i); } #ifndef NO_PLUGINS - const ModInstrument *pIns = bkp->pModInstrument; + const ModInstrument *pIns = bkChn.pModInstrument; IMixPlugin *pPlugin; if(pIns != nullptr && pIns->nMixPlug && (pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin) != nullptr) { - pPlugin->MidiCommand(GetBestMidiChannel(nChn), pIns->nMidiProgram, pIns->wMidiBank, bkp->nNote + NOTE_MAX_SPECIAL, 0, nChn); + pPlugin->MidiCommand(*pIns, bkChn.nNote + NOTE_MAX_SPECIAL, 0, nChn); } #endif // NO_PLUGINS } } } break; - case 3: pChn->nNNA = NNA_NOTECUT; break; - case 4: pChn->nNNA = NNA_CONTINUE; break; - case 5: pChn->nNNA = NNA_NOTEOFF; break; - case 6: pChn->nNNA = NNA_NOTEFADE; break; - case 7: pChn->VolEnv.flags.reset(ENV_ENABLED); break; - case 8: pChn->VolEnv.flags.set(ENV_ENABLED); break; - case 9: pChn->PanEnv.flags.reset(ENV_ENABLED); break; - case 10: pChn->PanEnv.flags.set(ENV_ENABLED); break; - case 11: pChn->PitchEnv.flags.reset(ENV_ENABLED); break; - case 12: pChn->PitchEnv.flags.set(ENV_ENABLED); break; + case 3: chn.nNNA = NNA_NOTECUT; break; + case 4: chn.nNNA = NNA_CONTINUE; break; + case 5: chn.nNNA = NNA_NOTEOFF; break; + case 6: chn.nNNA = NNA_NOTEFADE; break; + case 7: chn.VolEnv.flags.reset(ENV_ENABLED); break; + case 8: chn.VolEnv.flags.set(ENV_ENABLED); break; + case 9: chn.PanEnv.flags.reset(ENV_ENABLED); break; + case 10: chn.PanEnv.flags.set(ENV_ENABLED); break; + case 11: chn.PitchEnv.flags.reset(ENV_ENABLED); break; + case 12: chn.PitchEnv.flags.set(ENV_ENABLED); break; case 13: // S7D: Enable pitch envelope, force to play as pitch envelope case 14: // S7E: Enable pitch envelope, force to play as filter envelope if(GetType() == MOD_TYPE_MPT) { - pChn->PitchEnv.flags.set(ENV_ENABLED); - pChn->PitchEnv.flags.set(ENV_FILTER, param != 13); + chn.PitchEnv.flags.set(ENV_ENABLED); + chn.PitchEnv.flags.set(ENV_FILTER, param != 13); } break; } @@ -4533,19 +4643,19 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) case 0x80: if(m_SongFlags[SONG_FIRSTTICK]) { - Panning(pChn, param, Pan4bit); + Panning(chn, param, Pan4bit); } break; // S9x: Sound Control - case 0x90: ExtendedChannelEffect(pChn, param); break; + case 0x90: ExtendedChannelEffect(chn, param); break; // SAx: Set 64k Offset case 0xA0: if(m_SongFlags[SONG_FIRSTTICK]) { - pChn->nOldHiOffset = static_cast(param); - if (!m_playBehaviour[kITHighOffsetNoRetrig] && pChn->rowCommand.IsNote()) + chn.nOldHiOffset = static_cast(param); + if (!m_playBehaviour[kITHighOffsetNoRetrig] && chn.rowCommand.IsNote()) { SmpLength pos = param << 16; - if (pos < pChn->nLength) pChn->position.SetInt(pos); + if (pos < chn.nLength) chn.position.SetInt(pos); } } break; @@ -4571,35 +4681,35 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) case 0xF0: if(GetType() != MOD_TYPE_S3M) { - pChn->nActiveMacro = static_cast(param); + chn.nActiveMacro = static_cast(param); } break; } } -void CSoundFile::ExtendedChannelEffect(ModChannel *pChn, uint32 param) +void CSoundFile::ExtendedChannelEffect(ModChannel &chn, uint32 param) { // S9x and X9x commands (S3M/XM/IT only) if(!m_SongFlags[SONG_FIRSTTICK]) return; switch(param & 0x0F) { // S90: Surround Off - case 0x00: pChn->dwFlags.reset(CHN_SURROUND); break; + case 0x00: chn.dwFlags.reset(CHN_SURROUND); break; // S91: Surround On - case 0x01: pChn->dwFlags.set(CHN_SURROUND); pChn->nPan = 128; break; + case 0x01: chn.dwFlags.set(CHN_SURROUND); chn.nPan = 128; break; //////////////////////////////////////////////////////////// // ModPlug Extensions // S98: Reverb Off case 0x08: - pChn->dwFlags.reset(CHN_REVERB); - pChn->dwFlags.set(CHN_NOREVERB); + chn.dwFlags.reset(CHN_REVERB); + chn.dwFlags.set(CHN_NOREVERB); break; // S99: Reverb On case 0x09: - pChn->dwFlags.reset(CHN_NOREVERB); - pChn->dwFlags.set(CHN_REVERB); + chn.dwFlags.reset(CHN_NOREVERB); + chn.dwFlags.set(CHN_REVERB); break; // S9A: 2-Channels surround mode case 0x0A: @@ -4619,39 +4729,39 @@ void CSoundFile::ExtendedChannelEffect(ModChannel *pChn, uint32 param) break; // S9E: Go forward case 0x0E: - pChn->dwFlags.reset(CHN_PINGPONGFLAG); + chn.dwFlags.reset(CHN_PINGPONGFLAG); break; // S9F: Go backward (and set playback position to the end if sample just started) case 0x0F: - if(pChn->position.IsZero() && pChn->nLength && (pChn->rowCommand.IsNote() || !pChn->dwFlags[CHN_LOOP])) + if(chn.position.IsZero() && chn.nLength && (chn.rowCommand.IsNote() || !chn.dwFlags[CHN_LOOP])) { - pChn->position.Set(pChn->nLength - 1, SamplePosition::fractMax); + chn.position.Set(chn.nLength - 1, SamplePosition::fractMax); } - pChn->dwFlags.set(CHN_PINGPONGFLAG); + chn.dwFlags.set(CHN_PINGPONGFLAG); break; } } -void CSoundFile::InvertLoop(ModChannel *pChn) +void CSoundFile::InvertLoop(ModChannel &chn) { // EFx implementation for MOD files (PT 1.1A and up: Invert Loop) // This effect trashes samples. Thanks to 8bitbubsy for making this work. :) - if(GetType() != MOD_TYPE_MOD || pChn->nEFxSpeed == 0) return; + if(GetType() != MOD_TYPE_MOD || chn.nEFxSpeed == 0) return; // we obviously also need a sample for this - ModSample *pModSample = const_cast(pChn->pModSample); - if(pModSample == nullptr || pModSample->pSample == nullptr || !pModSample->uFlags[CHN_LOOP] || pModSample->uFlags[CHN_16BIT]) return; + ModSample *pModSample = const_cast(chn.pModSample); + if(pModSample == nullptr || !pModSample->HasSampleData() || !pModSample->uFlags[CHN_LOOP] || pModSample->uFlags[CHN_16BIT]) return; - pChn->nEFxDelay += ModEFxTable[pChn->nEFxSpeed & 0x0F]; - if((pChn->nEFxDelay & 0x80) == 0) return; // only applied if the "delay" reaches 128 - pChn->nEFxDelay = 0; + chn.nEFxDelay += ModEFxTable[chn.nEFxSpeed & 0x0F]; + if((chn.nEFxDelay & 0x80) == 0) return; // only applied if the "delay" reaches 128 + chn.nEFxDelay = 0; - if (++pChn->nEFxOffset >= pModSample->nLoopEnd - pModSample->nLoopStart) - pChn->nEFxOffset = 0; + if (++chn.nEFxOffset >= pModSample->nLoopEnd - pModSample->nLoopStart) + chn.nEFxOffset = 0; // TRASH IT!!! (Yes, the sample!) - uint8 &sample = static_cast(pModSample->pSample)[pModSample->nLoopStart + pChn->nEFxOffset]; + uint8 &sample = mpt::byte_cast(pModSample->sampleb())[pModSample->nLoopStart + chn.nEFxOffset]; sample = ~sample; ctrlSmp::PrecomputeLoops(*pModSample, *this, false); } @@ -4669,7 +4779,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * ModChannel &chn = m_PlayState.Chn[nChn]; const ModInstrument *pIns = GetNumInstruments() ? chn.pModInstrument : nullptr; - unsigned char out[MACRO_LENGTH]; + uint8 out[MACRO_LENGTH]; uint32 outPos = 0; // output buffer position, which also equals the number of complete bytes const uint8 lastZxxParam = chn.lastZxxParam; bool firstNibble = true; @@ -4677,78 +4787,115 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * for(uint32 pos = 0; pos < (MACRO_LENGTH - 1) && macro[pos]; pos++) { bool isNibble = false; // did we parse a nibble or a byte value? - unsigned char data = 0; // data that has just been parsed + uint8 data = 0; // data that has just been parsed // Parse next macro byte... See Impulse Tracker's MIDI.TXT for detailed information on each possible character. if(macro[pos] >= '0' && macro[pos] <= '9') { isNibble = true; - data = (unsigned char)macro[pos] - '0'; + data = static_cast(macro[pos] - '0'); } else if(macro[pos] >= 'A' && macro[pos] <= 'F') { isNibble = true; - data = (unsigned char)macro[pos] - 'A' + 0x0A; - } else if(macro[pos] == 'c') // c: MIDI channel + data = static_cast(macro[pos] - 'A' + 0x0A); + } else if(macro[pos] == 'c') { + // MIDI channel isNibble = true; - data = (unsigned char)GetBestMidiChannel(nChn); - } else if(macro[pos] == 'n') // n: note value (last triggered note) + data = GetBestMidiChannel(nChn); + } else if(macro[pos] == 'n') { + // Last triggered note if(ModCommand::IsNote(chn.nLastNote)) { - data = (unsigned char)(chn.nLastNote - NOTE_MIN); + data = chn.nLastNote - NOTE_MIN; } - } else if(macro[pos] == 'v') // v: velocity + } else if(macro[pos] == 'v') { + // Velocity // This is "almost" how IT does it - apparently, IT seems to lag one row behind on global volume or channel volume changes. const int swing = (m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) ? chn.nVolSwing : 0; const int vol = Util::muldiv((chn.nVolume + swing) * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 20); - data = (unsigned char)Clamp(vol / 2, 1, 127); + data = static_cast(Clamp(vol / 2, 1, 127)); //data = (unsigned char)MIN((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127); - } else if(macro[pos] == 'u') // u: volume (calculated) + } else if(macro[pos] == 'u') { + // Calculated volume // Same note as with velocity applies here, but apparently also for instrument / sample volumes? const int vol = Util::muldiv(chn.nCalcVolume * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 26); - data = (unsigned char)Clamp(vol / 2, 1, 127); + data = static_cast(Clamp(vol / 2, 1, 127)); //data = (unsigned char)MIN((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127); - } else if(macro[pos] == 'x') // x: pan set + } else if(macro[pos] == 'x') { - data = (unsigned char)std::min(chn.nPan / 2, 127); - } else if(macro[pos] == 'y') // y: calculated pan + // Pan set + data = static_cast(std::min(chn.nPan / 2, 127)); + } else if(macro[pos] == 'y') { - data = (unsigned char)std::min(chn.nRealPan / 2, 127); - } else if(macro[pos] == 'a') // a: high byte of bank select + // Calculated pan + data = static_cast(std::min(chn.nRealPan / 2, 127)); + } else if(macro[pos] == 'a') { + // High byte of bank select if(pIns && pIns->wMidiBank) { - data = (unsigned char)(((pIns->wMidiBank - 1) >> 7) & 0x7F); + data = static_cast(((pIns->wMidiBank - 1) >> 7) & 0x7F); } - } else if(macro[pos] == 'b') // b: low byte of bank select + } else if(macro[pos] == 'b') { + // Low byte of bank select if(pIns && pIns->wMidiBank) { - data = (unsigned char)((pIns->wMidiBank - 1) & 0x7F); + data = static_cast((pIns->wMidiBank - 1) & 0x7F); } - } else if(macro[pos] == 'p') // p: program select + } else if(macro[pos] == 'o') { + // Offset (ignoring high offset) + data = static_cast((chn.oldOffset >> 8) & 0xFF); + } else if(macro[pos] == 'h') + { + // Host channel number + data = static_cast((nChn >= GetNumChannels() ? (chn.nMasterChn - 1) : nChn) & 0x7F); + } else if(macro[pos] == 'm') + { + // Loop direction (judging from the character, it was supposed to be loop type, though) + data = chn.dwFlags[CHN_PINGPONGFLAG] ? 1 : 0; + } else if(macro[pos] == 'p') + { + // Program select if(pIns && pIns->nMidiProgram) { - data = (unsigned char)((pIns->nMidiProgram - 1) & 0x7F); + data = static_cast((pIns->nMidiProgram - 1) & 0x7F); } - } else if(macro[pos] == 'z') // z: macro data + } else if(macro[pos] == 'z') { + // Zxx parameter data = param & 0x7F; if(isSmooth && chn.lastZxxParam < 0x80 && (outPos < 3 || out[outPos - 3] != 0xF0 || out[outPos - 2] < 0xF0)) { // Interpolation for external MIDI messages - interpolation for internal messages // is handled separately to allow for more than 7-bit granularity where it's possible - data = (uint8)CalculateSmoothParamChange((float)lastZxxParam, (float)data); + data = static_cast(CalculateSmoothParamChange(lastZxxParam, data)); } chn.lastZxxParam = data; - } else // unrecognized byte (e.g. space char) + } else if(macro[pos] == 's') { + // SysEx Checksum (not an original Impulse Tracker macro variable, but added for convenience) + uint32 startPos = outPos; + while(startPos > 0 && out[--startPos] != 0xF0); + if(outPos - startPos < 5 || out[startPos] != 0xF0) + { + continue; + } + for(uint32 p = startPos + 5; p != outPos; p++) + { + data += out[p]; + } + data = (~data + 1) & 0x7F; + } else + { + // Unrecognized byte (e.g. space char) continue; } @@ -4780,14 +4927,9 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * outPos++; } - if(outPos == 0) - { - // Nothing there to send! - return; - } - // Macro string has been parsed and translated, now send the message(s)... uint32 sendPos = 0; + uint8 runningStatus = 0; while(sendPos < outPos) { uint32 sendLen = 0; @@ -4817,17 +4959,23 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * sendLen = outPos - sendPos; } } + } else if(!(out[sendPos] & 0x80)) + { + // Missing status byte? Try inserting running status + if(runningStatus != 0) + { + sendPos--; + out[sendPos] = runningStatus; + } else + { + // No running status to re-use; skip this byte + sendPos++; + } + continue; } else { - // Other MIDI messages, find beginning of next message - while(sendPos + (++sendLen) < outPos) - { - if((out[sendPos + sendLen] & 0x80) != 0) - { - // Next message begins here. - break; - } - } + // Other MIDI messages + sendLen = std::min(MIDIEvents::GetEventLength(out[sendPos]), outPos - sendPos); } if(sendLen == 0) @@ -4835,8 +4983,12 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * break; } + if(out[sendPos] < 0xF0) + { + runningStatus = out[sendPos]; + } uint32 bytesSent = SendMIDIData(nChn, isSmooth, out + sendPos, sendLen, plugin); - // Ideally (if there's no error in the macro data), we should have sendLen == bytesSent. + // If there's no error in the macro data (e.g. unrecognized internal MIDI macro), we have sendLen == bytesSent. if(bytesSent > 0) { sendPos += bytesSent; @@ -4866,7 +5018,7 @@ float CSoundFile::CalculateSmoothParamChange(float currentValue, float param) co } -// Process MIDI macro data parsed by ProcessMIDIMacro... return bytes sent on success, 0 on (parse) failure. +// Process exactly one MIDI message parsed by ProcessMIDIMacro. Returns bytes sent on success, 0 on (parse) failure. uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned char *macro, uint32 macroLen, PLUGINDEX plugin) { if(macroLen < 1) @@ -4874,8 +5026,17 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned return 0; } - ModChannel &chn = m_PlayState.Chn[nChn]; + if(macro[0] == 0xFA || macro[0] == 0xFC || macro[0] == 0xFF) + { + // Start Song, Stop Song, MIDI Reset - both interpreted internally and sent to plugins + for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) + { + m_PlayState.Chn[chn].nCutOff = 0x7F; + m_PlayState.Chn[chn].nResonance = 0x00; + } + } + ModChannel &chn = m_PlayState.Chn[nChn]; if(macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1)) { // Internal device. @@ -4895,10 +5056,16 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned chn.nCutOff = param; } else { - chn.nCutOff = Util::Round(CalculateSmoothParamChange(chn.nCutOff, param)); + chn.nCutOff = mpt::saturate_round(CalculateSmoothParamChange(chn.nCutOff, param)); } chn.nRestoreCutoffOnNewNote = 0; - SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]); + int cutoff = SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); + + if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) + { + // Cutoff doubles as modulator intensity for FM instruments + m_opl->Volume(nChn, static_cast(cutoff / 4), true); + } return 4; } else if(macroCode == 0x01 && !isExtended && param < 0x80) @@ -4912,7 +5079,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned chn.nResonance = (uint8)CalculateSmoothParamChange((float)chn.nResonance, (float)param); } chn.nRestoreResonanceOnNewNote = 0; - SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]); + SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); return 4; } else if(macroCode == 0x02 && !isExtended) @@ -4921,7 +5088,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned if(param < 0x20) { chn.nFilterMode = (param >> 4); - SetupChannelFilter(&chn, !chn.dwFlags[CHN_FILTER]); + SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); } return 4; @@ -4932,7 +5099,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted); if(plug > 0 && plug <= MAX_MIXPLUGINS && param < 0x80) { - const float newRatio = 1.0f - (static_cast(param & 0x7F) / 127.0f); + const float newRatio = (0x7F - (param & 0x7F)) / 127.0f; if(!isSmooth) { m_MixPlugins[plug - 1].fDryRatio = newRatio; @@ -4990,17 +5157,13 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned { if(macro[0] == 0xF0) { - pPlugin->MidiSysexSend(macro, macroLen); + pPlugin->MidiSysexSend(mpt::as_span(mpt::byte_cast(macro), macroLen)); } else { - for(uint32 pos = 0; pos < macroLen;) - { - uint32 len = std::min(MIDIEvents::GetEventLength(macro[pos]), macroLen - pos); - uint32 curData = 0; - memcpy(&curData, macro + pos, len); - pPlugin->MidiSend(curData); - pos += len; - } + uint32 len = std::min(MIDIEvents::GetEventLength(macro[0]), macroLen); + uint32 curData = 0; + memcpy(&curData, macro, len); + pPlugin->MidiSend(curData); } } } @@ -5032,7 +5195,7 @@ void CSoundFile::SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume) IMixPlugin *pPlug = m_MixPlugins[plug - 1].pMixPlugin; if (pPlug != nullptr) { - pPlug->MidiCommand(GetBestMidiChannel(chn), pIns->nMidiProgram, pIns->wMidiBank, note, volume, chn); + pPlug->MidiCommand(*pIns, note, volume, chn); if(note < NOTE_MIN_SPECIAL) channel.nLeftVU = channel.nRightVU = 0xFF; } @@ -5044,7 +5207,12 @@ void CSoundFile::SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume) void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const { - chn.proTrackerOffset += param; + // ST3 compatibility: Instrument-less note recalls previous note's offset + // Test case: OxxMemory.s3m + if(m_playBehaviour[kST3OffsetWithoutInstrument]) + chn.prevNoteOffset = 0; + + chn.prevNoteOffset += param; if(param >= chn.nLoopEnd && GetType() == MOD_TYPE_MTM && chn.dwFlags[CHN_LOOP] && chn.nLoopEnd > 0) { @@ -5073,8 +5241,8 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const { // ProTracker compatbility: PT1/2-style funky 9xx offset command // Test case: ptoffset.mod - chn.position.Set(chn.proTrackerOffset); - chn.proTrackerOffset += param; + chn.position.Set(chn.prevNoteOffset); + chn.prevNoteOffset += param; } else { chn.position.Set(param); @@ -5245,7 +5413,7 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) { if((chn.rowCommand.instr) && (param < 0x100)) { - InstrumentChange(&chn, chn.rowCommand.instr, false, false); + InstrumentChange(chn, chn.rowCommand.instr, false, false); resetEnv = true; } if (param < 0x100) resetEnv = true; @@ -5253,7 +5421,7 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) bool fading = chn.dwFlags[CHN_NOTEFADE]; // IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase) // Test case: retrig.it - NoteChange(&chn, note, m_playBehaviour[kITRetrigger], resetEnv); + NoteChange(chn, note, m_playBehaviour[kITRetrigger], resetEnv); // XM compatibility: Prevent NoteChange from resetting the fade flag in case an instrument number + note-off is present. // Test case: RetrigFade.xm if(fading && GetType() == MOD_TYPE_XM) @@ -5289,9 +5457,9 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) } -void CSoundFile::DoFreqSlide(ModChannel *pChn, int32 nFreqSlide) const +void CSoundFile::DoFreqSlide(ModChannel &chn, int32 nFreqSlide) const { - if(!pChn->nPeriod) return; + if(!chn.nPeriod) return; if(GetType() == MOD_TYPE_669) { // Like other oldskool trackers, Composer 669 doesn't have linear slides... @@ -5302,32 +5470,32 @@ void CSoundFile::DoFreqSlide(ModChannel *pChn, int32 nFreqSlide) const if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM) { // IT Linear slides - const auto nOldPeriod = pChn->nPeriod; + const auto nOldPeriod = chn.nPeriod; uint32 n = mpt::abs(nFreqSlide) / 4u; LimitMax(n, 255u); if(n != 0) { - pChn->nPeriod = Util::muldivr(pChn->nPeriod, nFreqSlide < 0 ? GetLinearSlideUpTable(this, n) : GetLinearSlideDownTable(this, n), 65536); - if(pChn->nPeriod == nOldPeriod) + chn.nPeriod = Util::muldivr(chn.nPeriod, nFreqSlide < 0 ? GetLinearSlideUpTable(this, n) : GetLinearSlideDownTable(this, n), 65536); + if(chn.nPeriod == nOldPeriod) { const bool incPeriod = m_playBehaviour[kHertzInLinearMode] == (nFreqSlide < 0); - if(incPeriod && pChn->nPeriod < Util::MaxValueOfType(pChn->nPeriod)) - pChn->nPeriod++; - else if(!incPeriod && pChn->nPeriod > 1) - pChn->nPeriod--; + if(incPeriod && chn.nPeriod < Util::MaxValueOfType(chn.nPeriod)) + chn.nPeriod++; + else if(!incPeriod && chn.nPeriod > 1) + chn.nPeriod--; } } } else { - pChn->nPeriod += nFreqSlide; + chn.nPeriod += nFreqSlide; } - if (pChn->nPeriod < 1) + if (chn.nPeriod < 1) { - pChn->nPeriod = 1; + chn.nPeriod = 1; if(GetType() == MOD_TYPE_S3M) { - pChn->nFadeOutVol = 0; - pChn->dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); + chn.nFadeOutVol = 0; + chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); } } } @@ -5337,71 +5505,76 @@ void CSoundFile::NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample) { if (m_PlayState.m_nTickCount == nTick) { - ModChannel *pChn = &m_PlayState.Chn[nChn]; + ModChannel &chn = m_PlayState.Chn[nChn]; if(cutSample) { - pChn->increment.Set(0); - pChn->nFadeOutVol = 0; - pChn->dwFlags.set(CHN_NOTEFADE); + chn.increment.Set(0); + chn.nFadeOutVol = 0; + chn.dwFlags.set(CHN_NOTEFADE); } else { - pChn->nVolume = 0; + chn.nVolume = 0; } - pChn->dwFlags.set(CHN_FASTVOLRAMP); + chn.dwFlags.set(CHN_FASTVOLRAMP); // instro sends to a midi chan - SendMIDINote(nChn, /*pChn->nNote+*/NOTE_MAX_SPECIAL, 0); + SendMIDINote(nChn, /*chn.nNote+*/NOTE_MAX_SPECIAL, 0); + + if(chn.dwFlags[CHN_ADLIB] && m_opl) + { + m_opl->NoteCut(nChn); + } } } -void CSoundFile::KeyOff(ModChannel *pChn) const +void CSoundFile::KeyOff(ModChannel &chn) const { - const bool bKeyOn = !pChn->dwFlags[CHN_KEYOFF]; - pChn->dwFlags.set(CHN_KEYOFF); - if(pChn->pModInstrument != nullptr && !pChn->VolEnv.flags[ENV_ENABLED]) + const bool keyIsOn = !chn.dwFlags[CHN_KEYOFF]; + chn.dwFlags.set(CHN_KEYOFF); + if(chn.pModInstrument != nullptr && !chn.VolEnv.flags[ENV_ENABLED]) { - pChn->dwFlags.set(CHN_NOTEFADE); + chn.dwFlags.set(CHN_NOTEFADE); } - if (!pChn->nLength) return; - if (pChn->dwFlags[CHN_SUSTAINLOOP] && pChn->pModSample && bKeyOn) + if (!chn.nLength) return; + if (chn.dwFlags[CHN_SUSTAINLOOP] && chn.pModSample && keyIsOn) { - const ModSample *pSmp = pChn->pModSample; + const ModSample *pSmp = chn.pModSample; if(pSmp->uFlags[CHN_LOOP]) { if (pSmp->uFlags[CHN_PINGPONGLOOP]) - pChn->dwFlags.set(CHN_PINGPONGLOOP); + chn.dwFlags.set(CHN_PINGPONGLOOP); else - pChn->dwFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGFLAG); - pChn->dwFlags.set(CHN_LOOP); - pChn->nLength = pSmp->nLength; - pChn->nLoopStart = pSmp->nLoopStart; - pChn->nLoopEnd = pSmp->nLoopEnd; - if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; - if(pChn->position.GetUInt() > pChn->nLength) + chn.dwFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGFLAG); + chn.dwFlags.set(CHN_LOOP); + chn.nLength = pSmp->nLength; + chn.nLoopStart = pSmp->nLoopStart; + chn.nLoopEnd = pSmp->nLoopEnd; + if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd; + if(chn.position.GetUInt() > chn.nLength) { // Test case: SusAfterLoop.it - pChn->position.Set(pChn->position.GetInt() - pChn->nLength + pChn->nLoopStart); + chn.position.Set(chn.position.GetInt() - chn.nLength + chn.nLoopStart); } } else { - pChn->dwFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGFLAG); - pChn->nLength = pSmp->nLength; + chn.dwFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGFLAG); + chn.nLength = pSmp->nLength; } } - if (pChn->pModInstrument) + if (chn.pModInstrument) { - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; if((pIns->VolEnv.dwFlags[ENV_LOOP] || (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MDL))) && pIns->nFadeOut != 0) { - pChn->dwFlags.set(CHN_NOTEFADE); + chn.dwFlags.set(CHN_NOTEFADE); } - if (pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET) + if (pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET && chn.VolEnv.nEnvValueAtReleaseJump == NOT_YET_RELEASED) { - pChn->VolEnv.nEnvValueAtReleaseJump = pIns->VolEnv.GetValueFromPosition(pChn->VolEnv.nEnvPosition, 256); - pChn->VolEnv.nEnvPosition = pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick; + chn.VolEnv.nEnvValueAtReleaseJump = pIns->VolEnv.GetValueFromPosition(chn.VolEnv.nEnvPosition, 256); + chn.VolEnv.nEnvPosition = pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick; } } } @@ -5485,22 +5658,22 @@ void CSoundFile::SetTempo(TEMPO param, bool setFromUI) } -ROWINDEX CSoundFile::PatternLoop(ModChannel *pChn, uint32 param) +ROWINDEX CSoundFile::PatternLoop(ModChannel &chn, uint32 param) { if (param) { // Loop Repeat - if(pChn->nPatternLoopCount) + if(chn.nPatternLoopCount) { // There's a loop left - pChn->nPatternLoopCount--; - if(!pChn->nPatternLoopCount) + chn.nPatternLoopCount--; + if(!chn.nPatternLoopCount) { // IT compatibility 10. Pattern loops (+ same fix for S3M files) // When finishing a pattern loop, the next loop without a dedicated SB0 starts on the first row after the previous loop. if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M)) { - pChn->nPatternLoop = m_PlayState.m_nRow + 1; + chn.nPatternLoop = m_PlayState.m_nRow + 1; } return ROWINDEX_INVALID; @@ -5513,20 +5686,20 @@ ROWINDEX CSoundFile::PatternLoop(ModChannel *pChn, uint32 param) if(!m_playBehaviour[kITFT2PatternLoop] && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M))) { ModChannel *p = m_PlayState.Chn; - for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, p++) if (p != pChn) + for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, p++) if (p != &chn) { // Loop on other channel if(p->nPatternLoopCount) return ROWINDEX_INVALID; } } - pChn->nPatternLoopCount = static_cast(param); + chn.nPatternLoopCount = static_cast(param); } - m_PlayState.m_nNextPatStartRow = pChn->nPatternLoop; // Nasty FT2 E60 bug emulation! - return pChn->nPatternLoop; + m_PlayState.m_nNextPatStartRow = chn.nPatternLoop; // Nasty FT2 E60 bug emulation! + return chn.nPatternLoop; } else { // Loop Start - pChn->nPatternLoop = m_PlayState.m_nRow; + chn.nPatternLoop = m_PlayState.m_nRow; } return ROWINDEX_INVALID; } @@ -5563,7 +5736,7 @@ void CSoundFile::GlobalVolSlide(ModCommand::PARAM param, uint8 &nOldGlobalVolSli if (param & 0xF0) { // IT compatibility: Ignore slide commands with both nibbles set. - if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_AMS2 | MOD_TYPE_DBM)) || (param & 0x0F) == 0) + if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM)) || (param & 0x0F) == 0) nGlbSlide = (int)((param & 0xF0) >> 4) * 2; } else { @@ -5573,7 +5746,7 @@ void CSoundFile::GlobalVolSlide(ModCommand::PARAM param, uint8 &nOldGlobalVolSli } if (nGlbSlide) { - if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_AMS2 | MOD_TYPE_DBM))) nGlbSlide *= 2; + if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM))) nGlbSlide *= 2; nGlbSlide += m_PlayState.m_nGlobalVolume; Limit(nGlbSlide, 0, 256); m_PlayState.m_nGlobalVolume = nGlbSlide; @@ -5683,10 +5856,10 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe } else { nFineTune = XM2MODFineTune(nFineTune); - if ((nFineTune) || (note < 36) || (note >= 36 + 6 * 12)) + if ((nFineTune) || (note < 24) || (note >= 24 + mpt::size(ProTrackerPeriodTable))) return (ProTrackerTunedPeriods[nFineTune * 12u + note % 12u] << 5) >> (note / 12u); else - return (ProTrackerPeriodTable[note - 36] << 2); + return (ProTrackerPeriodTable[note - 24] << 2); } } @@ -5879,23 +6052,24 @@ IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(CHANNELINDEX chn) const // Get the MIDI channel currently associated with a given tracker channel -uint8 CSoundFile::GetBestMidiChannel(CHANNELINDEX nChn) const +uint8 CSoundFile::GetBestMidiChannel(CHANNELINDEX trackerChn) const { - if(nChn >= MAX_CHANNELS) + if(trackerChn >= mpt::size(m_PlayState.Chn)) { return 0; } - const ModInstrument *ins = m_PlayState.Chn[nChn].pModInstrument; + const ModChannel &chn = m_PlayState.Chn[trackerChn]; + const ModInstrument *ins = chn.pModInstrument; if(ins != nullptr) { if(ins->nMidiChannel == MidiMappedChannel) { // For mapped channels, return their pattern channel, modulo 16 (because there are only 16 MIDI channels) - return (m_PlayState.Chn[nChn].nMasterChn ? (m_PlayState.Chn[nChn].nMasterChn - 1) : nChn) % 16; + return static_cast((chn.nMasterChn ? (chn.nMasterChn - 1u) : trackerChn) % 16u); } else if(ins->HasValidMIDIChannel()) { - return (ins->nMidiChannel - 1) & 0x0F; + return (ins->nMidiChannel - 1u) % 16u; } } return 0; @@ -5947,45 +6121,45 @@ void CSoundFile::UpdateTimeSignature() } -void CSoundFile::PortamentoMPT(ModChannel* pChn, int param) +void CSoundFile::PortamentoMPT(ModChannel &chn, int param) { //Behavior: Modifies portamento by param-steps on every tick. //Note that step meaning depends on tuning. - pChn->m_PortamentoFineSteps += param; - pChn->m_CalculateFreq = true; + chn.m_PortamentoFineSteps += param; + chn.m_CalculateFreq = true; } -void CSoundFile::PortamentoFineMPT(ModChannel* pChn, int param) +void CSoundFile::PortamentoFineMPT(ModChannel &chn, int param) { //Behavior: Divides portamento change between ticks/row. For example //if Ticks/row == 6, and param == +-6, portamento goes up/down by one tuning-dependent //fine step every tick. if(m_PlayState.m_nTickCount == 0) - pChn->nOldFinePortaUpDown = 0; + chn.nOldFinePortaUpDown = 0; const int tickParam = static_cast((m_PlayState.m_nTickCount + 1.0) * param / m_PlayState.m_nMusicSpeed); - pChn->m_PortamentoFineSteps += (param >= 0) ? tickParam - pChn->nOldFinePortaUpDown : tickParam + pChn->nOldFinePortaUpDown; + chn.m_PortamentoFineSteps += (param >= 0) ? tickParam - chn.nOldFinePortaUpDown : tickParam + chn.nOldFinePortaUpDown; if(m_PlayState.m_nTickCount + 1 == m_PlayState.m_nMusicSpeed) - pChn->nOldFinePortaUpDown = static_cast(mpt::abs(param)); + chn.nOldFinePortaUpDown = static_cast(mpt::abs(param)); else - pChn->nOldFinePortaUpDown = static_cast(mpt::abs(tickParam)); + chn.nOldFinePortaUpDown = static_cast(mpt::abs(tickParam)); - pChn->m_CalculateFreq = true; + chn.m_CalculateFreq = true; } -void CSoundFile::PortamentoExtraFineMPT(ModChannel* pChn, int param) +void CSoundFile::PortamentoExtraFineMPT(ModChannel &chn, int param) { // This kinda behaves like regular fine portamento. // It changes the pitch by n finetune steps on the first tick. - if(pChn->isFirstTick) + if(chn.isFirstTick) { - pChn->m_PortamentoFineSteps += param; - pChn->m_CalculateFreq = true; + chn.m_PortamentoFineSteps += param; + chn.m_CalculateFreq = true; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp index 6369bcdbe..8531d0774 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp @@ -30,11 +30,10 @@ #include "tuningcollection.h" #include "plugins/PluginManager.h" #include "plugins/PlugInterface.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" #include "../common/FileReader.h" #include "Container.h" -#include -#include +#include "OPL.h" #ifndef NO_ARCHIVE_SUPPORT #include "../unarchiver/unarchiver.h" @@ -50,10 +49,10 @@ mpt::ustring FileHistory::AsISO8601() const if(openTime > 0) { // Calculate the date when editing finished. - double openSeconds = (double)openTime / (double)HISTORY_TIMER_PRECISION; + double openSeconds = static_cast(openTime) / HISTORY_TIMER_PRECISION; tm tmpLoadDate = loadDate; int64 loadDateSinceEpoch = mpt::Date::Unix::FromUTC(tmpLoadDate); - int64 saveDateSinceEpoch = loadDateSinceEpoch + Util::Round(openSeconds); + int64 saveDateSinceEpoch = loadDateSinceEpoch + mpt::saturate_round(openSeconds); date = mpt::Date::Unix(saveDateSinceEpoch).AsUTC(); } return mpt::Date::ToShortenedISO8601(date); @@ -71,39 +70,21 @@ CSoundFile::CSoundFile() : #ifndef MODPLUG_TRACKER m_NoteNames(NoteNamesSharp), #endif - m_pTuningsTuneSpecific(nullptr), m_pModSpecs(&ModSpecs::itEx), m_nType(MOD_TYPE_NONE), Patterns(*this), Order(*this), #ifdef MODPLUG_TRACKER m_MIDIMapper(*this), - m_pModDoc(nullptr), #endif m_PRNG(mpt::make_prng(mpt::global_prng())), - visitedSongRows(*this), - m_pCustomLog(nullptr) + visitedSongRows(*this) { MemsetZero(MixSoundBuffer); MemsetZero(MixRearBuffer); MemsetZero(MixFloatBuffer); - gnDryLOfsVol = 0; - gnDryROfsVol = 0; - m_nType = MOD_TYPE_NONE; - m_ContainerType = MOD_CONTAINERTYPE_NONE; - m_nMixChannels = 0; - m_nSamples = 0; - m_nInstruments = 0; -#ifndef MODPLUG_TRACKER - m_nFreqFactor = m_nTempoFactor = 65536; -#endif - m_nRepeatCount = 0; - m_nTempoMode = tempoModeClassic; - m_bIsRendering = false; #ifdef MODPLUG_TRACKER - m_lockRowStart = m_lockRowEnd = ROWINDEX_INVALID; - m_lockOrderStart = m_lockOrderEnd = ORDERINDEX_INVALID; m_bChannelMuteTogglePending.reset(); m_nDefaultRowsPerBeat = m_PlayState.m_nCurrentRowsPerBeat = (TrackerSettings::Instance().m_nRowHighlightBeats) ? TrackerSettings::Instance().m_nRowHighlightBeats : 4; @@ -167,6 +148,7 @@ void CSoundFile::InitializeGlobals(MODTYPE type) m_nSamples = 0; m_nSamplePreAmp = 48; m_nVSTiVolume = 48; + m_OPLVolumeFactor = m_OPLVolumeFactorScale; m_nDefaultSpeed = 6; m_nDefaultTempo.Set(125); m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; @@ -174,7 +156,8 @@ void CSoundFile::InitializeGlobals(MODTYPE type) m_nMinPeriod = 16; m_nMaxPeriod = 32767; m_nResampling = SRCMODE_DEFAULT; - m_dwLastSavedWithVersion = m_dwCreatedWithVersion = 0; + m_dwLastSavedWithVersion = Version(0); + m_dwCreatedWithVersion = Version(0); SetMixLevels(mixLevelsCompatible); @@ -184,7 +167,7 @@ void CSoundFile::InitializeGlobals(MODTYPE type) m_songName.clear(); m_songArtist.clear(); m_songMessage.clear(); - m_madeWithTracker.clear(); + m_modFormat = ModFormatDetails(); m_FileHistory.clear(); m_tempoSwing.clear(); @@ -205,6 +188,74 @@ void CSoundFile::InitializeChannels() } +struct FileFormatLoader +{ + decltype(CSoundFile::ProbeFileHeaderXM) *prober; + decltype(&CSoundFile::ReadXM) loader; +}; + +#ifdef MODPLUG_TRACKER +#define MPT_DECLARE_FORMAT(format) { nullptr, &CSoundFile::Read ## format } +#else +#define MPT_DECLARE_FORMAT(format) { CSoundFile::ProbeFileHeader ## format, &CSoundFile::Read ## format } +#endif + +// All module format loaders, in the order they should be executed. +// This order matters, depending on the format, due to some unfortunate +// clashes or lack of magic bytes that can lead to mis-detection of some formats. +// Apart from that, more common formats with sane magic bytes are also found +// at the top of the list to match the most common cases more quickly. +static constexpr FileFormatLoader ModuleFormatLoaders[] = +{ + MPT_DECLARE_FORMAT(XM), + MPT_DECLARE_FORMAT(IT), + MPT_DECLARE_FORMAT(S3M), + MPT_DECLARE_FORMAT(STM), + MPT_DECLARE_FORMAT(MED), + MPT_DECLARE_FORMAT(MTM), + MPT_DECLARE_FORMAT(MDL), + MPT_DECLARE_FORMAT(DBM), + MPT_DECLARE_FORMAT(FAR), + MPT_DECLARE_FORMAT(AMS), + MPT_DECLARE_FORMAT(AMS2), + MPT_DECLARE_FORMAT(OKT), + MPT_DECLARE_FORMAT(PTM), + MPT_DECLARE_FORMAT(ULT), + MPT_DECLARE_FORMAT(DMF), + MPT_DECLARE_FORMAT(DSM), + MPT_DECLARE_FORMAT(AMF_Asylum), + MPT_DECLARE_FORMAT(AMF_DSMI), + MPT_DECLARE_FORMAT(PSM), + MPT_DECLARE_FORMAT(PSM16), + MPT_DECLARE_FORMAT(MT2), + MPT_DECLARE_FORMAT(ITP), +#if defined(MODPLUG_TRACKER) || defined(MPT_FUZZ_TRACKER) + // These make little sense for a module player library + MPT_DECLARE_FORMAT(UAX), + MPT_DECLARE_FORMAT(WAV), + MPT_DECLARE_FORMAT(MID), +#endif // MODPLUG_TRACKER || MPT_FUZZ_TRACKER + MPT_DECLARE_FORMAT(GDM), + MPT_DECLARE_FORMAT(IMF), + MPT_DECLARE_FORMAT(DIGI), + MPT_DECLARE_FORMAT(DTM), + MPT_DECLARE_FORMAT(PLM), + MPT_DECLARE_FORMAT(AM), + MPT_DECLARE_FORMAT(J2B), + MPT_DECLARE_FORMAT(PT36), + MPT_DECLARE_FORMAT(SFX), + MPT_DECLARE_FORMAT(STP), + MPT_DECLARE_FORMAT(MOD), + MPT_DECLARE_FORMAT(ICE), + MPT_DECLARE_FORMAT(669), + MPT_DECLARE_FORMAT(C67), + MPT_DECLARE_FORMAT(MO3), + MPT_DECLARE_FORMAT(M15), +}; + +#undef MPT_DECLARE_FORMAT + + CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize) { const uint64 availableFileSize = file.GetLength(); @@ -269,43 +320,13 @@ CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span*(format.loader))(file, loadFlags); + if(loaderSuccess) + break; + } + + if(!loaderSuccess) { m_nType = MOD_TYPE_NONE; m_ContainerType = MOD_CONTAINERTYPE_NONE; - if(loadFlags == onlyVerifyHeader) - { - return false; - } - } else + } + if(loadFlags == onlyVerifyHeader) { - if(loadFlags == onlyVerifyHeader) - { - return true; - } + return loaderSuccess; } if(packedContainerType != MOD_CONTAINERTYPE_NONE && m_ContainerType == MOD_CONTAINERTYPE_NONE) @@ -456,11 +438,6 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) m_ContainerType = packedContainerType; } - if(m_madeWithTracker.empty()) - { - m_madeWithTracker = ModTypeToTracker(GetType()); - } - #ifndef NO_ARCHIVE_SUPPORT // Read archive comment if there is no song comment if(m_songMessage.empty()) @@ -482,7 +459,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) { // New song InitializeGlobals(); - m_dwCreatedWithVersion = MptVersion::num; + m_dwCreatedWithVersion = Version::Current(); } // Adjust channels @@ -515,7 +492,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) { #ifndef MODPLUG_TRACKER // OpenMPT has its own way of reporting this error in CModDoc. - AddToLog(LogError, mpt::format(MPT_USTRING("Unable to load sample %1: %2"))(i, filename.ToUnicode())); + AddToLog(LogError, mpt::format(U_("Unable to load sample %1: %2"))(i, filename.ToUnicode())); #endif // MODPLUG_TRACKER } } else @@ -524,7 +501,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) } #endif // MPT_EXTERNAL_SAMPLES - if(sample.pSample) + if(sample.HasSampleData()) { sample.PrecomputeLoops(*this, false); } else if(!sample.uFlags[SMP_KEEPONDISK]) @@ -537,6 +514,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); } if(sample.nGlobalVol > 64) sample.nGlobalVol = 64; + if(sample.uFlags[CHN_ADLIB] && m_opl == nullptr) InitOPL(); } // Check invalid instruments INSTRUMENTINDEX maxInstr = 0; @@ -606,7 +584,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) #ifdef MODPLUG_TRACKER // Provide some visual feedback { - mpt::ustring s = mpt::format(MPT_USTRING("Loading Plugin FX%1: %2 (%3)"))( + mpt::ustring s = mpt::format(U_("Loading Plugin FX%1: %2 (%3)"))( mpt::ufmt::dec0<2>(plug + 1), mpt::ToUnicode(mpt::CharsetUTF8, plugin.Info.szLibraryName), mpt::ToUnicode(mpt::CharsetLocale, plugin.Info.szName)); @@ -631,7 +609,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) notFoundText.append(plugin.GetLibraryName()); notFoundText.append("\n"); #else - AddToLog(LogWarning, MPT_USTRING("Plugin not found: ") + mpt::ToUnicode(mpt::CharsetUTF8, plugin.GetLibraryName())); + AddToLog(LogWarning, U_("Plugin not found: ") + mpt::ToUnicode(mpt::CharsetUTF8, plugin.GetLibraryName())); #endif // MODPLUG_TRACKER } } @@ -651,7 +629,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) { notFoundText = "The following plugins have not been found:\n\n" + notFoundText + "\nDo you want to search for them online?"; } - if (Reporting::Confirm(mpt::ToWide(mpt::CharsetUTF8, notFoundText.c_str()), L"OpenMPT - Plugins missing", false, true) == cnfYes) + if(Reporting::Confirm(mpt::ToUnicode(mpt::CharsetUTF8, notFoundText), U_("OpenMPT - Plugins missing"), false, true) == cnfYes) { std::string url = "https://resources.openmpt.org/plugins/search.php?p="; for(const auto &id : notFoundIDs) @@ -700,7 +678,6 @@ bool CSoundFile::Destroy() m_songName.clear(); m_songArtist.clear(); m_songMessage.clear(); - m_madeWithTracker.clear(); m_FileHistory.clear(); for(auto &smp : Samples) @@ -931,6 +908,11 @@ void CSoundFile::ResetChannels() { chn.nROfs = chn.nLOfs = 0; chn.nLength = 0; + if(chn.dwFlags[CHN_ADLIB] && m_opl) + { + CHANNELINDEX c = static_cast(std::distance(std::begin(m_PlayState.Chn), &chn)); + m_opl->NoteCut(c); + } } } @@ -1056,6 +1038,10 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITInstrWithNoteOff); playBehaviour.set(kITMultiSampleInstrumentNumber); playBehaviour.set(kRowDelayWithNoteDelay); + if(type == MOD_TYPE_MPT) + { + playBehaviour.set(kOPLFlexibleNoteOff); + } break; case MOD_TYPE_XM: @@ -1095,6 +1081,8 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kFT2TremoloRampWaveform); playBehaviour.set(kFT2PortaUpDownMemory); + playBehaviour.set(kFT2PanSustainRelease); + playBehaviour.set(kFT2NoteDelayWithoutInstr); break; case MOD_TYPE_S3M: @@ -1108,6 +1096,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kST3VibratoMemory); playBehaviour.set(KST3PortaAfterArpeggio); playBehaviour.set(kRowDelayWithNoteDelay); + playBehaviour.set(kST3OffsetWithoutInstrument); break; case MOD_TYPE_MOD: @@ -1140,9 +1129,30 @@ PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type) playBehaviour.set(kHertzInLinearMode); playBehaviour.set(kPerChannelGlobalVolSlide); playBehaviour.set(kPanOverride); + playBehaviour.set(kITArpeggio); + playBehaviour.set(kITPortaMemoryShare); + playBehaviour.set(kITPatternLoopTargetReset); + playBehaviour.set(kITFT2PatternLoop); + playBehaviour.set(kITPingPongNoReset); + playBehaviour.set(kITClearOldNoteAfterCut); + playBehaviour.set(kITVibratoTremoloPanbrello); playBehaviour.set(kITMultiSampleBehaviour); + playBehaviour.set(kITPortaTargetReached); + playBehaviour.set(kITPatternLoopBreak); + playBehaviour.set(kITSwingBehaviour); + playBehaviour.set(kITSCxStopsSample); + playBehaviour.set(kITEnvelopePositionHandling); + playBehaviour.set(kITPingPongMode); + playBehaviour.set(kITRealNoteMapping); + playBehaviour.set(kITPortaNoNote); + playBehaviour.set(kITVolColMemory); + playBehaviour.set(kITFirstTickHandling); + playBehaviour.set(kITClearPortaTarget); playBehaviour.set(kITSampleAndHoldPanbrello); playBehaviour.set(kITPanbrelloHold); + playBehaviour.set(kITPanningReset); + playBehaviour.set(kITInstrWithNoteOff); + playBehaviour.set(kOPLFlexibleNoteOff); break; case MOD_TYPE_XM: @@ -1210,7 +1220,6 @@ MODTYPE CSoundFile::GetBestSaveFormat() const case MOD_TYPE_MTM: return MOD_TYPE_S3M; case MOD_TYPE_AMS: - case MOD_TYPE_AMS2: case MOD_TYPE_DMF: case MOD_TYPE_DBM: case MOD_TYPE_IMF: @@ -1288,6 +1297,12 @@ void CSoundFile::InitAmigaResampler() } +void CSoundFile::InitOPL() +{ + if(!m_opl) m_opl = mpt::make_unique(); +} + + // Detect samples that are referenced by an instrument, but actually not used in a song. // Only works in instrument mode. Unused samples are marked as false in the vector. SAMPLEINDEX CSoundFile::DetectUnusedSamples(std::vector &sampleUsed) const @@ -1344,7 +1359,7 @@ SAMPLEINDEX CSoundFile::DetectUnusedSamples(std::vector &sampleUsed) const } for (SAMPLEINDEX ichk = GetNumSamples(); ichk >= 1; ichk--) { - if ((!sampleUsed[ichk]) && (Samples[ichk].pSample)) unused++; + if ((!sampleUsed[ichk]) && (Samples[ichk].HasSampleData())) unused++; } return unused; @@ -1391,7 +1406,7 @@ bool CSoundFile::DestroySample(SAMPLEINDEX nSample) { return false; } - if(Samples[nSample].pSample == nullptr) + if(!Samples[nSample].HasSampleData()) { return true; } @@ -1411,6 +1426,7 @@ bool CSoundFile::DestroySample(SAMPLEINDEX nSample) sample.FreeSample(); sample.nLength = 0; sample.uFlags.reset(CHN_16BIT | CHN_STEREO); + sample.SetAdlib(false); #ifdef MODPLUG_TRACKER ResetSamplePath(nSample); @@ -1431,18 +1447,18 @@ CTuning* CSoundFile::CreateTuning12TET(const std::string &name) CTuning* pT = CTuning::CreateGeometric(name, 12, 2, 15); for(ModCommand::NOTE note = 0; note < 12; ++note) { - pT->SetNoteName(note, NoteNamesSharp[note]); + pT->SetNoteName(note, mpt::ToCharset(mpt::CharsetASCII, mpt::ustring(NoteNamesSharp[note]))); } return pT; } -std::string CSoundFile::GetNoteName(const ModCommand::NOTE note, const INSTRUMENTINDEX inst) const +mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const INSTRUMENTINDEX inst) const { // For MPTM instruments with custom tuning, find the appropriate note name. Else, use default note names. if(ModCommand::IsNote(note) && GetType() == MOD_TYPE_MPT && inst >= 1 && inst <= GetNumInstruments() && Instruments[inst] && Instruments[inst]->pTuning) { - return Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC); + return mpt::ToUnicode(GetCharsetInternal(), Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC)); } else { return GetNoteName(note); @@ -1450,30 +1466,32 @@ std::string CSoundFile::GetNoteName(const ModCommand::NOTE note, const INSTRUMEN } -std::string CSoundFile::GetNoteName(const ModCommand::NOTE note) const +mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note) const { return GetNoteName(note, m_NoteNames); } -std::string CSoundFile::GetNoteName(const ModCommand::NOTE note, const char (*noteNames)[4]) +mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const NoteName *noteNames) { if(ModCommand::IsSpecialNote(note)) { - const char specialNoteNames[][4] = { "PCs", "PC ", "~~~", "^^^", "===" }; + // cppcheck false-positive + // cppcheck-suppress constStatement + const MPT_UCHAR_TYPE specialNoteNames[][4] = { UL_("PCs"), UL_("PC "), UL_("~~~"), UL_("^^^"), UL_("===") }; STATIC_ASSERT(CountOf(specialNoteNames) == NOTE_MAX_SPECIAL - NOTE_MIN_SPECIAL + 1); return specialNoteNames[note - NOTE_MIN_SPECIAL]; } else if(ModCommand::IsNote(note)) { - return std::string() + return mpt::ustring() .append(noteNames[(note - NOTE_MIN) % 12]) - .append(1, '0' + (note - NOTE_MIN) / 12) + .append(1, UC_('0') + (note - NOTE_MIN) / 12) ; // e.g. "C#" + "5" } else if(note == NOTE_NONE) { - return "..."; + return UL_("..."); } - return "???"; + return UL_("???"); } @@ -1530,19 +1548,19 @@ void CSoundFile::SetType(MODTYPE type) #ifdef MODPLUG_TRACKER -void CSoundFile::ChangeModTypeTo(const MODTYPE& newType) +void CSoundFile::ChangeModTypeTo(const MODTYPE newType) { - const MODTYPE oldtype = GetType(); + const MODTYPE oldType = GetType(); m_nType = newType; SetModSpecsPointer(m_pModSpecs, m_nType); - if(oldtype == newType) + if(oldType == newType) return; SetupMODPanning(); // Setup LRRL panning scheme if needed // Only keep supported play behaviour flags - PlayBehaviourSet oldAllowedFlags = GetSupportedPlaybackBehaviour(oldtype); + PlayBehaviourSet oldAllowedFlags = GetSupportedPlaybackBehaviour(oldType); PlayBehaviourSet newAllowedFlags = GetSupportedPlaybackBehaviour(newType); PlayBehaviourSet newDefaultFlags = GetDefaultPlaybackBehaviour(newType); for(size_t i = 0; i < m_playBehaviour.size(); i++) @@ -1552,9 +1570,14 @@ void CSoundFile::ChangeModTypeTo(const MODTYPE& newType) // Set allowed flags to their defaults if they were not supported in the old format if(!oldAllowedFlags[i]) m_playBehaviour.set(i, newDefaultFlags[i]); } + // Special case for OPL behaviour when converting from S3M to MPTM to retain S3M-like note-off behaviour + if(oldType == MOD_TYPE_S3M && newType == MOD_TYPE_MPT && m_opl) + m_playBehaviour.reset(kOPLFlexibleNoteOff); - Order.OnModTypeChanged(oldtype); - Patterns.OnModTypeChanged(oldtype); + Order.OnModTypeChanged(oldType); + Patterns.OnModTypeChanged(oldType); + + m_modFormat.type = mpt::ToUnicode(mpt::CharsetUTF8, GetModSpecifications().fileExtension); } #endif // MODPLUG_TRACKER @@ -1704,9 +1727,13 @@ SAMPLEINDEX CSoundFile::GetNextFreeSample(INSTRUMENTINDEX targetInstrument, SAMP { for(SAMPLEINDEX i = start; i <= GetModSpecifications().samplesMax; i++) { + // Early exit for FM instruments + if(Samples[i].uFlags[CHN_ADLIB] && (targetInstrument == INSTRUMENTINDEX_INVALID || !IsSampleReferencedByInstrument(i, targetInstrument))) + continue; + // When loading into an instrument, ignore non-empty sample names. Else, only use this slot if the sample name is empty or we're in second pass. if((i > GetNumSamples() && passes == 1) - || (Samples[i].pSample == nullptr && (!m_szNames[i][0] || passes == 1 || targetInstrument != INSTRUMENTINDEX_INVALID)) + || (!Samples[i].HasSampleData() && (!m_szNames[i][0] || passes == 1 || targetInstrument != INSTRUMENTINDEX_INVALID)) || (targetInstrument != INSTRUMENTINDEX_INVALID && IsSampleReferencedByInstrument(i, targetInstrument))) // Not empty, but already used by this instrument. XXX this should only be done when replacing an instrument with a single sample! Otherwise it will use an inconsistent sample map! { // Empty slot, so it's a good candidate already. @@ -1831,12 +1858,12 @@ bool CSoundFile::LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &file // Copy over old attributes, but keep new sample data ModSample &sample = GetSample(smp); SmpLength newLength = sample.nLength; - void *newData = sample.pSample; + void *newData = sample.samplev(); SampleFlags newFlags = sample.uFlags; sample = origSample; sample.nLength = newLength; - sample.pSample = newData; + sample.pData.pSample = newData; sample.uFlags.set(CHN_16BIT, newFlags[CHN_16BIT]); sample.uFlags.set(CHN_STEREO, newFlags[CHN_STEREO]); sample.uFlags.reset(SMP_MODIFIED); @@ -1868,7 +1895,7 @@ void CSoundFile::SetupMODPanning(bool bForceSetup) } -void CSoundFile::PropagateXMAutoVibrato(INSTRUMENTINDEX ins, uint8 type, uint8 sweep, uint8 depth, uint8 rate) +void CSoundFile::PropagateXMAutoVibrato(INSTRUMENTINDEX ins, VibratoType type, uint8 sweep, uint8 depth, uint8 rate) { if(ins > m_nInstruments || Instruments[ins] == nullptr) return; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h index 176fa5673..e3f5f9568 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h @@ -11,10 +11,13 @@ #pragma once +#include "BuildSettings.h" + #include "SoundFilePlayConfig.h" #include "MixerSettings.h" #include "../common/misc_util.h" #include "../common/mptRandom.h" +#include "../common/version.h" #include #include #include @@ -61,7 +64,7 @@ OPENMPT_NAMESPACE_BEGIN // MODULAR ModInstrument FIELD ACCESS : body content in InstrumentExtensions.cpp // ----------------------------------------------------------------------------- #ifndef MODPLUG_NO_FILESAVE -extern void WriteInstrumentHeaderStructOrField(ModInstrument * input, FILE * file, uint32 only_this_code = -1 /* -1 for all */, uint16 fixedsize = 0); +extern void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code = -1 /* -1 for all */, uint16 fixedsize = 0); #endif // !MODPLUG_NO_FILESAVE extern bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, uint16 fsize, FileReader &file); // -------------------------------------------------------------------------------------------- @@ -84,9 +87,9 @@ typedef std::bitset PlayBehaviourSet; // For WAV export (writing pattern positions to file) struct PatternCuePoint { - uint64 offset; // offset in the file (in samples) - ORDERINDEX order; // which order is this? - bool processed; // has this point been processed by the main WAV render function yet? + uint64 offset; // offset in the file (in samples) + ORDERINDEX order; // which order is this? + bool processed; // has this point been processed by the main WAV render function yet? }; #endif // MODPLUG_TRACKER @@ -95,20 +98,14 @@ struct PatternCuePoint // Return values for GetLength() struct GetLengthType { - double duration; // total time in seconds - ROWINDEX lastRow; // last parsed row (if no target is specified, this is the first row that is parsed twice, i.e. not the *last* played order) - ROWINDEX endRow; // last row before module loops (UNDEFINED if a target is specified) - ROWINDEX startRow; // first row of parsed subsong - ORDERINDEX lastOrder; // last parsed order (see lastRow remark) - ORDERINDEX endOrder; // last order before module loops (UNDEFINED if a target is specified) - ORDERINDEX startOrder; // first order of parsed subsong - bool targetReached; // true if the specified order/row combination or duration has been reached while going through the module - - GetLengthType() - : duration(0.0) - , lastRow(ROWINDEX_INVALID), endRow(ROWINDEX_INVALID), startRow(0) - , lastOrder(ORDERINDEX_INVALID), endOrder(ORDERINDEX_INVALID), startOrder(0) - , targetReached(false) { } + double duration = 0.0; // Total time in seconds + ROWINDEX lastRow = ROWINDEX_INVALID; // Last parsed row (if no target is specified, this is the first row that is parsed twice, i.e. not the *last* played order) + ROWINDEX endRow = ROWINDEX_INVALID; // Last row before module loops (UNDEFINED if a target is specified) + ROWINDEX startRow = 0; // First row of parsed subsong + ORDERINDEX lastOrder = ORDERINDEX_INVALID; // Last parsed order (see lastRow remark) + ORDERINDEX endOrder = ORDERINDEX_INVALID; // Last order before module loops (UNDEFINED if a target is specified) + ORDERINDEX startOrder = 0; // First order of parsed subsong + bool targetReached = false; // True if the specified order/row combination or duration has been reached while going through the module }; @@ -133,10 +130,10 @@ struct GetLengthTarget enum Mode { - NoTarget, // Don't seek, i.e. return complete length of the first subsong. - GetAllSubsongs, // Same as NoTarget (i.e. get complete length), but returns the length of all sub songs - SeekPosition, // Seek to given pattern position. - SeekSeconds, // Seek to given time. + NoTarget, // Don't seek, i.e. return complete length of the first subsong. + GetAllSubsongs, // Same as NoTarget (i.e. get complete length), but returns the length of all sub songs + SeekPosition, // Seek to given pattern position. + SeekSeconds, // Seek to given time. } mode; // Don't seek, i.e. return complete module length. @@ -192,11 +189,11 @@ struct GetLengthTarget enum enmGetLengthResetMode { // Never adjust global variables / mod parameters - eNoAdjust = 0x00, + eNoAdjust = 0x00, // Mod parameters (such as global volume, speed, tempo, etc...) will always be memorized if the target was reached (i.e. they won't be reset to the previous values). If target couldn't be reached, they are reset to their default values. - eAdjust = 0x01, + eAdjust = 0x01, // Same as above, but global variables will only be memorized if the target could be reached. This does *NOT* influence the visited rows vector - it will *ALWAYS* be adjusted in this mode. - eAdjustOnSuccess = 0x02 | eAdjust, + eAdjustOnSuccess = 0x02 | eAdjust, // Same as previous option, but will also try to emulate sample playback so that voices from previous patterns will sound when continuing playback at the target position. eAdjustSamplePositions = 0x04 | eAdjustOnSuccess, }; @@ -215,6 +212,7 @@ class CTuningCollection; } // namespace Tuning typedef Tuning::CTuningCollection CTuningCollection; struct CModSpecifications; +class OPL; #ifdef MODPLUG_TRACKER class CModDoc; #endif // MODPLUG_TRACKER @@ -223,10 +221,11 @@ class CModDoc; ///////////////////////////////////////////////////////////////////////// // File edit history -#define HISTORY_TIMER_PRECISION 18.2f +#define HISTORY_TIMER_PRECISION 18.2 struct FileHistory { + FileHistory() : openTime(0) { MemsetZero(loadDate); } // Date when the file was loaded in the the tracker or created. tm loadDate; // Time the file was open in the editor, in 1/18.2th seconds (frequency of a standard DOS timer, to keep compatibility with Impulse Tracker easy). @@ -238,42 +237,70 @@ struct FileHistory struct TimingInfo { - double InputLatency; // seconds - double OutputLatency; // seconds - int64 StreamFrames; - uint64 SystemTimestamp; // nanoseconds - double Speed; - TimingInfo() - : InputLatency(0.0) - , OutputLatency(0.0) - , StreamFrames(0) - , SystemTimestamp(0) - , Speed(1.0) - { - return; - } + double InputLatency = 0.0; // seconds + double OutputLatency = 0.0; // seconds + int64 StreamFrames = 0; + uint64 SystemTimestamp = 0; // nanoseconds + double Speed = 1.0; +}; + + +struct ModFormatDetails +{ + mpt::ustring formatName; // "FastTracker 2" + mpt::ustring type; // "xm" + mpt::ustring madeWithTracker; // "OpenMPT 1.28.01.00" + mpt::ustring originalFormatName; // "FastTracker 2" in the case of converted formats like MO3 or GDM + mpt::ustring originalType; // "xm" in the case of converted formats like MO3 or GDM + mpt::Charset charset = mpt::CharsetUTF8; }; class IAudioReadTarget { protected: - virtual ~IAudioReadTarget() { } + virtual ~IAudioReadTarget() = default; public: - virtual void DataCallback(int *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; + virtual void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; }; -typedef char NoteName[4]; +class IAudioSource +{ +public: + virtual ~IAudioSource() = default; +public: + virtual void FillCallback(int32 * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) = 0; +}; + + +class AudioSourceNone + : public IAudioSource +{ +public: + void FillCallback(int32 * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) override + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + for(std::size_t frame = 0; frame < countChunk; ++frame) + { + MixSoundBuffers[channel][frame] = 0; + } + } + } +}; + + +typedef MPT_UCHAR_TYPE NoteName[4]; class CSoundFile { friend class GetLengthMemory; -public: //Misc +public: #ifdef MODPLUG_TRACKER - void ChangeModTypeTo(const MODTYPE& newType); + void ChangeModTypeTo(const MODTYPE newType); #endif // MODPLUG_TRACKER // Returns value in seconds. If given position won't be played at all, returns -1. @@ -287,20 +314,26 @@ public: static CTuning *GetDefaultTuning() {return nullptr;} CTuningCollection& GetTuneSpecificTunings() {return *m_pTuningsTuneSpecific;} - std::string GetNoteName(const ModCommand::NOTE note, const INSTRUMENTINDEX inst) const; - std::string GetNoteName(const ModCommand::NOTE note) const; - static std::string GetNoteName(const ModCommand::NOTE note, const NoteName *noteNames); + mpt::ustring GetNoteName(const ModCommand::NOTE note, const INSTRUMENTINDEX inst) const; + mpt::ustring GetNoteName(const ModCommand::NOTE note) const; + static mpt::ustring GetNoteName(const ModCommand::NOTE note, const NoteName *noteNames); #ifdef MODPLUG_TRACKER - static const NoteName *m_NoteNames; +public: static void SetDefaultNoteNames(); static const NoteName *GetDefaultNoteNames(); + static mpt::ustring GetDefaultNoteName(int note) // note = [0..11] + { + return m_NoteNames[note]; + } +private: + static const NoteName *m_NoteNames; #else +private: const NoteName *m_NoteNames; #endif private: - CTuningCollection* m_pTuningsTuneSpecific; - //<--Tuning + CTuningCollection* m_pTuningsTuneSpecific = nullptr; #ifdef MODPLUG_TRACKER public: @@ -323,8 +356,9 @@ private: mixsample_t MixRearBuffer[MIXBUFFERSIZE * 2]; // Non-interleaved plugin processing buffer float MixFloatBuffer[2][MIXBUFFERSIZE]; - mixsample_t gnDryLOfsVol; - mixsample_t gnDryROfsVol; + mixsample_t gnDryLOfsVol = 0; + mixsample_t gnDryROfsVol = 0; + mixsample_t MixInputBuffer[NUMMIXINPUTBUFFERS][MIXBUFFERSIZE]; public: MixerSettings m_MixerSettings; @@ -343,41 +377,44 @@ public: CAGC m_AGC; #endif - typedef uint32 samplecount_t; // Number of rendered samples + typedef uint32 samplecount_t; // Number of rendered samples public: // for Editing #ifdef MODPLUG_TRACKER - CModDoc *m_pModDoc; // Can be a null pointer for example when previewing samples from the treeview. + CModDoc *m_pModDoc = nullptr; // Can be a null pointer for example when previewing samples from the treeview. #endif // MODPLUG_TRACKER Enum m_nType; private: - MODCONTAINERTYPE m_ContainerType; + MODCONTAINERTYPE m_ContainerType = MOD_CONTAINERTYPE_NONE; public: - CHANNELINDEX m_nChannels; - SAMPLEINDEX m_nSamples; - INSTRUMENTINDEX m_nInstruments; + CHANNELINDEX m_nChannels = 0; + SAMPLEINDEX m_nSamples = 0; + INSTRUMENTINDEX m_nInstruments = 0; uint32 m_nDefaultSpeed, m_nDefaultGlobalVolume; TEMPO m_nDefaultTempo; FlagSet m_SongFlags; - CHANNELINDEX m_nMixChannels; + CHANNELINDEX m_nMixChannels = 0; private: CHANNELINDEX m_nMixStat; public: ROWINDEX m_nDefaultRowsPerBeat, m_nDefaultRowsPerMeasure; // default rows per beat and measure for this module - TempoMode m_nTempoMode; + TempoMode m_nTempoMode = tempoModeClassic; #ifdef MODPLUG_TRACKER // Lock playback between two rows. Lock is active if lock start != ROWINDEX_INVALID). - ROWINDEX m_lockRowStart, m_lockRowEnd; + ROWINDEX m_lockRowStart = ROWINDEX_INVALID, m_lockRowEnd = ROWINDEX_INVALID; // Lock playback between two orders. Lock is active if lock start != ORDERINDEX_INVALID). - ORDERINDEX m_lockOrderStart, m_lockOrderEnd; + ORDERINDEX m_lockOrderStart = ORDERINDEX_INVALID, m_lockOrderEnd = ORDERINDEX_INVALID; #endif // MODPLUG_TRACKER uint32 m_nSamplePreAmp, m_nVSTiVolume; + uint32 m_OPLVolumeFactor; // 16.16 + enum : uint32 { m_OPLVolumeFactorScale = (1 << 16) }; + bool IsGlobalVolumeUnset() const { return IsFirstTick(); } #ifndef MODPLUG_TRACKER - uint32 m_nFreqFactor; // Pitch shift factor (65536 = no pitch shifting). Only used in libopenmpt (openmpt::ext::interactive::set_pitch_factor) - uint32 m_nTempoFactor; // Tempo factor (65536 = no tempo adjustment). Only used in libopenmpt (openmpt::ext::interactive::set_tempo_factor) + uint32 m_nFreqFactor = 65536; // Pitch shift factor (65536 = no pitch shifting). Only used in libopenmpt (openmpt::ext::interactive::set_pitch_factor) + uint32 m_nTempoFactor = 65536; // Tempo factor (65536 = no tempo adjustment). Only used in libopenmpt (openmpt::ext::interactive::set_tempo_factor) #endif // Row swing factors for modern tempo mode @@ -388,12 +425,12 @@ public: // Periods in MPT are 4 times as fine as Amiga periods because of extra fine frequency slides (introduced in the S3M format). int32 m_nMinPeriod, m_nMaxPeriod; - ResamplingMode m_nResampling; // Resampling mode (if overriding the globally set resampling) - int32 m_nRepeatCount; // -1 means repeat infinitely. + ResamplingMode m_nResampling; // Resampling mode (if overriding the globally set resampling) + int32 m_nRepeatCount = 0; // -1 means repeat infinitely. ORDERINDEX m_nMaxOrderPosition; ModChannelSettings ChnSettings[MAX_BASECHANNELS]; // Initial channels settings - CPatternContainer Patterns; // Patterns - ModSequenceSet Order; // Modsequences. Order[x] returns an index of a pattern located at order x of the current sequence. + CPatternContainer Patterns; + ModSequenceSet Order; // Pattern sequences (order lists) protected: ModSample Samples[MAX_SAMPLES]; // Sample Headers public: @@ -404,8 +441,8 @@ public: #endif char m_szNames[MAX_SAMPLES][MAX_SAMPLENAME]; // Sample names - uint32 m_dwCreatedWithVersion; - uint32 m_dwLastSavedWithVersion; + Version m_dwCreatedWithVersion; + Version m_dwLastSavedWithVersion; PlayBehaviourSet m_playBehaviour; @@ -428,7 +465,7 @@ public: samplecount_t m_nBufferCount; double m_dBufferDiff; public: - samplecount_t m_lTotalSampleCount; + samplecount_t m_lTotalSampleCount = 0; public: uint32 m_nTickCount; @@ -437,8 +474,8 @@ public: public: uint32 m_nSamplesPerTick; ROWINDEX m_nCurrentRowsPerBeat, m_nCurrentRowsPerMeasure; // current rows per beat and measure for this module - uint32 m_nMusicSpeed; // Current speed - TEMPO m_nMusicTempo; // Current tempo + uint32 m_nMusicSpeed; // Current speed + TEMPO m_nMusicTempo; // Current tempo // Playback position ROWINDEX m_nRow; @@ -447,7 +484,7 @@ public: ROWINDEX m_nNextPatStartRow; // for FT2's E60 bug public: PATTERNINDEX m_nPattern; - ORDERINDEX m_nCurrentOrder, m_nNextOrder, m_nSeqOverride; + ORDERINDEX m_nCurrentOrder, m_nNextOrder, m_nSeqOverride = ORDERINDEX_INVALID; // Global volume public: @@ -458,17 +495,14 @@ public: int32 m_lHighResRampingGlobalVolume; public: - bool m_bPositionChanged; // Report to plugins that we jumped around in the module + bool m_bPositionChanged = true; // Report to plugins that we jumped around in the module public: - CHANNELINDEX ChnMix[MAX_CHANNELS]; // Channels to be mixed - ModChannel Chn[MAX_CHANNELS]; // Mixing channels... First m_nChannels channels are master channels (i.e. they are never NNA channels)! + CHANNELINDEX ChnMix[MAX_CHANNELS]; // Channels to be mixed + ModChannel Chn[MAX_CHANNELS]; // Mixing channels... First m_nChannels channels are master channels (i.e. they are never NNA channels)! public: PlayState() - : m_lTotalSampleCount(0) - , m_nSeqOverride(ORDERINDEX_INVALID) - , m_bPositionChanged(true) { std::fill(std::begin(Chn), std::end(Chn), ModChannel()); } @@ -484,9 +518,12 @@ public: #ifdef MODPLUG_TRACKER std::bitset m_bChannelMuteTogglePending; - std::vector m_PatternCuePoints; // For WAV export (writing pattern positions to file) + std::vector *m_PatternCuePoints = nullptr; // For WAV export (writing pattern positions to file) + std::vector *m_SamplePlayLengths = nullptr; // For storing the maximum play length of each sample for automatic sample trimming #endif // MODPLUG_TRACKER + std::unique_ptr m_opl; + public: #ifdef LIBOPENMPT_BUILD #ifndef NO_PLUGINS @@ -495,13 +532,10 @@ public: #endif public: - std::string m_songName; mpt::ustring m_songArtist; - - // Song message SongMessage m_songMessage; - mpt::ustring m_madeWithTracker; + ModFormatDetails m_modFormat; protected: std::vector m_FileHistory; // File edit history @@ -519,17 +553,17 @@ public: void ResetSamplePath(SAMPLEINDEX smp) { if(m_samplePaths.size() >= smp) m_samplePaths[smp - 1] = mpt::PathString(); Samples[smp].uFlags.reset(SMP_KEEPONDISK | SMP_MODIFIED);} mpt::PathString GetSamplePath(SAMPLEINDEX smp) const { if(m_samplePaths.size() >= smp) return m_samplePaths[smp - 1]; else return mpt::PathString(); } bool SampleHasPath(SAMPLEINDEX smp) const { if(m_samplePaths.size() >= smp) return !m_samplePaths[smp - 1].empty(); else return false; } - bool IsExternalSampleMissing(SAMPLEINDEX smp) const { return Samples[smp].uFlags[SMP_KEEPONDISK] && Samples[smp].pSample == nullptr; } + bool IsExternalSampleMissing(SAMPLEINDEX smp) const { return Samples[smp].uFlags[SMP_KEEPONDISK] && !Samples[smp].HasSampleData(); } bool LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &filename); #endif // MPT_EXTERNAL_SAMPLES - bool m_bIsRendering; + bool m_bIsRendering = false; TimingInfo m_TimingInfo; // only valid if !m_bIsRendering private: // logging - ILog *m_pCustomLog; + ILog *m_pCustomLog = nullptr; public: CSoundFile(); @@ -546,10 +580,10 @@ public: enum ModLoadingFlags { onlyVerifyHeader = 0x00, - loadPatternData = 0x01, // If unset, advise loaders to not process any pattern data (if possible) - loadSampleData = 0x02, // If unset, advise loaders to not process any sample data (if possible) - loadPluginData = 0x04, // If unset, plugin data is not loaded (and as a consequence, plugins are not instanciated). - loadPluginInstance = 0x08, // If unset, plugins are not instanciated. + loadPatternData = 0x01, // If unset, advise loaders to not process any pattern data (if possible) + loadSampleData = 0x02, // If unset, advise loaders to not process any sample data (if possible) + loadPluginData = 0x04, // If unset, plugin data is not loaded (and as a consequence, plugins are not instanciated). + loadPluginInstance = 0x08, // If unset, plugins are not instanciated. skipContainer = 0x10, skipModules = 0x20, @@ -587,7 +621,7 @@ public: #ifdef MODPLUG_TRACKER // Get parent CModDoc. Can be nullptr if previewing from tree view, and is always nullptr if we're not actually compiling OpenMPT. - CModDoc *GetpModDoc() const { return m_pModDoc; } + CModDoc *GetpModDoc() const noexcept { return m_pModDoc; } bool Create(FileReader file, ModLoadingFlags loadFlags = loadCompleteModule, CModDoc *pModDoc = nullptr); #else @@ -595,14 +629,14 @@ public: #endif // MODPLUG_TRACKER bool Destroy(); - Enum GetType() const { return m_nType; } + Enum GetType() const noexcept { return m_nType; } - MODCONTAINERTYPE GetContainerType() const { return m_ContainerType; } + MODCONTAINERTYPE GetContainerType() const noexcept { return m_ContainerType; } // rough heuristic, could be improved mpt::Charset GetCharsetFile() const // 8bit string encoding of strings in the on-disk file { - return GetCharsetFromModType(GetType()); + return m_modFormat.charset; } mpt::Charset GetCharsetInternal() const // 8bit string encoding of strings internal in CSoundFile { @@ -651,14 +685,10 @@ public: TEMPO GetMusicTempo() const { return m_PlayState.m_nMusicTempo; } bool IsFirstTick() const { return (m_PlayState.m_lTotalSampleCount == 0); } - //Get modlength in various cases: total length, length to - //specific order&row etc. Return value is in seconds. + // Get song duration in various cases: total length, length to specific order & row, etc. std::vector GetLength(enmGetLengthResetMode adjustMode, GetLengthTarget target = GetLengthTarget()); public: - //Returns song length in seconds. - double GetSongTime() { return GetLength(eNoAdjust).back().duration; } - void RecalculateSamplesPerTick(); double GetRowDuration(TEMPO tempo, uint32 speed) const; uint32 GetTickDuration(PlayState &playState) const; @@ -666,12 +696,16 @@ public: // A repeat count value of -1 means infinite loop void SetRepeatCount(int n) { m_nRepeatCount = n; } int GetRepeatCount() const { return m_nRepeatCount; } - bool IsPaused() const { return m_SongFlags[SONG_PAUSED | SONG_STEP]; } // Added SONG_STEP as it seems to be desirable in most cases to check for this as well. + bool IsPaused() const { return m_SongFlags[SONG_PAUSED | SONG_STEP]; } // Added SONG_STEP as it seems to be desirable in most cases to check for this as well. void LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow = 0); bool InitChannel(CHANNELINDEX nChn); void InitAmigaResampler(); + void InitOPL(); + static constexpr bool SupportsOPL(MODTYPE type) { return type & (MOD_TYPE_S3M | MOD_TYPE_MPT); } + bool SupportsOPL() const noexcept { return SupportsOPL(m_nType); } + static ProbeResult ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderUMX(MemoryFileReader file, const uint64 *pfilesize); @@ -683,6 +717,7 @@ public: static ProbeResult ProbeFileHeaderAMF_DSMI(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderC67(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDTM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize); @@ -715,6 +750,10 @@ public: static ProbeResult ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderXM(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderMID(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderUAX(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderWAV(MemoryFileReader file, const uint64 *pfilesize); + // Module Loaders bool Read669(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -722,6 +761,7 @@ public: bool ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAMS(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadAMS2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadC67(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDBM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadDIGI(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -732,13 +772,13 @@ public: bool ReadICE(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadIMF(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadIT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadITProject(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadITP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadJ2B(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadM15(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMDL(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadMed(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadMED(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMO3(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadMod(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadMOD(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMT2(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadOKT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); @@ -751,33 +791,31 @@ public: bool ReadSFX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadUlt(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadULT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadXM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadUAX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); - bool ReadWav(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadWAV(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); static std::vector GetSupportedExtensions(bool otherFormats); static bool IsExtensionSupported(const char *ext); // UTF8, casing of ext is ignored - static mpt::Charset GetCharsetFromModType(MODTYPE modtype); - static mpt::ustring ModTypeToString(MODTYPE modtype); static mpt::ustring ModContainerTypeToString(MODCONTAINERTYPE containertype); - static mpt::ustring ModTypeToTracker(MODTYPE modtype); static mpt::ustring ModContainerTypeToTracker(MODCONTAINERTYPE containertype); + // Repair non-standard stuff in modules saved with previous ModPlug versions void UpgradeModule(); // Save Functions #ifndef MODPLUG_NO_FILESAVE - bool SaveXM(const mpt::PathString &filename, bool compatibilityExport = false); - bool SaveS3M(const mpt::PathString &filename) const; - bool SaveMod(const mpt::PathString &filename) const; - bool SaveIT(const mpt::PathString &filename, bool compatibilityExport = false); - uint32 SaveMixPlugins(FILE *f=NULL, bool bUpdate=true); - void WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 size, FILE* f, INSTRUMENTINDEX nInstruments) const; - void SaveExtendedInstrumentProperties(INSTRUMENTINDEX nInstruments, FILE* f) const; - void SaveExtendedSongProperties(FILE* f) const; + bool SaveXM(std::ostream &f, bool compatibilityExport = false); + bool SaveS3M(std::ostream &f) const; + bool SaveMod(std::ostream &f) const; + bool SaveIT(std::ostream &f, const mpt::PathString &filename, bool compatibilityExport = false); + uint32 SaveMixPlugins(std::ostream *file=nullptr, bool bUpdate=true); + void WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX nInstruments) const; + void SaveExtendedInstrumentProperties(INSTRUMENTINDEX nInstruments, std::ostream &f) const; + void SaveExtendedSongProperties(std::ostream &f) const; #endif // MODPLUG_NO_FILESAVE void LoadExtendedSongProperties(FileReader &file, bool ignoreChannelCount, bool* pInterpretMptMade = nullptr); void LoadMPTMProperties(FileReader &file, uint16 cwtv); @@ -785,9 +823,8 @@ public: mpt::ustring GetSchismTrackerVersion(uint16 cwtv); // Reads extended instrument properties(XM/IT/MPTM). - // If no errors occur and song extension tag is found, returns pointer to the beginning - // of the tag, else returns NULL. - void LoadExtendedInstrumentProperties(FileReader &file, bool *pInterpretMptMade = nullptr); + // Returns true if extended instrument properties were found. + bool LoadExtendedInstrumentProperties(FileReader &file); void SetDefaultPlaybackBehaviour(MODTYPE type); static PlayBehaviourSet GetSupportedPlaybackBehaviour(MODTYPE type); @@ -811,14 +848,16 @@ public: void StopAllVsti(); void RecalculateGainForAllPlugs(); void ResetChannels(); - samplecount_t Read(samplecount_t count, IAudioReadTarget &target); + samplecount_t Read(samplecount_t count, IAudioReadTarget &target) { AudioSourceNone source; return Read(count, target, source); } + samplecount_t Read(samplecount_t count, IAudioReadTarget &target, IAudioSource &source); private: void CreateStereoMix(int count); public: bool FadeSong(uint32 msec); private: - void ProcessDSP(std::size_t countChunk); + void ProcessDSP(uint32 countChunk); void ProcessPlugins(uint32 nCount); + void ProcessInputChannels(IAudioSource &source, std::size_t countChunk); public: samplecount_t GetTotalSampleCount() const { return m_PlayState.m_lTotalSampleCount; } bool HasPositionChanged() { bool b = m_PlayState.m_bPositionChanged; m_PlayState.m_bPositionChanged = false; return b; } @@ -842,13 +881,13 @@ public: bool ProcessEffects(); CHANNELINDEX GetNNAChannel(CHANNELINDEX nChn) const; CHANNELINDEX CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, bool forceCut); - void NoteChange(ModChannel *pChn, int note, bool bPorta = false, bool bResetEnv = true, bool bManual = false) const; - void InstrumentChange(ModChannel *pChn, uint32 instr, bool bPorta = false, bool bUpdVol = true, bool bResetEnv = true) const; - void ApplyInstrumentPanning(ModChannel *pChn, const ModInstrument *instr, const ModSample *smp) const; + void NoteChange(ModChannel &chn, int note, bool bPorta = false, bool bResetEnv = true, bool bManual = false, CHANNELINDEX channelHint = CHANNELINDEX_INVALID) const; + void InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta = false, bool bUpdVol = true, bool bResetEnv = true) const; + void ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *instr, const ModSample *smp) const; uint32 CalculateXParam(PATTERNINDEX pat, ROWINDEX row, CHANNELINDEX chn, bool *isExtended = nullptr) const; // Channel Effects - void KeyOff(ModChannel *pChn) const; + void KeyOff(ModChannel &chn) const; // Global Effects void SetTempo(TEMPO param, bool setAsNonModcommand = false); void SetSpeed(PlayState &playState, uint32 param) const; @@ -863,31 +902,31 @@ protected: // Channel effect processing int GetVibratoDelta(int type, int position) const; - void ProcessVolumeSwing(ModChannel *pChn, int &vol) const; - void ProcessPanningSwing(ModChannel *pChn) const; - void ProcessTremolo(ModChannel *pChn, int &vol) const; + void ProcessVolumeSwing(ModChannel &chn, int &vol) const; + void ProcessPanningSwing(ModChannel &chn) const; + void ProcessTremolo(ModChannel &chn, int &vol) const; void ProcessTremor(CHANNELINDEX nChn, int &vol); - bool IsEnvelopeProcessed(const ModChannel *pChn, EnvelopeType env) const; - void ProcessVolumeEnvelope(ModChannel *pChn, int &vol) const; - void ProcessPanningEnvelope(ModChannel *pChn) const; - void ProcessPitchFilterEnvelope(ModChannel *pChn, int &period) const; + bool IsEnvelopeProcessed(const ModChannel &chn, EnvelopeType env) const; + void ProcessVolumeEnvelope(ModChannel &chn, int &vol) const; + void ProcessPanningEnvelope(ModChannel &chn) const; + int ProcessPitchFilterEnvelope(ModChannel &chn, int &period) const; - void IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envType) const; - void IncrementEnvelopePositions(ModChannel *pChn) const; + void IncrementEnvelopePosition(ModChannel &chn, EnvelopeType envType) const; + void IncrementEnvelopePositions(ModChannel &chn) const; - void ProcessInstrumentFade(ModChannel *pChn, int &vol) const; + void ProcessInstrumentFade(ModChannel &chn, int &vol) const; - void ProcessPitchPanSeparation(ModChannel *pChn) const; - void ProcessPanbrello(ModChannel *pChn) const; + void ProcessPitchPanSeparation(ModChannel &chn) const; + void ProcessPanbrello(ModChannel &chn) const; void ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEINDEXTYPE &arpeggioSteps); void ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYPE &vibratoFactor); - void ProcessSampleAutoVibrato(ModChannel *pChn, int &period, Tuning::RATIOTYPE &vibratoFactor, int &nPeriodFrac) const; + void ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning::RATIOTYPE &vibratoFactor, int &nPeriodFrac) const; - void ProcessRamping(ModChannel *pChn) const; + void ProcessRamping(ModChannel &chn) const; - SamplePosition GetChannelIncrement(ModChannel *pChn, uint32 period, int periodFrac) const; + SamplePosition GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const; protected: // Type of panning command @@ -898,38 +937,38 @@ protected: Pan8bit = 8, }; // Channel Effects - void UpdateS3MEffectMemory(ModChannel *pChn, ModCommand::PARAM param) const; + void UpdateS3MEffectMemory(ModChannel &chn, ModCommand::PARAM param) const; void PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular = false); void PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular = false); void MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides); - void FinePortamentoUp(ModChannel *pChn, ModCommand::PARAM param) const; - void FinePortamentoDown(ModChannel *pChn, ModCommand::PARAM param) const; - void ExtraFinePortamentoUp(ModChannel *pChn, ModCommand::PARAM param) const; - void ExtraFinePortamentoDown(ModChannel *pChn, ModCommand::PARAM param) const; - void PortamentoMPT(ModChannel*, int); - void PortamentoFineMPT(ModChannel*, int); - void PortamentoExtraFineMPT(ModChannel*, int); - void NoteSlide(ModChannel *pChn, uint32 param, bool slideUp, bool retrig) const; - void TonePortamento(ModChannel *pChn, uint32 param) const; - void Vibrato(ModChannel *pChn, uint32 param) const; - void FineVibrato(ModChannel *pChn, uint32 param) const; - void VolumeSlide(ModChannel *pChn, ModCommand::PARAM param); - void PanningSlide(ModChannel *pChn, ModCommand::PARAM param, bool memory = true); - void ChannelVolSlide(ModChannel *pChn, ModCommand::PARAM param) const; - void FineVolumeUp(ModChannel *pChn, ModCommand::PARAM param, bool volCol) const; - void FineVolumeDown(ModChannel *pChn, ModCommand::PARAM param, bool volCol) const; - void Tremolo(ModChannel *pChn, uint32 param) const; - void Panbrello(ModChannel *pChn, uint32 param) const; - void Panning(ModChannel *pChn, uint32 param, PanningType panBits) const; + void FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const; + void FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const; + void ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const; + void ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const; + void PortamentoMPT(ModChannel &chn, int); + void PortamentoFineMPT(ModChannel &chn, int); + void PortamentoExtraFineMPT(ModChannel &chn, int); + void NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const; + void TonePortamento(ModChannel &chn, uint32 param) const; + void Vibrato(ModChannel &chn, uint32 param) const; + void FineVibrato(ModChannel &chn, uint32 param) const; + void VolumeSlide(ModChannel &chn, ModCommand::PARAM param); + void PanningSlide(ModChannel &chn, ModCommand::PARAM param, bool memory = true); + void ChannelVolSlide(ModChannel &chn, ModCommand::PARAM param) const; + void FineVolumeUp(ModChannel &chn, ModCommand::PARAM param, bool volCol) const; + void FineVolumeDown(ModChannel &chn, ModCommand::PARAM param, bool volCol) const; + void Tremolo(ModChannel &chn, uint32 param) const; + void Panbrello(ModChannel &chn, uint32 param) const; + void Panning(ModChannel &chn, uint32 param, PanningType panBits) const; void RetrigNote(CHANNELINDEX nChn, int param, int offset = 0); void SampleOffset(ModChannel &chn, SmpLength param) const; void ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) const; void NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample); - ROWINDEX PatternLoop(ModChannel *, uint32 param); + ROWINDEX PatternLoop(ModChannel &chn, uint32 param); void ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param); void ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param); - void ExtendedChannelEffect(ModChannel *, uint32 param); - void InvertLoop(ModChannel* pChn); + void ExtendedChannelEffect(ModChannel &chn, uint32 param); + void InvertLoop(ModChannel &chn); ROWINDEX PatternBreak(PlayState &state, CHANNELINDEX chn, uint8 param) const; void GlobalVolSlide(ModCommand::PARAM param, uint8 &nOldGlobalVolSlide); @@ -939,17 +978,17 @@ protected: uint32 SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned char *macro, uint32 macroLen, PLUGINDEX plugin); void SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume); - void SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modifier = 256) const; + int SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier = 256) const; // Low-Level effect processing - void DoFreqSlide(ModChannel *pChn, int32 nFreqSlide) const; + void DoFreqSlide(ModChannel &chn, int32 nFreqSlide) const; void UpdateTimeSignature(); public: // Convert frequency to IT cutoff (0...127) uint8 FrequencyToCutOff(double frequency) const; // Convert IT cutoff (0...127 + modifier) to frequency - uint32 CutOffToFrequency(uint32 nCutOff, int flt_modifier = 256) const; // [0-127] => [1-10KHz] + uint32 CutOffToFrequency(uint32 nCutOff, int envModifier = 256) const; // [0-127] => [1-10KHz] // Returns true if periods are actually plain frequency values in Hz. bool PeriodsAreFrequencies() const @@ -992,13 +1031,17 @@ public: SAMPLEINDEX RemoveSelectedSamples(const std::vector &keepSamples); // Set the autovibrato settings for all samples associated to the given instrument. - void PropagateXMAutoVibrato(INSTRUMENTINDEX ins, uint8 type, uint8 sweep, uint8 depth, uint8 rate); + void PropagateXMAutoVibrato(INSTRUMENTINDEX ins, VibratoType type, uint8 sweep, uint8 depth, uint8 rate); // Samples file I/O bool ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false, bool includeInstrumentFormats = true); bool ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false, FileReader *wsmpChunk = nullptr); +protected: + bool ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadPATSample(SAMPLEINDEX nSample, FileReader &file); bool ReadS3ISample(SAMPLEINDEX nSample, FileReader &file); + bool ReadSBISample(SAMPLEINDEX sample, FileReader &file); + bool ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize = false); bool ReadXISample(SAMPLEINDEX nSample, FileReader &file); @@ -1008,15 +1051,17 @@ public: bool ReadFLACSample(SAMPLEINDEX sample, FileReader &file); bool ReadOpusSample(SAMPLEINDEX sample, FileReader &file); bool ReadVorbisSample(SAMPLEINDEX sample, FileReader &file); - bool ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool mo3Decode = false); - bool ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, bool mo3Decode = false); + bool ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw = false, bool mo3Decode = false); // raw: ignore all encoder-/decodr-delays, decode just raw frames ; mod3Decode: skip metadata and loop-precompute + bool ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, bool mo3Decode = false); // mod3Decode: skip metadata and loop-precompute +public: #ifdef MODPLUG_TRACKER static std::vector GetMediaFoundationFileTypes(); #endif // MODPLUG_TRACKER #ifndef MODPLUG_NO_FILESAVE - bool SaveWAVSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const; - bool SaveRAWSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const; - bool SaveFLACSample(SAMPLEINDEX nSample, const mpt::PathString &filename) const; + bool SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const; + bool SaveRAWSample(SAMPLEINDEX nSample, std::ostream &f) const; + bool SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const; + bool SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const; #endif static bool CanReadMP3(); static bool CanReadVorbis(); @@ -1024,14 +1069,17 @@ public: // Instrument file I/O bool ReadInstrumentFromFile(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize=false); + bool ReadSampleAsInstrument(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize=false); +protected: bool ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file); bool ReadITIInstrument(INSTRUMENTINDEX nInstr, FileReader &file); bool ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file); bool ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file); - bool ReadSampleAsInstrument(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize=false); +public: #ifndef MODPLUG_NO_FILESAVE - bool SaveXIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString &filename) const; - bool SaveITIInstrument(INSTRUMENTINDEX nInstr, const mpt::PathString &filename, bool compress, bool allowExternal) const; + bool SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const; + bool SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool compress, bool allowExternal) const; + bool SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const; #endif // I/O from another sound file @@ -1048,7 +1096,7 @@ public: uint32 MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChannel, uint8 note, bool isXG, std::bitset<16> drumChns); size_t ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers); - void LoadMixPlugins(FileReader &file); + bool LoadMixPlugins(FileReader &file); #ifndef NO_PLUGINS static void ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin); void ProcessMidiOut(CHANNELINDEX nChn); @@ -1085,11 +1133,10 @@ inline IMixPlugin* CSoundFile::GetInstrumentPlugin(INSTRUMENTINDEX instr) /////////////////////////////////////////////////////////// // Low-level Mixing functions -#define SCRATCH_BUFFER_SIZE 64 //Used for plug's final processing (cleanup) #define FADESONGDELAY 100 -#define MOD2XMFineTune(k) ((int)( (signed char)((k)<<4) )) -#define XM2MODFineTune(k) ((int)( (k>>4)&0x0f )) +static MPT_CONSTEXPR11_FUN int8 MOD2XMFineTune(int v) { return static_cast(static_cast(v) << 4); } +static MPT_CONSTEXPR11_FUN int8 XM2MODFineTune(int v) { return static_cast(static_cast(v) >> 4); } // Read instrument property with 'code' and 'size' from 'file' to instrument 'pIns'. void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const uint16 size, FileReader &file); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp index 848ca1792..7e29aee8b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp @@ -22,6 +22,7 @@ #ifndef NO_PLUGINS #include "plugins/PlugInterface.h" #endif // NO_PLUGINS +#include "OPL.h" OPENMPT_NAMESPACE_BEGIN @@ -106,6 +107,10 @@ void CSoundFile::InitPlayer(bool bReset) #ifndef NO_AGC m_AGC.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif + if(m_opl) + { + m_opl->Initialize(m_MixerSettings.gdwMixingFreq); + } } @@ -183,7 +188,22 @@ static void ApplyStereoSeparation(mixsample_t *SoundFrontBuffer, mixsample_t *So } -CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget &target) +void CSoundFile::ProcessInputChannels(IAudioSource &source, std::size_t countChunk) +{ + for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel) + { + std::fill(&(MixInputBuffer[channel][0]), &(MixInputBuffer[channel][countChunk]), 0); + } + mixsample_t * buffers[NUMMIXINPUTBUFFERS]; + for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel) + { + buffers[channel] = MixInputBuffer[channel]; + } + source.FillCallback(buffers, m_MixerSettings.NumInputChannels, countChunk); +} + + +CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget &target, IAudioSource &source) { MPT_ASSERT_ALWAYS(m_MixerSettings.IsValid()); @@ -207,27 +227,31 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget // Update Channel Data if(!m_PlayState.m_nBufferCount) - { // last tick or fade completely processed, find out what to do next + { + // Last tick or fade completely processed, find out what to do next if(m_SongFlags[SONG_FADINGSONG]) - { // song was faded out + { + // Song was faded out m_SongFlags.set(SONG_ENDREACHED); } else if(ReadNote()) - { // render next tick (normal progress) + { + // Render next tick (normal progress) MPT_ASSERT(m_PlayState.m_nBufferCount > 0); #ifdef MODPLUG_TRACKER // Save pattern cue points for WAV rendering here (if we reached a new pattern, that is.) - if(IsRenderingToDisc() && (m_PatternCuePoints.empty() || m_PlayState.m_nCurrentOrder != m_PatternCuePoints.back().order)) + if(m_PatternCuePoints != nullptr && (m_PatternCuePoints->empty() || m_PlayState.m_nCurrentOrder != m_PatternCuePoints->back().order)) { PatternCuePoint cue; cue.offset = countRendered; cue.order = m_PlayState.m_nCurrentOrder; cue.processed = false; // We don't know the base offset in the file here. It has to be added in the main conversion loop. - m_PatternCuePoints.push_back(cue); + m_PatternCuePoints->push_back(cue); } #endif } else - { // no new pattern data + { + // No new pattern data #ifdef MODPLUG_TRACKER if((m_nMaxOrderPosition) && (m_PlayState.m_nCurrentOrder >= m_nMaxOrderPosition)) { @@ -235,7 +259,8 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget } #endif // MODPLUG_TRACKER if(IsRenderingToDisc()) - { // rewbs: disable song fade when rendering. + { + // Disable song fade when rendering or when requested in libopenmpt. m_SongFlags.set(SONG_ENDREACHED); } else { // end of song reached, fade it out @@ -254,15 +279,31 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget if(m_SongFlags[SONG_ENDREACHED]) { - break; // mix done + // Mix done. + + // If we decide to continue the mix (possible in libopenmpt), the tick count + // is valid right now (0), meaning that no new row data will be processed. + // This would effectively prolong the last played row. + m_PlayState.m_nTickCount = GetNumTicksOnCurrentRow(); + break; } MPT_ASSERT(m_PlayState.m_nBufferCount > 0); // assert that we have actually something to do - const samplecount_t countChunk = std::min(MIXBUFFERSIZE, std::min(m_PlayState.m_nBufferCount, countToRender)); + const samplecount_t countChunk = std::min({ MIXBUFFERSIZE, m_PlayState.m_nBufferCount, countToRender }); + + if(m_MixerSettings.NumInputChannels > 0) + { + ProcessInputChannels(source, countChunk); + } CreateStereoMix(countChunk); + if(m_opl) + { + m_opl->Mix(MixSoundBuffer, countChunk, m_OPLVolumeFactor * m_nVSTiVolume / 48); + } + #ifndef NO_REVERB m_Reverb.Process(MixSoundBuffer, countChunk); #endif // NO_REVERB @@ -329,7 +370,7 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget } -void CSoundFile::ProcessDSP(std::size_t countChunk) +void CSoundFile::ProcessDSP(uint32 countChunk) { #ifndef NO_DSP if(m_MixerSettings.DSPMask & SNDDSP_SURROUND) @@ -763,45 +804,45 @@ int CSoundFile::GetVibratoDelta(int type, int position) const } -void CSoundFile::ProcessVolumeSwing(ModChannel *pChn, int &vol) const +void CSoundFile::ProcessVolumeSwing(ModChannel &chn, int &vol) const { if(m_playBehaviour[kITSwingBehaviour]) { - vol += pChn->nVolSwing; + vol += chn.nVolSwing; Limit(vol, 0, 64); } else if(m_playBehaviour[kMPTOldSwingBehaviour]) { - vol += pChn->nVolSwing; + vol += chn.nVolSwing; Limit(vol, 0, 256); } else { - pChn->nVolume += pChn->nVolSwing; - Limit(pChn->nVolume, 0, 256); - vol = pChn->nVolume; - pChn->nVolSwing = 0; + chn.nVolume += chn.nVolSwing; + Limit(chn.nVolume, 0, 256); + vol = chn.nVolume; + chn.nVolSwing = 0; } } -void CSoundFile::ProcessPanningSwing(ModChannel *pChn) const +void CSoundFile::ProcessPanningSwing(ModChannel &chn) const { if(m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) { - pChn->nRealPan = pChn->nPan + pChn->nPanSwing; - Limit(pChn->nRealPan, 0, 256); + chn.nRealPan = chn.nPan + chn.nPanSwing; + Limit(chn.nRealPan, 0, 256); } else { - pChn->nPan += pChn->nPanSwing; - Limit(pChn->nPan, 0, 256); - pChn->nPanSwing = 0; - pChn->nRealPan = pChn->nPan; + chn.nPan += chn.nPanSwing; + Limit(chn.nPan, 0, 256); + chn.nPanSwing = 0; + chn.nRealPan = chn.nPan; } } -void CSoundFile::ProcessTremolo(ModChannel *pChn, int &vol) const +void CSoundFile::ProcessTremolo(ModChannel &chn, int &vol) const { - if (pChn->dwFlags[CHN_TREMOLO]) + if (chn.dwFlags[CHN_TREMOLO]) { if(m_SongFlags.test_all(SONG_FIRSTTICK | SONG_PT_MODE)) { @@ -816,39 +857,39 @@ void CSoundFile::ProcessTremolo(ModChannel *pChn, int &vol) const // IT compatibility: We don't need a different attenuation here because of the different tables we're going to use const uint8 attenuation = ((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) || m_playBehaviour[kITVibratoTremoloPanbrello]) ? 5 : 6; - int delta = GetVibratoDelta(pChn->nTremoloType, pChn->nTremoloPos); - if((pChn->nTremoloType & 0x03) == 1 && m_playBehaviour[kFT2TremoloRampWaveform]) + int delta = GetVibratoDelta(chn.nTremoloType, chn.nTremoloPos); + if((chn.nTremoloType & 0x03) == 1 && m_playBehaviour[kFT2TremoloRampWaveform]) { // FT2 compatibility: Tremolo ramp down / triangle implementation is weird and affected by vibrato position (copypaste bug) // Test case: TremoloWaveforms.xm, TremoloVibrato.xm - uint8 ramp = (pChn->nTremoloPos * 4u) & 0x7F; + uint8 ramp = (chn.nTremoloPos * 4u) & 0x7F; // Volume-colum vibrato gets executed first in FT2, so we may need to advance the vibrato position first - uint32 vibPos = pChn->nVibratoPos; - if(!m_SongFlags[SONG_FIRSTTICK] && pChn->dwFlags[CHN_VIBRATO]) - vibPos += pChn->nVibratoSpeed; + uint32 vibPos = chn.nVibratoPos; + if(!m_SongFlags[SONG_FIRSTTICK] && chn.dwFlags[CHN_VIBRATO]) + vibPos += chn.nVibratoSpeed; if((vibPos & 0x3F) >= 32) ramp ^= 0x7F; - if((pChn->nTremoloPos & 0x3F) >= 32) + if((chn.nTremoloPos & 0x3F) >= 32) delta = -ramp; else delta = ramp; } if(GetType() != MOD_TYPE_DMF) { - vol += (delta * pChn->nTremoloDepth) / (1 << attenuation); + vol += (delta * chn.nTremoloDepth) / (1 << attenuation); } else { // Tremolo in DMF always attenuates by a percentage of the current note volume - vol -= (vol * pChn->nTremoloDepth * (64 - delta)) / (128 * 64); + vol -= (vol * chn.nTremoloDepth * (64 - delta)) / (128 * 64); } } if(!m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS])) { // IT compatibility: IT has its own, more precise tables if(m_playBehaviour[kITVibratoTremoloPanbrello]) - pChn->nTremoloPos += 4 * pChn->nTremoloSpeed; + chn.nTremoloPos += 4 * chn.nTremoloSpeed; else - pChn->nTremoloPos += pChn->nTremoloSpeed; + chn.nTremoloPos += chn.nTremoloSpeed; } } } @@ -950,56 +991,54 @@ void CSoundFile::ProcessTremor(CHANNELINDEX nChn, int &vol) IMixPlugin *pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin; if(pPlugin) { - uint8 midiChn = GetBestMidiChannel(nChn); - bool isPlaying = pPlugin->IsNotePlaying(chn.nLastNote, midiChn, nChn); + const bool isPlaying = pPlugin->IsNotePlaying(chn.nLastNote, nChn); if(vol == 0 && isPlaying) - pPlugin->MidiCommand(midiChn, pIns->nMidiProgram, pIns->wMidiBank, chn.nLastNote + NOTE_MAX_SPECIAL, 0, nChn); + pPlugin->MidiCommand(*pIns, chn.nLastNote + NOTE_MAX_SPECIAL, 0, nChn); else if(vol != 0 && !isPlaying) - pPlugin->MidiCommand(midiChn, pIns->nMidiProgram, pIns->wMidiBank, chn.nLastNote, static_cast(chn.nVolume), nChn); + pPlugin->MidiCommand(*pIns, chn.nLastNote, static_cast(chn.nVolume), nChn); } } #endif // NO_PLUGINS } -bool CSoundFile::IsEnvelopeProcessed(const ModChannel *pChn, EnvelopeType env) const +bool CSoundFile::IsEnvelopeProcessed(const ModChannel &chn, EnvelopeType env) const { - if(pChn->pModInstrument == nullptr) + if(chn.pModInstrument == nullptr) { return false; } - const InstrumentEnvelope &insEnv = pChn->pModInstrument->GetEnvelope(env); + const InstrumentEnvelope &insEnv = chn.pModInstrument->GetEnvelope(env); // IT Compatibility: S77/S79/S7B do not disable the envelope, they just pause the counter - // Test cases: s77.it, EnvLoops.xm - return ((pChn->GetEnvelope(env).flags[ENV_ENABLED] || (insEnv.dwFlags[ENV_ENABLED] && m_playBehaviour[kITEnvelopePositionHandling])) + // Test cases: s77.it, EnvLoops.xm, PanSustainRelease.xm + bool playIfPaused = m_playBehaviour[kITEnvelopePositionHandling] || m_playBehaviour[kFT2PanSustainRelease]; + return ((chn.GetEnvelope(env).flags[ENV_ENABLED] || (insEnv.dwFlags[ENV_ENABLED] && playIfPaused)) && !insEnv.empty()); } -void CSoundFile::ProcessVolumeEnvelope(ModChannel *pChn, int &vol) const +void CSoundFile::ProcessVolumeEnvelope(ModChannel &chn, int &vol) const { - if(IsEnvelopeProcessed(pChn, ENV_VOLUME)) + if(IsEnvelopeProcessed(chn, ENV_VOLUME)) { - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; - if(m_playBehaviour[kITEnvelopePositionHandling] && pChn->VolEnv.nEnvPosition == 0) + if(m_playBehaviour[kITEnvelopePositionHandling] && chn.VolEnv.nEnvPosition == 0) { // If the envelope is disabled at the very same moment as it is triggered, we do not process anything. return; } - const int envpos = pChn->VolEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); + const int envpos = chn.VolEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); // Get values in [0, 256] int envval = pIns->VolEnv.GetValueFromPosition(envpos, 256); // if we are in the release portion of the envelope, // rescale envelope factor so that it is proportional to the release point // and release envelope beginning. - if(pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET - && envpos >= pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick - && pChn->VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED) + if(chn.VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED) { - int envValueAtReleaseJump = pChn->VolEnv.nEnvValueAtReleaseJump; + int envValueAtReleaseJump = chn.VolEnv.nEnvValueAtReleaseJump; int envValueAtReleaseNode = pIns->VolEnv[pIns->VolEnv.nReleaseNode].value * 4; //If we have just hit the release node, force the current env value @@ -1008,8 +1047,19 @@ void CSoundFile::ProcessVolumeEnvelope(ModChannel *pChn, int &vol) const if(envpos == pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick) envval = envValueAtReleaseNode; - int relativeVolumeChange = (envval - envValueAtReleaseNode) * 2; - envval = envValueAtReleaseJump + relativeVolumeChange; + if(m_playBehaviour[kLegacyReleaseNode]) + { + // Old, hard to grasp release node behaviour (additive) + int relativeVolumeChange = (envval - envValueAtReleaseNode) * 2; + envval = envValueAtReleaseJump + relativeVolumeChange; + } else + { + // New behaviour, truly relative to release node + if(envValueAtReleaseNode > 0) + envval = envValueAtReleaseJump * envval / envValueAtReleaseNode; + else + envval = 0; + } } vol = (vol * Clamp(envval, 0, 512)) / 256; } @@ -1017,23 +1067,23 @@ void CSoundFile::ProcessVolumeEnvelope(ModChannel *pChn, int &vol) const } -void CSoundFile::ProcessPanningEnvelope(ModChannel *pChn) const +void CSoundFile::ProcessPanningEnvelope(ModChannel &chn) const { - if(IsEnvelopeProcessed(pChn, ENV_PANNING)) + if(IsEnvelopeProcessed(chn, ENV_PANNING)) { - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; - if(m_playBehaviour[kITEnvelopePositionHandling] && pChn->PanEnv.nEnvPosition == 0) + if(m_playBehaviour[kITEnvelopePositionHandling] && chn.PanEnv.nEnvPosition == 0) { // If the envelope is disabled at the very same moment as it is triggered, we do not process anything. return; } - const int envpos = pChn->PanEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); + const int envpos = chn.PanEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); // Get values in [-32, 32] const int envval = pIns->PanEnv.GetValueFromPosition(envpos, 64) - 32; - int pan = pChn->nRealPan; + int pan = chn.nRealPan; if(pan >= 128) { pan += (envval * (256 - pan)) / 32; @@ -1041,25 +1091,25 @@ void CSoundFile::ProcessPanningEnvelope(ModChannel *pChn) const { pan += (envval * (pan)) / 32; } - pChn->nRealPan = Clamp(pan, 0, 256); + chn.nRealPan = Clamp(pan, 0, 256); } } -void CSoundFile::ProcessPitchFilterEnvelope(ModChannel *pChn, int &period) const +int CSoundFile::ProcessPitchFilterEnvelope(ModChannel &chn, int &period) const { - if(IsEnvelopeProcessed(pChn, ENV_PITCH)) + if(IsEnvelopeProcessed(chn, ENV_PITCH)) { - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; - if(m_playBehaviour[kITEnvelopePositionHandling] && pChn->PitchEnv.nEnvPosition == 0) + if(m_playBehaviour[kITEnvelopePositionHandling] && chn.PitchEnv.nEnvPosition == 0) { // If the envelope is disabled at the very same moment as it is triggered, we do not process anything. - return; + return -1; } - const int envpos = pChn->PitchEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); + const int envpos = chn.PitchEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0); // Get values in [-256, 256] #ifdef MODPLUG_TRACKER const int32 range = ENVELOPE_MAX; @@ -1067,30 +1117,30 @@ void CSoundFile::ProcessPitchFilterEnvelope(ModChannel *pChn, int &period) const #else // TODO: AMS2 envelopes behave differently when linear slides are off - emulate with 15 * (-128...127) >> 6 // Copy over vibrato behaviour for that? - const int32 range = GetType() == MOD_TYPE_AMS2 ? uint8_max : ENVELOPE_MAX; + const int32 range = GetType() == MOD_TYPE_AMS ? uint8_max : ENVELOPE_MAX; int32 amp; switch(GetType()) { - case MOD_TYPE_AMS2: amp = 64; break; + case MOD_TYPE_AMS: amp = 64; break; case MOD_TYPE_MDL: amp = 192; break; default: amp = 512; } #endif const int envval = pIns->PitchEnv.GetValueFromPosition(envpos, amp, range) - amp / 2; - if(pChn->PitchEnv.flags[ENV_FILTER]) + if(chn.PitchEnv.flags[ENV_FILTER]) { // Filter Envelope: controls cutoff frequency - SetupChannelFilter(pChn, !pChn->dwFlags[CHN_FILTER], envval); + return SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER], envval); } else { // Pitch Envelope - if(GetType() == MOD_TYPE_MPT && pChn->pModInstrument && pChn->pModInstrument->pTuning) + if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) { - if(pChn->nFineTune != envval) + if(chn.nFineTune != envval) { - pChn->nFineTune = envval; - pChn->m_CalculateFreq = true; + chn.nFineTune = envval; + chn.m_CalculateFreq = true; //Preliminary tests indicated that this behavior //is very close to original(with 12TET) when finestep count //is 15. @@ -1115,14 +1165,15 @@ void CSoundFile::ProcessPitchFilterEnvelope(ModChannel *pChn, int &period) const } //End: Original behavior. } } + return -1; } -void CSoundFile::IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envType) const +void CSoundFile::IncrementEnvelopePosition(ModChannel &chn, EnvelopeType envType) const { - ModChannel::EnvInfo &chnEnv = pChn->GetEnvelope(envType); + ModChannel::EnvInfo &chnEnv = chn.GetEnvelope(envType); - if(pChn->pModInstrument == nullptr || !chnEnv.flags[ENV_ENABLED]) + if(chn.pModInstrument == nullptr || !chnEnv.flags[ENV_ENABLED]) { return; } @@ -1130,7 +1181,7 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envTyp // Increase position uint32 position = chnEnv.nEnvPosition + (m_playBehaviour[kITEnvelopePositionHandling] ? 0 : 1); - const InstrumentEnvelope &insEnv = pChn->pModInstrument->GetEnvelope(envType); + const InstrumentEnvelope &insEnv = chn.pModInstrument->GetEnvelope(envType); if(insEnv.empty()) { return; @@ -1149,7 +1200,7 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envTyp // FT2 compatibility: If the sustain point is at the loop end and the sustain loop has been released, don't loop anymore. // Test case: EnvLoops.xm - const bool escapeLoop = (insEnv.nLoopEnd == insEnv.nSustainEnd && insEnv.dwFlags[ENV_SUSTAIN] && pChn->dwFlags[CHN_KEYOFF] && m_playBehaviour[kFT2EnvelopeEscape]); + const bool escapeLoop = (insEnv.nLoopEnd == insEnv.nSustainEnd && insEnv.dwFlags[ENV_SUSTAIN] && chn.dwFlags[CHN_KEYOFF] && m_playBehaviour[kFT2EnvelopeEscape]); if(position == end && !escapeLoop) { @@ -1157,12 +1208,18 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envTyp } } - if(insEnv.dwFlags[ENV_SUSTAIN] && !pChn->dwFlags[CHN_KEYOFF]) + if(insEnv.dwFlags[ENV_SUSTAIN] && !chn.dwFlags[CHN_KEYOFF]) { // Envelope sustained if(position == insEnv[insEnv.nSustainEnd].tick + 1u) { position = insEnv[insEnv.nSustainStart].tick; + // FT2 compatibility: If the panning envelope reaches its sustain point before key-off, it stays there forever. + // Test case: PanSustainRelease.xm + if(m_playBehaviour[kFT2PanSustainRelease] && envType == ENV_PANNING && !chn.dwFlags[CHN_KEYOFF]) + { + chnEnv.flags.reset(ENV_ENABLED); + } } } else { @@ -1182,7 +1239,7 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envTyp // IT compatiblity: OpenMPT processes the key-off flag earlier than IT. Grab the flag from the previous tick instead. // Test case: EnvOffLength.it - if(insEnv.dwFlags[ENV_SUSTAIN] && !pChn->dwOldFlags[CHN_KEYOFF]) + if(insEnv.dwFlags[ENV_SUSTAIN] && !chn.dwOldFlags[CHN_KEYOFF] && (chnEnv.nEnvValueAtReleaseJump == NOT_YET_RELEASED || m_playBehaviour[kReleaseNodePastSustainBug])) { // Envelope sustained start = insEnv[insEnv.nSustainStart].tick; @@ -1212,18 +1269,18 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envTyp if(envType == ENV_VOLUME && endReached) { // Special handling for volume envelopes at end of envelope - if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (pChn->dwFlags[CHN_KEYOFF] && GetType() != MOD_TYPE_MDL)) + if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (chn.dwFlags[CHN_KEYOFF] && GetType() != MOD_TYPE_MDL)) { - pChn->dwFlags.set(CHN_NOTEFADE); + chn.dwFlags.set(CHN_NOTEFADE); } - if(insEnv.back().value == 0 && (pChn->nMasterChn > 0 || (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))) + if(insEnv.back().value == 0 && (chn.nMasterChn > 0 || (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))) { // Stop channel if the last envelope node is silent anyway. - pChn->dwFlags.set(CHN_NOTEFADE); - pChn->nFadeOutVol = 0; - pChn->nRealVolume = 0; - pChn->nCalcVolume = 0; + chn.dwFlags.set(CHN_NOTEFADE); + chn.nFadeOutVol = 0; + chn.nRealVolume = 0; + chn.nCalcVolume = 0; } } @@ -1232,28 +1289,28 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel *pChn, EnvelopeType envTyp } -void CSoundFile::IncrementEnvelopePositions(ModChannel *pChn) const +void CSoundFile::IncrementEnvelopePositions(ModChannel &chn) const { - IncrementEnvelopePosition(pChn, ENV_VOLUME); - IncrementEnvelopePosition(pChn, ENV_PANNING); - IncrementEnvelopePosition(pChn, ENV_PITCH); + IncrementEnvelopePosition(chn, ENV_VOLUME); + IncrementEnvelopePosition(chn, ENV_PANNING); + IncrementEnvelopePosition(chn, ENV_PITCH); } -void CSoundFile::ProcessInstrumentFade(ModChannel *pChn, int &vol) const +void CSoundFile::ProcessInstrumentFade(ModChannel &chn, int &vol) const { // FadeOut volume - if(pChn->dwFlags[CHN_NOTEFADE] && pChn->pModInstrument != nullptr) + if(chn.dwFlags[CHN_NOTEFADE] && chn.pModInstrument != nullptr) { - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; uint32 fadeout = pIns->nFadeOut; if (fadeout) { - pChn->nFadeOutVol -= fadeout * 2; - if (pChn->nFadeOutVol <= 0) pChn->nFadeOutVol = 0; - vol = (vol * pChn->nFadeOutVol) / 65536; - } else if (!pChn->nFadeOutVol) + chn.nFadeOutVol -= fadeout * 2; + if (chn.nFadeOutVol <= 0) chn.nFadeOutVol = 0; + vol = (vol * chn.nFadeOutVol) / 65536; + } else if (!chn.nFadeOutVol) { vol = 0; } @@ -1261,89 +1318,89 @@ void CSoundFile::ProcessInstrumentFade(ModChannel *pChn, int &vol) const } -void CSoundFile::ProcessPitchPanSeparation(ModChannel *pChn) const +void CSoundFile::ProcessPitchPanSeparation(ModChannel &chn) const { - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; - if ((pIns->nPPS) && (pChn->nNote != NOTE_NONE)) + if ((pIns->nPPS) && (chn.nNote != NOTE_NONE)) { // with PPS = 16 / PPC = C-5, E-6 will pan hard right (and D#6 will not) - int pandelta = (int)pChn->nRealPan + (int)((int)(pChn->nNote - pIns->nPPC - NOTE_MIN) * (int)pIns->nPPS) / 2; - pChn->nRealPan = Clamp(pandelta, 0, 256); + int pandelta = (int)chn.nRealPan + (int)((int)(chn.nNote - pIns->nPPC - NOTE_MIN) * (int)pIns->nPPS) / 2; + chn.nRealPan = Clamp(pandelta, 0, 256); } } -void CSoundFile::ProcessPanbrello(ModChannel *pChn) const +void CSoundFile::ProcessPanbrello(ModChannel &chn) const { - int pdelta = pChn->nPanbrelloOffset; - if(pChn->rowCommand.command == CMD_PANBRELLO) + int pdelta = chn.nPanbrelloOffset; + if(chn.rowCommand.command == CMD_PANBRELLO) { uint32 panpos; // IT compatibility: IT has its own, more precise tables if(m_playBehaviour[kITVibratoTremoloPanbrello]) - panpos = pChn->nPanbrelloPos; + panpos = chn.nPanbrelloPos; else - panpos = ((pChn->nPanbrelloPos + 0x10) >> 2); + panpos = ((chn.nPanbrelloPos + 0x10) >> 2); - pdelta = GetVibratoDelta(pChn->nPanbrelloType, panpos); + pdelta = GetVibratoDelta(chn.nPanbrelloType, panpos); // IT compatibility: Sample-and-hold style random panbrello (tremolo and vibrato don't use this mechanism in IT) // Test case: RandomWaveform.it - if(m_playBehaviour[kITSampleAndHoldPanbrello] && pChn->nPanbrelloType == 3) + if(m_playBehaviour[kITSampleAndHoldPanbrello] && chn.nPanbrelloType == 3) { - if(pChn->nPanbrelloPos == 0 || pChn->nPanbrelloPos >= pChn->nPanbrelloSpeed) + if(chn.nPanbrelloPos == 0 || chn.nPanbrelloPos >= chn.nPanbrelloSpeed) { - pChn->nPanbrelloPos = 0; - pChn->nPanbrelloRandomMemory = static_cast(pdelta); + chn.nPanbrelloPos = 0; + chn.nPanbrelloRandomMemory = static_cast(pdelta); } - pChn->nPanbrelloPos++; - pdelta = pChn->nPanbrelloRandomMemory; + chn.nPanbrelloPos++; + pdelta = chn.nPanbrelloRandomMemory; } else { - pChn->nPanbrelloPos += pChn->nPanbrelloSpeed; + chn.nPanbrelloPos += chn.nPanbrelloSpeed; } // IT compatibility: Panbrello effect is active until next note or panning command. // Test case: PanbrelloHold.it if(m_playBehaviour[kITPanbrelloHold]) { - pChn->nPanbrelloOffset = static_cast(pdelta); + chn.nPanbrelloOffset = static_cast(pdelta); } } if(pdelta) { - pdelta = ((pdelta * (int)pChn->nPanbrelloDepth) + 2) / 8; - pdelta += pChn->nRealPan; - pChn->nRealPan = Clamp(pdelta, 0, 256); + pdelta = ((pdelta * (int)chn.nPanbrelloDepth) + 2) / 8; + pdelta += chn.nRealPan; + chn.nRealPan = Clamp(pdelta, 0, 256); } } void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEINDEXTYPE &arpeggioSteps) { - ModChannel *pChn = &m_PlayState.Chn[nChn]; + ModChannel &chn = m_PlayState.Chn[nChn]; #ifndef NO_PLUGINS // Plugin arpeggio - if(pChn->pModInstrument && pChn->pModInstrument->nMixPlug - && !pChn->pModInstrument->dwFlags[INS_MUTE] - && !pChn->dwFlags[CHN_MUTE | CHN_SYNCMUTE]) + if(chn.pModInstrument && chn.pModInstrument->nMixPlug + && !chn.pModInstrument->dwFlags[INS_MUTE] + && !chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) { - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; IMixPlugin *pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin; if(pPlugin) { uint8 step = 0; - const bool arpOnRow = (pChn->rowCommand.command == CMD_ARPEGGIO); - const ModCommand::NOTE lastNote = ModCommand::IsNote(pChn->nLastNote) ? pIns->NoteMap[pChn->nLastNote - NOTE_MIN] : NOTE_NONE; + const bool arpOnRow = (chn.rowCommand.command == CMD_ARPEGGIO); + const ModCommand::NOTE lastNote = ModCommand::IsNote(chn.nLastNote) ? pIns->NoteMap[chn.nLastNote - NOTE_MIN] : NOTE_NONE; if(arpOnRow) { switch(m_PlayState.m_nTickCount % 3) { - case 1: step = pChn->nArpeggio >> 4; break; - case 2: step = pChn->nArpeggio & 0x0F; break; + case 1: step = chn.nArpeggio >> 4; break; + case 2: step = chn.nArpeggio & 0x0F; break; } - pChn->nArpeggioBaseNote = lastNote; + chn.nArpeggioBaseNote = lastNote; } // Trigger new note: @@ -1353,43 +1410,43 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND // - If there's no arpeggio // - but an arpeggio note is still active and // - there's no note stop or new note that would stop it anyway - if((arpOnRow && pChn->nArpeggioLastNote != pChn->nArpeggioBaseNote + step && (!m_SongFlags[SONG_FIRSTTICK] || !pChn->rowCommand.IsNote())) - || (!arpOnRow && pChn->rowCommand.note == NOTE_NONE && pChn->nArpeggioLastNote != NOTE_NONE)) - SendMIDINote(nChn, pChn->nArpeggioBaseNote + step, static_cast(pChn->nVolume)); + if((arpOnRow && chn.nArpeggioLastNote != chn.nArpeggioBaseNote + step && (!m_SongFlags[SONG_FIRSTTICK] || !chn.rowCommand.IsNote())) + || (!arpOnRow && chn.rowCommand.note == NOTE_NONE && chn.nArpeggioLastNote != NOTE_NONE)) + SendMIDINote(nChn, chn.nArpeggioBaseNote + step, static_cast(chn.nVolume)); // Stop note: // - If some arpeggio note is still registered or // - When starting an arpeggio on a row with no other note on it, stop some possibly still playing note. - if(pChn->nArpeggioLastNote != NOTE_NONE) - SendMIDINote(nChn, pChn->nArpeggioLastNote + NOTE_MAX_SPECIAL, 0); - else if(arpOnRow && m_SongFlags[SONG_FIRSTTICK] && !pChn->rowCommand.IsNote() && ModCommand::IsNote(lastNote)) + if(chn.nArpeggioLastNote != NOTE_NONE) + SendMIDINote(nChn, chn.nArpeggioLastNote + NOTE_MAX_SPECIAL, 0); + else if(arpOnRow && m_SongFlags[SONG_FIRSTTICK] && !chn.rowCommand.IsNote() && ModCommand::IsNote(lastNote)) SendMIDINote(nChn, lastNote + NOTE_MAX_SPECIAL, 0); - if(pChn->rowCommand.command == CMD_ARPEGGIO) - pChn->nArpeggioLastNote = pChn->nArpeggioBaseNote + step; + if(chn.rowCommand.command == CMD_ARPEGGIO) + chn.nArpeggioLastNote = chn.nArpeggioBaseNote + step; else - pChn->nArpeggioLastNote = NOTE_NONE; + chn.nArpeggioLastNote = NOTE_NONE; } } #endif // NO_PLUGINS - if(pChn->nCommand == CMD_ARPEGGIO) + if(chn.nCommand == CMD_ARPEGGIO) { - if((GetType() & MOD_TYPE_MPT) && pChn->pModInstrument && pChn->pModInstrument->pTuning) + if((GetType() & MOD_TYPE_MPT) && chn.pModInstrument && chn.pModInstrument->pTuning) { switch(m_PlayState.m_nTickCount % 3) { case 0: arpeggioSteps = 0; break; - case 1: arpeggioSteps = pChn->nArpeggio >> 4; break; - case 2: arpeggioSteps = pChn->nArpeggio & 0x0F; break; + case 1: arpeggioSteps = chn.nArpeggio >> 4; break; + case 2: arpeggioSteps = chn.nArpeggio & 0x0F; break; } - pChn->m_CalculateFreq = true; - pChn->m_ReCalculateFreqOnFirstTick = true; + chn.m_CalculateFreq = true; + chn.m_ReCalculateFreqOnFirstTick = true; } else { if(GetType() == MOD_TYPE_MT2 && m_SongFlags[SONG_FIRSTTICK]) { // MT2 resets any previous portamento when an arpeggio occurs. - pChn->nPeriod = period = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC5Speed); + chn.nPeriod = period = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } if(m_playBehaviour[kITArpeggio]) @@ -1398,13 +1455,13 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND // Pattern delay restarts tick counting. Not quite correct yet! const uint32 tick = m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay); - if(pChn->nArpeggio != 0) + if(chn.nArpeggio != 0) { uint32 arpRatio = 65536; switch(tick % 3) { - case 1: arpRatio = LinearSlideUpTable[(pChn->nArpeggio >> 4) * 16]; break; - case 2: arpRatio = LinearSlideUpTable[(pChn->nArpeggio & 0x0F) * 16]; break; + case 1: arpRatio = LinearSlideUpTable[(chn.nArpeggio >> 4) * 16]; break; + case 2: arpRatio = LinearSlideUpTable[(chn.nArpeggio & 0x0F) * 16]; break; } if(PeriodsAreFrequencies()) period = Util::muldivr(period, arpRatio, 65536); @@ -1419,7 +1476,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND // Arpeggio is added on top of current note, but cannot do it the IT way because of // the behaviour in ArpeggioClamp.xm. // Test case: ArpSlide.xm - auto note = GetNoteFromPeriod(period, pChn->nFineTune, pChn->nC5Speed); + auto note = GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed); // The fact that arpeggio behaves in a totally fucked up way at 16 ticks/row or more is that the arpeggio offset LUT only has 16 entries in FT2. // At more than 16 ticks/row, FT2 reads into the vibrato table, which is placed right after the arpeggio table. @@ -1430,17 +1487,17 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND else arpPos %= 3; switch(arpPos) { - case 1: note += (pChn->nArpeggio >> 4); break; - case 2: note += (pChn->nArpeggio & 0x0F); break; + case 1: note += (chn.nArpeggio >> 4); break; + case 2: note += (chn.nArpeggio & 0x0F); break; } - period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC5Speed); + period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); // FT2 compatibility: FT2 has a different note limit for Arpeggio. // Test case: ArpeggioClamp.xm if(note >= 108 + NOTE_MIN && arpPos != 0) { - period = std::max(period, GetPeriodFromNote(108 + NOTE_MIN, 0, pChn->nC5Speed)); + period = std::max(period, GetPeriodFromNote(108 + NOTE_MIN, 0, chn.nC5Speed)); } } @@ -1451,15 +1508,15 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND uint32 tick = m_PlayState.m_nTickCount; // TODO other likely formats for MOD case: MED, OKT, etc - uint8 note = (GetType() != MOD_TYPE_MOD) ? pChn->nNote : static_cast(GetNoteFromPeriod(period, pChn->nFineTune, pChn->nC5Speed)); + uint8 note = (GetType() != MOD_TYPE_MOD) ? chn.nNote : static_cast(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed)); if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI)) tick += 2; switch(tick % 3) { - case 1: note += (pChn->nArpeggio >> 4); break; - case 2: note += (pChn->nArpeggio & 0x0F); break; + case 1: note += (chn.nArpeggio >> 4); break; + case 2: note += (chn.nArpeggio & 0x0F); break; } - if(note != pChn->nNote || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_STM)) || m_playBehaviour[KST3PortaAfterArpeggio]) + if(note != chn.nNote || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_STM)) || m_playBehaviour[KST3PortaAfterArpeggio]) { if(m_SongFlags[SONG_PT_MODE]) { @@ -1474,16 +1531,18 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND note -= 37; } } - period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC5Speed); + period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_PSM | MOD_TYPE_STM)) { // The arpeggio note offset remains effective after the end of the current row in ScreamTracker 2. // This fixes the flute lead in MORPH.STM by Skaven, pattern 27. - pChn->nPeriod = period; + // Note that ScreamTracker 2.24 handles arpeggio slightly differently: It only considers the lower + // nibble, and switches to that note halfway through the row. + chn.nPeriod = period; } else if(m_playBehaviour[KST3PortaAfterArpeggio]) { - pChn->nArpeggioLastNote = note; + chn.nArpeggioLastNote = note; } } } @@ -1606,7 +1665,7 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP { pwd = chn.pModInstrument->midiPWD; } - plugin->MidiVibrato(GetBestMidiChannel(nChn), midiDelta, pwd); + plugin->MidiVibrato(midiDelta, pwd, nChn); } #endif // NO_PLUGINS } @@ -1625,20 +1684,20 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn); if(plugin != nullptr) { - plugin->MidiVibrato(GetBestMidiChannel(nChn), 0, 0); + plugin->MidiVibrato(0, 0, nChn); } #endif // NO_PLUGINS } } -void CSoundFile::ProcessSampleAutoVibrato(ModChannel *pChn, int &period, Tuning::RATIOTYPE &vibratoFactor, int &nPeriodFrac) const +void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning::RATIOTYPE &vibratoFactor, int &nPeriodFrac) const { // Sample Auto-Vibrato - if ((pChn->pModSample) && (pChn->pModSample->nVibDepth)) + if(chn.pModSample != nullptr && chn.pModSample->nVibDepth) { - const ModSample *pSmp = pChn->pModSample; - const bool alternativeTuning = pChn->pModInstrument && pChn->pModInstrument->pTuning; + const ModSample *pSmp = chn.pModSample; + const bool alternativeTuning = chn.pModInstrument && chn.pModInstrument->pTuning; // In IT linear slide mode, we use frequencies, otherwise we use periods, which are upside down. // In this context, the "up" tables refer to the tables that increase frequency, and the down tables are the ones that decrease frequency. @@ -1664,14 +1723,14 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel *pChn, int &period, Tuning: 4) AH contains the depth of the vibrato as a fine-linear slide. 5) Mov [SomeVariableNameRelatingToVibrato], AX ; For the next cycle. */ - const int vibpos = pChn->nAutoVibPos & 0xFF; - int adepth = pChn->nAutoVibDepth; // (1) + const int vibpos = chn.nAutoVibPos & 0xFF; + int adepth = chn.nAutoVibDepth; // (1) adepth += pSmp->nVibSweep; // (2 & 3) LimitMax(adepth, static_cast(pSmp->nVibDepth * 256u)); - pChn->nAutoVibDepth = adepth; // (5) + chn.nAutoVibDepth = adepth; // (5) adepth /= 256; // (4) - pChn->nAutoVibPos += pSmp->nVibRate; + chn.nAutoVibPos += pSmp->nVibRate; int vdelta; switch(pSmp->nVibType) @@ -1720,65 +1779,65 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel *pChn, int &period, Tuning: // MPT's autovibrato code if (pSmp->nVibSweep == 0 && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { - pChn->nAutoVibDepth = pSmp->nVibDepth * 256; + chn.nAutoVibDepth = pSmp->nVibDepth * 256; } else { // Calculate current autovibrato depth using vibsweep if (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { - pChn->nAutoVibDepth += pSmp->nVibSweep * 2u; + chn.nAutoVibDepth += pSmp->nVibSweep * 2u; } else { - if(!pChn->dwFlags[CHN_KEYOFF]) + if(!chn.dwFlags[CHN_KEYOFF]) { - pChn->nAutoVibDepth += (pSmp->nVibDepth * 256u) / pSmp->nVibSweep; + chn.nAutoVibDepth += (pSmp->nVibDepth * 256u) / pSmp->nVibSweep; } } - LimitMax(pChn->nAutoVibDepth, static_cast(pSmp->nVibDepth * 256u)); + LimitMax(chn.nAutoVibDepth, static_cast(pSmp->nVibDepth * 256u)); } - pChn->nAutoVibPos += pSmp->nVibRate; + chn.nAutoVibPos += pSmp->nVibRate; int vdelta; switch(pSmp->nVibType) { case VIB_RANDOM: - vdelta = ModRandomTable[pChn->nAutoVibPos & 0x3F]; - pChn->nAutoVibPos++; + vdelta = ModRandomTable[chn.nAutoVibPos & 0x3F]; + chn.nAutoVibPos++; break; case VIB_RAMP_DOWN: - vdelta = ((0x40 - (pChn->nAutoVibPos / 2u)) & 0x7F) - 0x40; + vdelta = ((0x40 - (chn.nAutoVibPos / 2u)) & 0x7F) - 0x40; break; case VIB_RAMP_UP: - vdelta = ((0x40 + (pChn->nAutoVibPos / 2u)) & 0x7F) - 0x40; + vdelta = ((0x40 + (chn.nAutoVibPos / 2u)) & 0x7F) - 0x40; break; case VIB_SQUARE: - vdelta = (pChn->nAutoVibPos & 128) ? +64 : -64; + vdelta = (chn.nAutoVibPos & 128) ? +64 : -64; break; case VIB_SINE: default: if(GetType() != MOD_TYPE_MT2) { - vdelta = ft2VibratoTable[pChn->nAutoVibPos & 0xFF]; + vdelta = ft2VibratoTable[chn.nAutoVibPos & 0xFF]; } else { // Fix flat-sounding pads in "another worlds" by Eternal Engine. // Vibrato starts at the maximum amplitude of the sine wave // and the vibrato frequency never decreases below the original note's frequency. - vdelta = (ft2VibratoTable[(pChn->nAutoVibPos + 192) & 0xFF] + 64) / 2; + vdelta = (ft2VibratoTable[(chn.nAutoVibPos + 192) & 0xFF] + 64) / 2; } } - int n = (vdelta * pChn->nAutoVibDepth) / 256; + int n = (vdelta * chn.nAutoVibDepth) / 256; if(alternativeTuning) { //Vib sweep is not taken into account here. vibratoFactor += 0.05F * pSmp->nVibDepth * vdelta / 4096.0f; //4096 == 64^2 //See vibrato for explanation. - pChn->m_CalculateFreq = true; + chn.m_CalculateFreq = true; /* Finestep vibrato: const float autoVibDepth = pSmp->nVibDepth * val / 4096.0f; //4096 == 64^2 - vibratoFineSteps += static_cast(pChn->pModInstrument->pTuning->GetFineStepCount() * autoVibDepth); - pChn->m_CalculateFreq = true; + vibratoFineSteps += static_cast(chn.pModInstrument->pTuning->GetFineStepCount() * autoVibDepth); + chn.m_CalculateFreq = true; */ } else //Original behavior @@ -1812,12 +1871,12 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel *pChn, int &period, Tuning: } -void CSoundFile::ProcessRamping(ModChannel *pChn) const +void CSoundFile::ProcessRamping(ModChannel &chn) const { - pChn->leftRamp = pChn->rightRamp = 0; - if(pChn->dwFlags[CHN_VOLUMERAMP] && (pChn->leftVol != pChn->newLeftVol || pChn->rightVol != pChn->newRightVol)) + chn.leftRamp = chn.rightRamp = 0; + if(chn.dwFlags[CHN_VOLUMERAMP] && (chn.leftVol != chn.newLeftVol || chn.rightVol != chn.newRightVol)) { - const bool rampUp = (pChn->newLeftVol > pChn->leftVol) || (pChn->newRightVol > pChn->rightVol); + const bool rampUp = (chn.newLeftVol > chn.leftVol) || (chn.newRightVol > chn.rightVol); int32 rampLength, globalRampLength, instrRampLength = 0; rampLength = globalRampLength = (rampUp ? m_MixerSettings.GetVolumeRampUpSamples() : m_MixerSettings.GetVolumeRampDownSamples()); //XXXih: add real support for bidi ramping here @@ -1828,9 +1887,9 @@ void CSoundFile::ProcessRamping(ModChannel *pChn) const rampLength = globalRampLength = Util::muldivr(5, m_MixerSettings.gdwMixingFreq, 1000); } - if(pChn->pModInstrument != nullptr && rampUp) + if(chn.pModInstrument != nullptr && rampUp) { - instrRampLength = pChn->pModInstrument->nVolRampUp; + instrRampLength = chn.pModInstrument->nVolRampUp; rampLength = instrRampLength ? (m_MixerSettings.gdwMixingFreq * instrRampLength / 100000) : globalRampLength; } const bool enableCustomRamp = (instrRampLength > 0); @@ -1840,55 +1899,55 @@ void CSoundFile::ProcessRamping(ModChannel *pChn) const rampLength = 1; } - int32 leftDelta = ((pChn->newLeftVol - pChn->leftVol) * (1 << VOLUMERAMPPRECISION)); - int32 rightDelta = ((pChn->newRightVol - pChn->rightVol) * (1 << VOLUMERAMPPRECISION)); + int32 leftDelta = ((chn.newLeftVol - chn.leftVol) * (1 << VOLUMERAMPPRECISION)); + int32 rightDelta = ((chn.newRightVol - chn.rightVol) * (1 << VOLUMERAMPPRECISION)); if(!enableCustomRamp) { // Extra-smooth ramping, unless we're forced to use the default values - if((pChn->leftVol | pChn->rightVol) && (pChn->newLeftVol | pChn->newRightVol) && !pChn->dwFlags[CHN_FASTVOLRAMP]) + if((chn.leftVol | chn.rightVol) && (chn.newLeftVol | chn.newRightVol) && !chn.dwFlags[CHN_FASTVOLRAMP]) { rampLength = m_PlayState.m_nBufferCount; - Limit(rampLength, globalRampLength, 1 << (VOLUMERAMPPRECISION - 1)); + Limit(rampLength, globalRampLength, int32(1 << (VOLUMERAMPPRECISION - 1))); } } - pChn->leftRamp = leftDelta / rampLength; - pChn->rightRamp = rightDelta / rampLength; - pChn->leftVol = pChn->newLeftVol - ((pChn->leftRamp * rampLength) / (1 << VOLUMERAMPPRECISION)); - pChn->rightVol = pChn->newRightVol - ((pChn->rightRamp * rampLength) / (1 << VOLUMERAMPPRECISION)); + chn.leftRamp = leftDelta / rampLength; + chn.rightRamp = rightDelta / rampLength; + chn.leftVol = chn.newLeftVol - ((chn.leftRamp * rampLength) / (1 << VOLUMERAMPPRECISION)); + chn.rightVol = chn.newRightVol - ((chn.rightRamp * rampLength) / (1 << VOLUMERAMPPRECISION)); - if (pChn->leftRamp|pChn->rightRamp) + if (chn.leftRamp|chn.rightRamp) { - pChn->nRampLength = rampLength; + chn.nRampLength = rampLength; } else { - pChn->dwFlags.reset(CHN_VOLUMERAMP); - pChn->leftVol = pChn->newLeftVol; - pChn->rightVol = pChn->newRightVol; + chn.dwFlags.reset(CHN_VOLUMERAMP); + chn.leftVol = chn.newLeftVol; + chn.rightVol = chn.newRightVol; } } else { - pChn->dwFlags.reset(CHN_VOLUMERAMP); - pChn->leftVol = pChn->newLeftVol; - pChn->rightVol = pChn->newRightVol; + chn.dwFlags.reset(CHN_VOLUMERAMP); + chn.leftVol = chn.newLeftVol; + chn.rightVol = chn.newRightVol; } - pChn->rampLeftVol = pChn->leftVol * (1 << VOLUMERAMPPRECISION); - pChn->rampRightVol = pChn->rightVol * (1 << VOLUMERAMPPRECISION); - pChn->dwFlags.reset(CHN_FASTVOLRAMP); + chn.rampLeftVol = chn.leftVol * (1 << VOLUMERAMPPRECISION); + chn.rampRightVol = chn.rightVol * (1 << VOLUMERAMPPRECISION); + chn.dwFlags.reset(CHN_FASTVOLRAMP); } -SamplePosition CSoundFile::GetChannelIncrement(ModChannel *pChn, uint32 period, int periodFrac) const +SamplePosition CSoundFile::GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const { uint32 freq; - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; if(GetType() != MOD_TYPE_MPT || pIns == nullptr || pIns->pTuning == nullptr) { - freq = GetFreqFromPeriod(period, pChn->nC5Speed, periodFrac); + freq = GetFreqFromPeriod(period, chn.nC5Speed, periodFrac); } else { - freq = pChn->m_Freq; + freq = chn.m_Freq; } // Applying Pitch/Tempo lock. @@ -1967,56 +2026,56 @@ bool CSoundFile::ReadNote() //////////////////////////////////////////////////////////////////////////////////// // Update channels data m_nMixChannels = 0; - ModChannel *pChn = m_PlayState.Chn; - for (CHANNELINDEX nChn = 0; nChn < MAX_CHANNELS; nChn++, pChn++) + for (CHANNELINDEX nChn = 0; nChn < MAX_CHANNELS; nChn++) { + ModChannel &chn = m_PlayState.Chn[nChn]; // FT2 Compatibility: Prevent notes to be stopped after a fadeout. This way, a portamento effect can pick up a faded instrument which is long enough. // This occurs for example in the bassline (channel 11) of jt_burn.xm. I hope this won't break anything else... // I also suppose this could decrease mixing performance a bit, but hey, which CPU can't handle 32 muted channels these days... :-) - if(pChn->dwFlags[CHN_NOTEFADE] && (!(pChn->nFadeOutVol|pChn->leftVol|pChn->rightVol)) && !m_playBehaviour[kFT2ProcessSilentChannels]) + if(chn.dwFlags[CHN_NOTEFADE] && (!(chn.nFadeOutVol|chn.leftVol|chn.rightVol)) && !m_playBehaviour[kFT2ProcessSilentChannels]) { - pChn->nLength = 0; - pChn->nROfs = pChn->nLOfs = 0; + chn.nLength = 0; + chn.nROfs = chn.nLOfs = 0; } // Check for unused channel - if(pChn->dwFlags[CHN_MUTE] || (nChn >= m_nChannels && !pChn->nLength)) + if(chn.dwFlags[CHN_MUTE] || (nChn >= m_nChannels && !chn.nLength)) { if(nChn < m_nChannels) { // Process MIDI macros on channels that are currently muted. ProcessMacroOnChannel(nChn); } - pChn->nLeftVU = pChn->nRightVU = 0; + chn.nLeftVU = chn.nRightVU = 0; continue; } // Reset channel data - pChn->increment = SamplePosition(0); - pChn->nRealVolume = 0; - pChn->nCalcVolume = 0; + chn.increment = SamplePosition(0); + chn.nRealVolume = 0; + chn.nCalcVolume = 0; - pChn->nRampLength = 0; + chn.nRampLength = 0; //Aux variables Tuning::RATIOTYPE vibratoFactor = 1; Tuning::NOTEINDEXTYPE arpeggioSteps = 0; - const ModInstrument *pIns = pChn->pModInstrument; + const ModInstrument *pIns = chn.pModInstrument; // Calc Frequency int period; // Also process envelopes etc. when there's a plugin on this channel, for possible fake automation using volume and pan data. // We only care about master channels, though, since automation only "happens" on them. - const bool samplePlaying = (pChn->nPeriod && pChn->nLength); - const bool plugAssigned = (nChn < m_nChannels) && (ChnSettings[nChn].nMixPlugin || (pChn->pModInstrument != nullptr && pChn->pModInstrument->nMixPlug)); + const bool samplePlaying = (chn.nPeriod && chn.nLength); + const bool plugAssigned = (nChn < m_nChannels) && (ChnSettings[nChn].nMixPlugin || (chn.pModInstrument != nullptr && chn.pModInstrument->nMixPlug)); if (samplePlaying || plugAssigned) { - int vol = pChn->nVolume; - int insVol = pChn->nInsVol; // This is the "SV * IV" value in ITTECH.TXT + int vol = chn.nVolume; + int insVol = chn.nInsVol; // This is the "SV * IV" value in ITTECH.TXT - ProcessVolumeSwing(pChn, m_playBehaviour[kITSwingBehaviour] ? insVol : vol); - ProcessPanningSwing(pChn); - ProcessTremolo(pChn, vol); + ProcessVolumeSwing(chn, m_playBehaviour[kITSwingBehaviour] ? insVol : vol); + ProcessPanningSwing(chn); + ProcessTremolo(chn, vol); ProcessTremor(nChn, vol); // Clip volume and multiply (extend to 14 bits) @@ -2033,65 +2092,65 @@ bool CSoundFile::ReadNote() // When using MPT behaviour, we get the envelope position for the next tick while we are still calculating the current tick, // which then results in wrong position information when the envelope is paused on the next row. // Test cases: s77.it - IncrementEnvelopePositions(pChn); + IncrementEnvelopePositions(chn); } - ProcessVolumeEnvelope(pChn, vol); - ProcessInstrumentFade(pChn, vol); - ProcessPanningEnvelope(pChn); - ProcessPitchPanSeparation(pChn); + ProcessVolumeEnvelope(chn, vol); + ProcessInstrumentFade(chn, vol); + ProcessPanningEnvelope(chn); + ProcessPitchPanSeparation(chn); } else { // No Envelope: key off => note cut - if(pChn->dwFlags[CHN_NOTEFADE]) // 1.41-: CHN_KEYOFF|CHN_NOTEFADE + if(chn.dwFlags[CHN_NOTEFADE]) // 1.41-: CHN_KEYOFF|CHN_NOTEFADE { - pChn->nFadeOutVol = 0; + chn.nFadeOutVol = 0; vol = 0; } } // vol is 14-bits if (vol) { - // IMPORTANT: pChn->nRealVolume is 14 bits !!! + // IMPORTANT: chn.nRealVolume is 14 bits !!! // -> Util::muldiv( 14+8, 6+6, 18); => RealVolume: 14-bit result (22+12-20) - if(pChn->dwFlags[CHN_SYNCMUTE]) + if(chn.dwFlags[CHN_SYNCMUTE]) { - pChn->nRealVolume = 0; + chn.nRealVolume = 0; } else if (m_PlayConfig.getGlobalVolumeAppliesToMaster()) { // Don't let global volume affect level of sample if // Global volume is going to be applied to master output anyway. - pChn->nRealVolume = Util::muldiv(vol * MAX_GLOBAL_VOLUME, pChn->nGlobalVol * insVol, 1 << 20); + chn.nRealVolume = Util::muldiv(vol * MAX_GLOBAL_VOLUME, chn.nGlobalVol * insVol, 1 << 20); } else { - pChn->nRealVolume = Util::muldiv(vol * m_PlayState.m_nGlobalVolume, pChn->nGlobalVol * insVol, 1 << 20); + chn.nRealVolume = Util::muldiv(vol * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * insVol, 1 << 20); } } - pChn->nCalcVolume = vol; // Update calculated volume for MIDI macros + chn.nCalcVolume = vol; // Update calculated volume for MIDI macros // ST3 only clamps the final output period, but never the channel's internal period. // Test case: PeriodLimit.s3m - if (pChn->nPeriod < m_nMinPeriod + if (chn.nPeriod < m_nMinPeriod && GetType() != MOD_TYPE_S3M && !PeriodsAreFrequencies()) { - pChn->nPeriod = m_nMinPeriod; + chn.nPeriod = m_nMinPeriod; } - if(m_playBehaviour[kFT2Periods]) Clamp(pChn->nPeriod, 1, 31999); - period = pChn->nPeriod; + if(m_playBehaviour[kFT2Periods]) Clamp(chn.nPeriod, 1, 31999); + period = chn.nPeriod; // When glissando mode is set to semitones, clamp to the next halftone. - if((pChn->dwFlags & (CHN_GLISSANDO | CHN_PORTAMENTO)) == (CHN_GLISSANDO | CHN_PORTAMENTO) - && (!m_SongFlags[SONG_PT_MODE] || (pChn->rowCommand.IsPortamento() && !m_SongFlags[SONG_FIRSTTICK]))) + if((chn.dwFlags & (CHN_GLISSANDO | CHN_PORTAMENTO)) == (CHN_GLISSANDO | CHN_PORTAMENTO) + && (!m_SongFlags[SONG_PT_MODE] || (chn.rowCommand.IsPortamento() && !m_SongFlags[SONG_FIRSTTICK]))) { - if(period != pChn->cachedPeriod) + if(period != chn.cachedPeriod) { // Only recompute this whole thing in case the base period has changed. - pChn->cachedPeriod = period; - pChn->glissandoPeriod = GetPeriodFromNote(GetNoteFromPeriod(period, pChn->nFineTune, pChn->nC5Speed), pChn->nFineTune, pChn->nC5Speed); + chn.cachedPeriod = period; + chn.glissandoPeriod = GetPeriodFromNote(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed), chn.nFineTune, chn.nC5Speed); } - period = pChn->glissandoPeriod; + period = chn.glissandoPeriod; } ProcessArpeggio(nChn, period, arpeggioSteps); @@ -2106,25 +2165,24 @@ bool CSoundFile::ReadNote() int limitLow = 113 * 4, limitHigh = 856 * 4; if(GetType() != MOD_TYPE_S3M) { - const int tableOffset = XM2MODFineTune(pChn->nFineTune) * 12; + const int tableOffset = XM2MODFineTune(chn.nFineTune) * 12; limitLow = ProTrackerTunedPeriods[tableOffset + 11] / 2; limitHigh = ProTrackerTunedPeriods[tableOffset] * 2; // Amiga cannot actually keep up with lower periods if(limitLow < 113 * 4) limitLow = 113 * 4; } Limit(period, limitLow, limitHigh); - Limit(pChn->nPeriod, limitLow, limitHigh); + Limit(chn.nPeriod, limitLow, limitHigh); } - ProcessPanbrello(pChn); - + ProcessPanbrello(chn); } // IT Compatibility: Ensure that there is no pan swing, panbrello, panning envelopes, etc. applied on surround channels. // Test case: surround-pan.it - if(pChn->dwFlags[CHN_SURROUND] && !m_SongFlags[SONG_SURROUNDPAN] && m_playBehaviour[kITNoSurroundPan]) + if(chn.dwFlags[CHN_SURROUND] && !m_SongFlags[SONG_SURROUNDPAN] && m_playBehaviour[kITNoSurroundPan]) { - pChn->nRealPan = 128; + chn.nRealPan = 128; } // Now that all relevant envelopes etc. have been processed, we can parse the MIDI macro data. @@ -2133,11 +2191,16 @@ bool CSoundFile::ReadNote() // After MIDI macros have been processed, we can also process the pitch / filter envelope and other pitch-related things. if(samplePlaying) { - ProcessPitchFilterEnvelope(pChn, period); + int cutoff = ProcessPitchFilterEnvelope(chn, period); + if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) + { + // Cutoff doubles as modulator intensity for FM instruments + m_opl->Volume(nChn, static_cast(cutoff / 4), true); + } } - if(pChn->rowCommand.volcmd == VOLCMD_VIBRATODEPTH && - (pChn->rowCommand.command == CMD_VIBRATO || pChn->rowCommand.command == CMD_VIBRATOVOL || pChn->rowCommand.command == CMD_FINEVIBRATO)) + if(chn.rowCommand.volcmd == VOLCMD_VIBRATODEPTH && + (chn.rowCommand.command == CMD_VIBRATO || chn.rowCommand.command == CMD_VIBRATOVOL || chn.rowCommand.command == CMD_FINEVIBRATO)) { if(GetType() == MOD_TYPE_XM) { @@ -2145,13 +2208,13 @@ bool CSoundFile::ReadNote() // Effect column vibrato parameter has precedence if non-zero. // Test case: VibratoDouble.xm if(!m_SongFlags[SONG_FIRSTTICK]) - pChn->nVibratoPos += pChn->nVibratoSpeed; + chn.nVibratoPos += chn.nVibratoSpeed; } else if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { // IT Compatibility: Vibrato should be applied twice if both volume-colum and effect column vibrato is present. // Volume column vibrato parameter has precedence if non-zero. // Test case: VibratoDouble.it - Vibrato(pChn, pChn->rowCommand.vol); + Vibrato(chn, chn.rowCommand.vol); ProcessVibrato(nChn, period, vibratoFactor); } } @@ -2161,36 +2224,68 @@ bool CSoundFile::ReadNote() if(samplePlaying) { int nPeriodFrac = 0; - ProcessSampleAutoVibrato(pChn, period, vibratoFactor, nPeriodFrac); + ProcessSampleAutoVibrato(chn, period, vibratoFactor, nPeriodFrac); // Final Period // ST3 only clamps the final output period, but never the channel's internal period. // Test case: PeriodLimit.s3m if (period <= m_nMinPeriod) { - if(m_playBehaviour[kST3LimitPeriod]) pChn->nLength = 0; // Pattern 15 in watcha.s3m + if(m_playBehaviour[kST3LimitPeriod]) chn.nLength = 0; // Pattern 15 in watcha.s3m period = m_nMinPeriod; } + if((chn.dwFlags & (CHN_ADLIB | CHN_MUTE | CHN_SYNCMUTE)) == CHN_ADLIB && !chn.pModSample->uFlags[CHN_MUTE] && m_opl) + { + const bool doProcess = m_playBehaviour[kOPLFlexibleNoteOff] || !chn.dwFlags[CHN_NOTEFADE] || GetType() == MOD_TYPE_S3M; + if(doProcess && !(GetType() == MOD_TYPE_S3M && chn.dwFlags[CHN_KEYOFF])) + { + // In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440. + // Hence, we have to translate our "sample rate" into pitch. + const auto freq = GetFreqFromPeriod(period, chn.nC5Speed, nPeriodFrac); + const auto oplmilliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS); + const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0); + m_opl->Frequency(nChn, oplmilliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]); + } + if(doProcess) + { + // Scale volume to OPL range (0...63). + m_opl->Volume(nChn, static_cast(Util::muldivr_unsigned(chn.nCalcVolume * chn.nGlobalVol * chn.nInsVol, 63, 1 << 26)), false); + chn.nRealPan = m_opl->Pan(nChn, chn.nRealPan) * 128 + 128; + } + + // Deallocate OPL channels for notes that are most definitely never going to play again. + const auto *ins = chn.pModInstrument; + if(ins != nullptr + && (ins->VolEnv.dwFlags & (ENV_ENABLED | ENV_LOOP | ENV_SUSTAIN)) == ENV_ENABLED + && !ins->VolEnv.empty() + && chn.GetEnvelope(ENV_VOLUME).nEnvPosition >= ins->VolEnv.back().tick + && ins->VolEnv.back().value == 0) + { + m_opl->NoteCut(nChn); + chn.dwFlags.set(CHN_NOTEFADE); + chn.nFadeOutVol = 0; + } + } + if(GetType() == MOD_TYPE_MPT && pIns != nullptr && pIns->pTuning != nullptr) { // In this case: GetType() == MOD_TYPE_MPT and using custom tunings. - if(pChn->m_CalculateFreq || (pChn->m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0)) + if(chn.m_CalculateFreq || (chn.m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0)) { - ModCommand::NOTE note = pChn->nNote; - if(!ModCommand::IsNote(note)) note = pChn->nLastNote; + ModCommand::NOTE note = chn.nNote; + if(!ModCommand::IsNote(note)) note = chn.nLastNote; if(m_playBehaviour[kITRealNoteMapping] && note >= NOTE_MIN && note <= NOTE_MAX) note = pIns->NoteMap[note - NOTE_MIN]; - pChn->m_Freq = Util::Round((pChn->nC5Speed << FREQ_FRACBITS) * vibratoFactor * pIns->pTuning->GetRatio(note - NOTE_MIDDLEC + arpeggioSteps, pChn->nFineTune+pChn->m_PortamentoFineSteps)); - if(!pChn->m_CalculateFreq) - pChn->m_ReCalculateFreqOnFirstTick = false; + chn.m_Freq = mpt::saturate_round((chn.nC5Speed << FREQ_FRACBITS) * vibratoFactor * pIns->pTuning->GetRatio(note - NOTE_MIDDLEC + arpeggioSteps, chn.nFineTune+chn.m_PortamentoFineSteps)); + if(!chn.m_CalculateFreq) + chn.m_ReCalculateFreqOnFirstTick = false; else - pChn->m_CalculateFreq = false; + chn.m_CalculateFreq = false; } } - - SamplePosition ninc = GetChannelIncrement(pChn, period, nPeriodFrac); + SamplePosition ninc = GetChannelIncrement(chn, period, nPeriodFrac); #ifndef MODPLUG_TRACKER ninc.MulDiv(m_nFreqFactor, 65536); #endif // !MODPLUG_TRACKER @@ -2198,7 +2293,7 @@ bool CSoundFile::ReadNote() { ninc.Set(0, 1); } - pChn->increment = ninc; + chn.increment = ninc; } // Increment envelope positions @@ -2206,41 +2301,41 @@ bool CSoundFile::ReadNote() { // In IT and FT2 compatible mode, envelope positions are updated above. // Test cases: s77.it, EnvLoops.xm - IncrementEnvelopePositions(pChn); + IncrementEnvelopePositions(chn); } // Volume ramping - pChn->dwFlags.set(CHN_VOLUMERAMP, (pChn->nRealVolume | pChn->rightVol | pChn->leftVol) != 0); + chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol | chn.leftVol) != 0); - if (pChn->nLeftVU > VUMETER_DECAY) pChn->nLeftVU -= VUMETER_DECAY; else pChn->nLeftVU = 0; - if (pChn->nRightVU > VUMETER_DECAY) pChn->nRightVU -= VUMETER_DECAY; else pChn->nRightVU = 0; + if (chn.nLeftVU > VUMETER_DECAY) chn.nLeftVU -= VUMETER_DECAY; else chn.nLeftVU = 0; + if (chn.nRightVU > VUMETER_DECAY) chn.nRightVU -= VUMETER_DECAY; else chn.nRightVU = 0; - pChn->newLeftVol = pChn->newRightVol = 0; - pChn->pCurrentSample = (pChn->pModSample && pChn->pModSample->pSample && pChn->nLength && pChn->IsSamplePlaying()) ? pChn->pModSample->pSample : nullptr; - if (pChn->pCurrentSample || (pChn->HasMIDIOutput() && !pChn->dwFlags[CHN_KEYOFF | CHN_NOTEFADE])) + chn.newLeftVol = chn.newRightVol = 0; + chn.pCurrentSample = (chn.pModSample && chn.pModSample->HasSampleData() && chn.nLength && chn.IsSamplePlaying()) ? chn.pModSample->samplev() : nullptr; + if (chn.pCurrentSample || (chn.HasMIDIOutput() && !chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE])) { // Update VU-Meter (nRealVolume is 14-bit) - uint32 vul = (pChn->nRealVolume * pChn->nRealPan) / (1 << 14); + uint32 vul = (chn.nRealVolume * chn.nRealPan) / (1 << 14); if (vul > 127) vul = 127; - if (pChn->nLeftVU > 127) pChn->nLeftVU = (uint8)vul; + if (chn.nLeftVU > 127) chn.nLeftVU = (uint8)vul; vul /= 2; - if (pChn->nLeftVU < vul) pChn->nLeftVU = (uint8)vul; - uint32 vur = (pChn->nRealVolume * (256-pChn->nRealPan)) / (1 << 14); + if (chn.nLeftVU < vul) chn.nLeftVU = (uint8)vul; + uint32 vur = (chn.nRealVolume * (256-chn.nRealPan)) / (1 << 14); if (vur > 127) vur = 127; - if (pChn->nRightVU > 127) pChn->nRightVU = (uint8)vur; + if (chn.nRightVU > 127) chn.nRightVU = (uint8)vur; vur /= 2; - if (pChn->nRightVU < vur) pChn->nRightVU = (uint8)vur; + if (chn.nRightVU < vur) chn.nRightVU = (uint8)vur; } else { // Note change but no sample - if (pChn->nLeftVU > 128) pChn->nLeftVU = 0; - if (pChn->nRightVU > 128) pChn->nRightVU = 0; + if (chn.nLeftVU > 128) chn.nLeftVU = 0; + if (chn.nRightVU > 128) chn.nRightVU = 0; } - if (pChn->pCurrentSample) + if (chn.pCurrentSample) { #ifdef MODPLUG_TRACKER - const uint32 kChnMasterVol = pChn->dwFlags[CHN_EXTRALOUD] ? (uint32)m_PlayConfig.getNormalSamplePreAmp() : nMasterVol; + const uint32 kChnMasterVol = chn.dwFlags[CHN_EXTRALOUD] ? (uint32)m_PlayConfig.getNormalSamplePreAmp() : nMasterVol; #else const uint32 kChnMasterVol = nMasterVol; #endif // MODPLUG_TRACKER @@ -2248,17 +2343,17 @@ bool CSoundFile::ReadNote() // Adjusting volumes if (m_MixerSettings.gnChannels >= 2) { - int32 pan = pChn->nRealPan; + int32 pan = chn.nRealPan; Limit(pan, 0, 256); int32 realvol; if (m_PlayConfig.getUseGlobalPreAmp()) { - realvol = (pChn->nRealVolume * kChnMasterVol) / 128; + realvol = (chn.nRealVolume * kChnMasterVol) / 128; } else { // Extra attenuation required here if we're bypassing pre-amp. - realvol = (pChn->nRealVolume * kChnMasterVol) / 256; + realvol = (chn.nRealVolume * kChnMasterVol) / 256; } const ForcePanningMode panningMode = m_PlayConfig.getForcePanningMode(); @@ -2266,12 +2361,12 @@ bool CSoundFile::ReadNote() { if (pan < 128) { - pChn->newLeftVol = (realvol * 128) / 256; - pChn->newRightVol = (realvol * pan) / 256; + chn.newLeftVol = (realvol * 128) / 256; + chn.newRightVol = (realvol * pan) / 256; } else { - pChn->newLeftVol = (realvol * (256 - pan)) / 256; - pChn->newRightVol = (realvol * 128) / 256; + chn.newLeftVol = (realvol * (256 - pan)) / 256; + chn.newRightVol = (realvol * 128) / 256; } } else if(panningMode == forceFT2Panning) { @@ -2282,71 +2377,74 @@ bool CSoundFile::ReadNote() LimitMax(pan, 255); const int panL = pan > 0 ? XMPanningTable[256 - pan] : 65536; const int panR = XMPanningTable[pan]; - pChn->newLeftVol = (realvol * panL) / 65536; - pChn->newRightVol = (realvol * panR) / 65536; + chn.newLeftVol = (realvol * panL) / 65536; + chn.newRightVol = (realvol * panR) / 65536; } else { - pChn->newLeftVol = (realvol * (256 - pan)) / 256; - pChn->newRightVol = (realvol * pan) / 256; + chn.newLeftVol = (realvol * (256 - pan)) / 256; + chn.newRightVol = (realvol * pan) / 256; } } else { - pChn->newLeftVol = (pChn->nRealVolume * kChnMasterVol) / 256; - pChn->newRightVol = pChn->newLeftVol; + chn.newLeftVol = (chn.nRealVolume * kChnMasterVol) / 256; + chn.newRightVol = chn.newLeftVol; } // Clipping volumes - //if (pChn->nNewRightVol > 0xFFFF) pChn->nNewRightVol = 0xFFFF; - //if (pChn->nNewLeftVol > 0xFFFF) pChn->nNewLeftVol = 0xFFFF; + //if (chn.nNewRightVol > 0xFFFF) chn.nNewRightVol = 0xFFFF; + //if (chn.nNewLeftVol > 0xFFFF) chn.nNewLeftVol = 0xFFFF; - if(pChn->pModInstrument && IsKnownResamplingMode(pChn->pModInstrument->nResampling)) + if(chn.pModInstrument && Resampling::IsKnownMode(chn.pModInstrument->nResampling)) { // For defined resampling modes, use per-instrument resampling mode if set - pChn->resamplingMode = static_cast(pChn->pModInstrument->nResampling); - } else if(IsKnownResamplingMode(m_nResampling)) + chn.resamplingMode = static_cast(chn.pModInstrument->nResampling); + } else if(Resampling::IsKnownMode(m_nResampling)) { - pChn->resamplingMode = static_cast(m_nResampling); + chn.resamplingMode = static_cast(m_nResampling); } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga) { // Enforce Amiga resampler for Amiga modules - pChn->resamplingMode = SRCMODE_AMIGA; + chn.resamplingMode = SRCMODE_AMIGA; } else { // Default to global mixer settings - pChn->resamplingMode = static_cast(m_Resampler.m_Settings.SrcMode); + chn.resamplingMode = static_cast(m_Resampler.m_Settings.SrcMode); } - if(pChn->increment.IsUnity() && !(pChn->dwFlags[CHN_VIBRATO] || pChn->nAutoVibDepth || pChn->resamplingMode == SRCMODE_AMIGA)) + if(chn.increment.IsUnity() && !(chn.dwFlags[CHN_VIBRATO] || chn.nAutoVibDepth || chn.resamplingMode == SRCMODE_AMIGA)) { // Exact sample rate match, do not interpolate at all // - unless vibrato is applied, because in this case the constant enabling and disabling // of resampling can introduce clicks (this is easily observable with a sine sample // played at the mix rate). - pChn->resamplingMode = SRCMODE_NEAREST; + chn.resamplingMode = SRCMODE_NEAREST; } const int extraAttenuation = m_PlayConfig.getExtraSampleAttenuation(); - pChn->newLeftVol /= (1 << extraAttenuation); - pChn->newRightVol /= (1 << extraAttenuation); + chn.newLeftVol /= (1 << extraAttenuation); + chn.newRightVol /= (1 << extraAttenuation); // Dolby Pro-Logic Surround - if(pChn->dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels == 2) pChn->newRightVol = - pChn->newRightVol; + if(chn.dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels == 2) chn.newRightVol = - chn.newRightVol; // Checking Ping-Pong Loops - if(pChn->dwFlags[CHN_PINGPONGFLAG]) pChn->increment.Negate(); + if(chn.dwFlags[CHN_PINGPONGFLAG]) chn.increment.Negate(); // Setting up volume ramp - ProcessRamping(pChn); + ProcessRamping(chn); // Adding the channel in the channel list - m_PlayState.ChnMix[m_nMixChannels++] = nChn; + if(!chn.dwFlags[CHN_ADLIB]) + { + m_PlayState.ChnMix[m_nMixChannels++] = nChn; + } } else { - pChn->rightVol = pChn->leftVol = 0; - pChn->nLength = 0; + chn.rightVol = chn.leftVol = 0; + chn.nLength = 0; } - pChn->dwOldFlags = pChn->dwFlags; + chn.dwOldFlags = chn.dwFlags; } // If there are more channels being mixed than allowed, order them by volume and discard the most quiet ones @@ -2361,19 +2459,19 @@ bool CSoundFile::ReadNote() void CSoundFile::ProcessMacroOnChannel(CHANNELINDEX nChn) { - ModChannel *pChn = &m_PlayState.Chn[nChn]; + ModChannel &chn = m_PlayState.Chn[nChn]; if(nChn < GetNumChannels()) { // TODO evaluate per-plugin macros here //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_PAN]); //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_VOLUME]); - if((pChn->rowCommand.command == CMD_MIDI && m_SongFlags[SONG_FIRSTTICK]) || pChn->rowCommand.command == CMD_SMOOTHMIDI) + if((chn.rowCommand.command == CMD_MIDI && m_SongFlags[SONG_FIRSTTICK]) || chn.rowCommand.command == CMD_SMOOTHMIDI) { - if(pChn->rowCommand.param < 0x80) - ProcessMIDIMacro(nChn, (pChn->rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro], pChn->rowCommand.param); + if(chn.rowCommand.param < 0x80) + ProcessMIDIMacro(nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[chn.nActiveMacro], chn.rowCommand.param); else - ProcessMIDIMacro(nChn, (pChn->rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(pChn->rowCommand.param & 0x7F)], 0); + ProcessMIDIMacro(nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(chn.rowCommand.param & 0x7F)], 0); } } } @@ -2431,7 +2529,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) SendMIDINote(nChn, realNote, static_cast(chn.nVolume)); } else if(hasVolCommand) { - pPlugin->MidiCC(GetBestMidiChannel(nChn), MIDIEvents::MIDICC_Volume_Fine, vol, nChn); + pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Fine, vol, nChn); } return; } @@ -2475,8 +2573,8 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) break; case PLUGIN_VOLUMEHANDLING_MIDI: - if(hasVolCommand) pPlugin->MidiCC(GetBestMidiChannel(nChn), MIDIEvents::MIDICC_Volume_Coarse, std::min(127u, 2u * vol), nChn); - else pPlugin->MidiCC(GetBestMidiChannel(nChn), MIDIEvents::MIDICC_Volume_Coarse, static_cast(std::min(127u, 2u * defaultVolume)), nChn); + if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(127u, 2u * vol), nChn); + else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast(std::min(127u, 2u * defaultVolume)), nChn); break; } @@ -2487,7 +2585,7 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) template -MPT_FORCEINLINE void ApplyGlobalVolumeWithRamping(int *SoundBuffer, int *RearBuffer, int32 lCount, int32 m_nGlobalVolume, int32 step, int32 &m_nSamplesToGlobalVolRampDest, int32 &m_lHighResRampingGlobalVolume) +MPT_FORCEINLINE void ApplyGlobalVolumeWithRamping(int32 *SoundBuffer, int32 *RearBuffer, int32 lCount, int32 m_nGlobalVolume, int32 step, int32 &m_nSamplesToGlobalVolRampDest, int32 &m_lHighResRampingGlobalVolume) { const bool isStereo = (channels >= 2); const bool hasRear = (channels >= 4); @@ -2499,15 +2597,15 @@ MPT_FORCEINLINE void ApplyGlobalVolumeWithRamping(int *SoundBuffer, int *RearBuf m_lHighResRampingGlobalVolume += step; SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); MPT_CONSTANT_IF(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); - MPT_CONSTANT_IF(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); - MPT_CONSTANT_IF(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); + MPT_CONSTANT_IF(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); + MPT_CONSTANT_IF(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); m_nSamplesToGlobalVolRampDest--; } else { SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_nGlobalVolume, MAX_GLOBAL_VOLUME); MPT_CONSTANT_IF(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_nGlobalVolume, MAX_GLOBAL_VOLUME); - MPT_CONSTANT_IF(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); - MPT_CONSTANT_IF(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); + MPT_CONSTANT_IF(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); + MPT_CONSTANT_IF(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); m_lHighResRampingGlobalVolume = m_nGlobalVolume << VOLUMERAMPPRECISION; } SoundBuffer += isStereo ? 2 : 1; @@ -2555,7 +2653,7 @@ void CSoundFile::ProcessGlobalVolume(long lCount) // If step is too big (might cause click), extend ramp length. // Warning: This increases the volume ramp length by EXTREME amounts (factors of 100 are easily reachable) // compared to the user-defined setting, so this really should not be used! - int32 maxStep = std::max(50, (10000 / (m_PlayState.m_nGlobalVolumeRampAmount + 1))); + int32 maxStep = std::max(50, (10000 / (m_PlayState.m_nGlobalVolumeRampAmount + 1))); while(mpt::abs(step) > maxStep) { m_PlayState.m_nSamplesToGlobalVolRampDest += m_PlayState.m_nGlobalVolumeRampAmount; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.cpp index 2bc37bb72..3186ec88a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.cpp @@ -18,10 +18,7 @@ OPENMPT_NAMESPACE_BEGIN CSoundFilePlayConfig::CSoundFilePlayConfig() { setVSTiVolume(1.0f); -} - -CSoundFilePlayConfig::~CSoundFilePlayConfig() -{ + SetMixLevels(mixLevelsCompatible); } void CSoundFilePlayConfig::SetMixLevels(MixLevels mixLevelType) @@ -38,9 +35,9 @@ void CSoundFilePlayConfig::SetMixLevels(MixLevels mixLevelType) setUseGlobalPreAmp(true); setForcePanningMode(dontForcePanningMode); setDisplayDBValues(false); - setNormalSamplePreAmp(256.0); - setNormalVSTiVol(100.0); - setNormalGlobalVol(128.0); + setNormalSamplePreAmp(256.0f); + setNormalVSTiVol(100.0f); + setNormalGlobalVol(128.0f); setExtraSampleAttenuation(MIXING_ATTENUATION); break; @@ -54,9 +51,9 @@ void CSoundFilePlayConfig::SetMixLevels(MixLevels mixLevelType) setUseGlobalPreAmp(true); setForcePanningMode(dontForcePanningMode); setDisplayDBValues(false); - setNormalSamplePreAmp(256.0); - setNormalVSTiVol(100.0); - setNormalGlobalVol(128.0); + setNormalSamplePreAmp(256.0f); + setNormalVSTiVol(100.0f); + setNormalGlobalVol(128.0f); setExtraSampleAttenuation(MIXING_ATTENUATION); break; @@ -71,9 +68,9 @@ void CSoundFilePlayConfig::SetMixLevels(MixLevels mixLevelType) setUseGlobalPreAmp(true); setForcePanningMode(dontForcePanningMode); setDisplayDBValues(false); - setNormalSamplePreAmp(256.0); - setNormalVSTiVol(100.0); - setNormalGlobalVol(128.0); + setNormalSamplePreAmp(256.0f); + setNormalVSTiVol(100.0f); + setNormalGlobalVol(128.0f); setExtraSampleAttenuation(MIXING_ATTENUATION); break; @@ -89,9 +86,9 @@ void CSoundFilePlayConfig::SetMixLevels(MixLevels mixLevelType) setUseGlobalPreAmp(false); setForcePanningMode(forceSoftPanning); setDisplayDBValues(true); - setNormalSamplePreAmp(128.0); - setNormalVSTiVol(128.0); - setNormalGlobalVol(256.0); + setNormalSamplePreAmp(128.0f); + setNormalVSTiVol(128.0f); + setNormalGlobalVol(256.0f); setExtraSampleAttenuation(0); break; @@ -107,9 +104,9 @@ void CSoundFilePlayConfig::SetMixLevels(MixLevels mixLevelType) setUseGlobalPreAmp(false); setForcePanningMode(mixLevelType == mixLevelsCompatible ? forceNoSoftPanning : forceFT2Panning); setDisplayDBValues(true); - setNormalSamplePreAmp(mixLevelType == mixLevelsCompatible ? 256.0 : 192.0); - setNormalVSTiVol(mixLevelType == mixLevelsCompatible ? 256.0 : 192.0); - setNormalGlobalVol(256.0); + setNormalSamplePreAmp(mixLevelType == mixLevelsCompatible ? 256.0f : 192.0f); + setNormalVSTiVol(mixLevelType == mixLevelsCompatible ? 256.0f : 192.0f); + setNormalGlobalVol(256.0f); setExtraSampleAttenuation(1); break; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.h b/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.h index e8b48a5e6..60cbe38f6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SoundFilePlayConfig.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN enum @@ -48,8 +50,7 @@ enum ForcePanningMode class CSoundFilePlayConfig { public: - CSoundFilePlayConfig(void); - ~CSoundFilePlayConfig(void); + CSoundFilePlayConfig(); void SetMixLevels(MixLevels mixLevelType); @@ -81,12 +82,12 @@ public: void setDisplayDBValues(bool in) { m_displayDBValues = in; } // Values at which volumes are unchanged - double getNormalSamplePreAmp() const { return m_normalSamplePreAmp; } - void setNormalSamplePreAmp(double in) { m_normalSamplePreAmp = in; } - double getNormalVSTiVol() const { return m_normalVSTiVol; } - void setNormalVSTiVol(double in) { m_normalVSTiVol = in; } - double getNormalGlobalVol() const { return m_normalGlobalVol; } - void setNormalGlobalVol(double in) { m_normalGlobalVol = in; } + float getNormalSamplePreAmp() const { return m_normalSamplePreAmp; } + void setNormalSamplePreAmp(float in) { m_normalSamplePreAmp = in; } + float getNormalVSTiVol() const { return m_normalVSTiVol; } + void setNormalVSTiVol(float in) { m_normalVSTiVol = in; } + float getNormalGlobalVol() const { return m_normalGlobalVol; } + void setNormalGlobalVol(float in) { m_normalGlobalVol = in; } // Extra sample attenuation in bits int getExtraSampleAttenuation() const { return m_extraAttenuation; } @@ -99,9 +100,9 @@ protected: float m_VSTiAttenuation; float m_VSTiVolume; - double m_normalSamplePreAmp; - double m_normalVSTiVol; - double m_normalGlobalVol; + float m_normalSamplePreAmp; + float m_normalVSTiVol; + float m_normalGlobalVol; int m_extraAttenuation; ForcePanningMode m_forceSoftPanning; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp index 7a1606a14..bd4fa2bb7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp @@ -11,11 +11,11 @@ #include "stdafx.h" #include "Tables.h" -#include #include "Sndfile.h" #include "Resampler.h" #include "WindowedFIR.h" +#include OPENMPT_NAMESPACE_BEGIN @@ -24,16 +24,16 @@ OPENMPT_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////////////////////// // Note Name Tables -const char NoteNamesSharp[12][4] = +const MPT_UCHAR_TYPE NoteNamesSharp[12][4] = { - "C-", "C#", "D-", "D#", "E-", "F-", - "F#", "G-", "G#", "A-", "A#", "B-" + UL_("C-"), UL_("C#"), UL_("D-"), UL_("D#"), UL_("E-"), UL_("F-"), + UL_("F#"), UL_("G-"), UL_("G#"), UL_("A-"), UL_("A#"), UL_("B-") }; -const char NoteNamesFlat[12][4] = +const MPT_UCHAR_TYPE NoteNamesFlat[12][4] = { - "C-", "Db", "D-", "Eb", "E-", "F-", - "Gb", "G-", "Ab", "A-", "Bb", "B-" + UL_("C-"), UL_("Db"), UL_("D-"), UL_("Eb"), UL_("E-"), UL_("F-"), + UL_("Gb"), UL_("G-"), UL_("Ab"), UL_("A-"), UL_("Bb"), UL_("B-") }; @@ -47,60 +47,64 @@ struct ModFormatInfo const char *extension; // "mod" }; -// remember to also update libopenmpt/foo_openmpt.cpp (all other plugins read these dynamically) +// Note: Formats with identical extensions must be grouped together. static constexpr ModFormatInfo modFormatInfo[] = { - { MOD_TYPE_MOD, MPT_ULITERAL("ProTracker"), "mod" }, - { MOD_TYPE_S3M, MPT_ULITERAL("ScreamTracker 3"), "s3m" }, - { MOD_TYPE_XM, MPT_ULITERAL("FastTracker II"), "xm" }, - { MOD_TYPE_IT, MPT_ULITERAL("Impulse Tracker"), "it" }, -#ifdef MPT_EXTERNAL_SAMPLES - { MOD_TYPE_IT, MPT_ULITERAL("Impulse Tracker Project"), "itp" }, -#endif - { MOD_TYPE_MPT, MPT_ULITERAL("OpenMPT"), "mptm" }, - { MOD_TYPE_STM, MPT_ULITERAL("ScreamTracker 2"), "stm" }, - { MOD_TYPE_MOD, MPT_ULITERAL("ProTracker"), "pt36" }, - { MOD_TYPE_MOD, MPT_ULITERAL("NoiseTracker"), "nst" }, - { MOD_TYPE_MOD, MPT_ULITERAL("Soundtracker"), "m15" }, - { MOD_TYPE_MOD, MPT_ULITERAL("Soundtracker"), "stk" }, - { MOD_TYPE_MOD, MPT_ULITERAL("SoundTracker 2.6"), "st26" }, - { MOD_TYPE_MOD, MPT_ULITERAL("Ice Tracker"), "ice" }, - { MOD_TYPE_MOD, MPT_ULITERAL("Mod's Grave"), "wow" }, - { MOD_TYPE_ULT, MPT_ULITERAL("UltraTracker"), "ult" }, - { MOD_TYPE_669, MPT_ULITERAL("Composer 669 / UNIS 669"), "669" }, - { MOD_TYPE_MTM, MPT_ULITERAL("MultiTracker"), "mtm" }, - { MOD_TYPE_MED, MPT_ULITERAL("OctaMed"), "med" }, - { MOD_TYPE_FAR, MPT_ULITERAL("Farandole Composer"), "far" }, - { MOD_TYPE_MDL, MPT_ULITERAL("Digitrakker"), "mdl" }, - { MOD_TYPE_AMS, MPT_ULITERAL("Extreme's Tracker"), "ams" }, - { MOD_TYPE_AMS2, MPT_ULITERAL("Velvet Studio"), "ams" }, - { MOD_TYPE_DSM, MPT_ULITERAL("DSIK Format"), "dsm" }, - { MOD_TYPE_AMF, MPT_ULITERAL("DSMI"), "amf" }, - { MOD_TYPE_AMF0, MPT_ULITERAL("ASYLUM"), "amf" }, - { MOD_TYPE_OKT, MPT_ULITERAL("Oktalyzer"), "okt" }, - { MOD_TYPE_DMF, MPT_ULITERAL("X-Tracker"), "dmf" }, - { MOD_TYPE_PTM, MPT_ULITERAL("PolyTracker"), "ptm" }, - { MOD_TYPE_PSM, MPT_ULITERAL("Epic Megagames MASI"), "psm" }, - { MOD_TYPE_MT2, MPT_ULITERAL("MadTracker 2"), "mt2" }, - { MOD_TYPE_DBM, MPT_ULITERAL("DigiBooster Pro"), "dbm" }, - { MOD_TYPE_DIGI, MPT_ULITERAL("DigiBooster"), "digi" }, - { MOD_TYPE_IMF, MPT_ULITERAL("Imago Orpheus"), "imf" }, - { MOD_TYPE_J2B, MPT_ULITERAL("Galaxy Sound System"), "j2b" }, - { MOD_TYPE_PLM, MPT_ULITERAL("Disorder Tracker 2"), "plm" }, - { MOD_TYPE_SFX, MPT_ULITERAL("SoundFX"), "sfx" }, - { MOD_TYPE_SFX, MPT_ULITERAL("SoundFX"), "sfx2" }, - { MOD_TYPE_SFX, MPT_ULITERAL("MultiMedia Sound"), "mms" }, - { MOD_TYPE_STP, MPT_ULITERAL("Soundtracker Pro II"), "stp" }, - { MOD_TYPE_DTM, MPT_ULITERAL("Digital Tracker"), "dtm" }, + { MOD_TYPE_MPT, UL_("OpenMPT"), "mptm" }, + { MOD_TYPE_MOD, UL_("ProTracker"), "mod" }, + { MOD_TYPE_S3M, UL_("ScreamTracker 3"), "s3m" }, + { MOD_TYPE_XM, UL_("FastTracker 2"), "xm" }, + { MOD_TYPE_IT, UL_("Impulse Tracker"), "it" }, + { MOD_TYPE_669, UL_("Composer 669 / UNIS 669"), "669" }, + { MOD_TYPE_AMF0, UL_("ASYLUM Music Format"), "amf" }, + { MOD_TYPE_AMF, UL_("DSMI Advanced Music Format"), "amf" }, + { MOD_TYPE_AMS, UL_("Extreme's Tracker"), "ams" }, + { MOD_TYPE_AMS, UL_("Velvet Studio"), "ams" }, + { MOD_TYPE_S3M, UL_("CDFM / Composer 670"), "c67" }, + { MOD_TYPE_DBM, UL_("DigiBooster Pro"), "dbm" }, + { MOD_TYPE_DIGI, UL_("DigiBooster"), "digi" }, + { MOD_TYPE_DMF, UL_("X-Tracker"), "dmf" }, + { MOD_TYPE_DSM, UL_("DSIK Format"), "dsm" }, + { MOD_TYPE_DTM, UL_("Digital Tracker"), "dtm" }, + { MOD_TYPE_FAR, UL_("Farandole Composer"), "far" }, + { MOD_TYPE_IMF, UL_("Imago Orpheus"), "imf" }, + { MOD_TYPE_MOD, UL_("Ice Tracker"), "ice" }, +#ifdef MPT_EXTERNAL_SAMPLES + { MOD_TYPE_IT, UL_("Impulse Tracker Project"), "itp" }, +#endif + { MOD_TYPE_J2B, UL_("Galaxy Sound System"), "j2b" }, + { MOD_TYPE_MOD, UL_("Soundtracker"), "m15" }, + { MOD_TYPE_MDL, UL_("Digitrakker"), "mdl" }, + { MOD_TYPE_MED, UL_("OctaMED"), "med" }, + { MOD_TYPE_SFX, UL_("MultiMedia Sound"), "mms" }, + { MOD_TYPE_MT2, UL_("MadTracker 2"), "mt2" }, + { MOD_TYPE_MTM, UL_("MultiTracker"), "mtm" }, + { MOD_TYPE_MOD, UL_("NoiseTracker"), "nst" }, + { MOD_TYPE_OKT, UL_("Oktalyzer"), "okt" }, + { MOD_TYPE_PLM, UL_("Disorder Tracker 2"), "plm" }, + { MOD_TYPE_PSM, UL_("Epic Megagames MASI"), "psm" }, + { MOD_TYPE_MOD, UL_("ProTracker"), "pt36" }, + { MOD_TYPE_PTM, UL_("PolyTracker"), "ptm" }, + { MOD_TYPE_SFX, UL_("SoundFX"), "sfx" }, + { MOD_TYPE_SFX, UL_("SoundFX"), "sfx2" }, + { MOD_TYPE_MOD, UL_("SoundTracker 2.6"), "st26" }, + { MOD_TYPE_MOD, UL_("Soundtracker"), "stk" }, + { MOD_TYPE_STM, UL_("ScreamTracker 2"), "stm" }, + { MOD_TYPE_STP, UL_("Soundtracker Pro II"), "stp" }, + { MOD_TYPE_ULT, UL_("UltraTracker"), "ult" }, + { MOD_TYPE_MOD, UL_("Grave Composer"), "wow" }, + // converted formats (no MODTYPE) + { MOD_TYPE_NONE, UL_("General Digital Music"), "gdm" }, + { MOD_TYPE_NONE, UL_("Un4seen MO3"), "mo3" }, #ifndef NO_ARCHIVE_SUPPORT // Compressed modules - { MOD_TYPE_MOD, MPT_ULITERAL("ProTracker"), "mdz" }, - { MOD_TYPE_MOD, MPT_ULITERAL("ProTracker"), "mdr" }, - { MOD_TYPE_S3M, MPT_ULITERAL("ScreamTracker 3"), "s3z" }, - { MOD_TYPE_XM, MPT_ULITERAL("FastTracker II"), "xmz" }, - { MOD_TYPE_IT, MPT_ULITERAL("Impulse Tracker"), "itz" }, - { MOD_TYPE_MPT, MPT_ULITERAL("OpenMPT"), "mptmz" }, + { MOD_TYPE_MOD, UL_("Compressed ProTracker"), "mdz" }, + { MOD_TYPE_MOD, UL_("Compressed Module"), "mdr" }, + { MOD_TYPE_S3M, UL_("Compressed ScreamTracker 3"), "s3z" }, + { MOD_TYPE_XM, UL_("Compressed FastTracker 2"), "xmz" }, + { MOD_TYPE_IT, UL_("Compressed Impulse Tracker"), "itz" }, + { MOD_TYPE_MPT, UL_("Compressed OpenMPT"), "mptmz" }, #endif }; @@ -112,20 +116,17 @@ struct ModContainerInfo const char *extension; // "umx" }; -// remember to also update libopenmpt/libopenmpt_foobar2000.cpp (all other plugins read these dynamically) static constexpr ModContainerInfo modContainerInfo[] = { // Container formats - { MOD_CONTAINERTYPE_GDM, MPT_ULITERAL("General Digital Music"), "gdm" }, - { MOD_CONTAINERTYPE_UMX, MPT_ULITERAL("Unreal Music"), "umx" }, - { MOD_CONTAINERTYPE_MO3, MPT_ULITERAL("Un4seen MO3"), "mo3" }, - { MOD_CONTAINERTYPE_XPK, MPT_ULITERAL("XPK packed"), "xpk" }, - { MOD_CONTAINERTYPE_PP20, MPT_ULITERAL("PowerPack PP20"), "ppm" }, - { MOD_CONTAINERTYPE_MMCMP, MPT_ULITERAL("Music Module Compressor"), "mmcmp" } + { MOD_CONTAINERTYPE_UMX, UL_("Unreal Music"), "umx" }, + { MOD_CONTAINERTYPE_XPK, UL_("XPK packed"), "xpk" }, + { MOD_CONTAINERTYPE_PP20, UL_("PowerPack PP20"), "ppm" }, + { MOD_CONTAINERTYPE_MMCMP, UL_("Music Module Compressor"), "mmcmp" } #ifdef MODPLUG_TRACKER , - { MOD_CONTAINERTYPE_WAV, MPT_ULITERAL("Wave"), "wav" }, - { MOD_CONTAINERTYPE_UAX, MPT_ULITERAL("Unreal Sounds"), "uax" } + { MOD_CONTAINERTYPE_WAV, UL_("Wave"), "wav" }, + { MOD_CONTAINERTYPE_UAX, UL_("Unreal Sounds"), "uax" } #endif }; @@ -133,77 +134,13 @@ static constexpr ModContainerInfo modContainerInfo[] = #ifdef MODPLUG_TRACKER static constexpr ModFormatInfo otherFormatInfo[] = { - { MOD_TYPE_MID, MPT_ULITERAL("MIDI"), "mid" }, - { MOD_TYPE_MID, MPT_ULITERAL("MIDI"), "rmi" }, - { MOD_TYPE_MID, MPT_ULITERAL("MIDI"), "smf" } + { MOD_TYPE_MID, UL_("MIDI"), "mid" }, + { MOD_TYPE_MID, UL_("MIDI"), "rmi" }, + { MOD_TYPE_MID, UL_("MIDI"), "smf" } }; #endif -struct ModCharsetInfo -{ - MODTYPE type; - mpt::Charset charset; -}; - -static constexpr ModCharsetInfo ModCharsetInfos[] = -{ - // Amiga - { MOD_TYPE_OKT , mpt::CharsetISO8859_1 }, - { MOD_TYPE_DBM , mpt::CharsetISO8859_1 }, - { MOD_TYPE_DIGI, mpt::CharsetISO8859_1 }, - { MOD_TYPE_SFX , mpt::CharsetISO8859_1 }, - { MOD_TYPE_STP, mpt::CharsetISO8859_1 }, - // Amiga // DOS - { MOD_TYPE_MOD , mpt::CharsetISO8859_1 }, - { MOD_TYPE_MED , mpt::CharsetISO8859_1 }, - // Atari - { MOD_TYPE_DTM , mpt::CharsetISO8859_1 }, - // DOS - { MOD_TYPE_S3M , mpt::CharsetCP437 }, - { MOD_TYPE_XM , mpt::CharsetCP437 }, - { MOD_TYPE_MTM , mpt::CharsetCP437 }, - { MOD_TYPE_IT , mpt::CharsetCP437 }, - { MOD_TYPE_669 , mpt::CharsetCP437 }, - { MOD_TYPE_STM , mpt::CharsetCP437 }, - { MOD_TYPE_FAR , mpt::CharsetCP437 }, - { MOD_TYPE_AMF , mpt::CharsetCP437 }, - { MOD_TYPE_AMF0, mpt::CharsetCP437 }, - { MOD_TYPE_MDL , mpt::CharsetCP437 }, - { MOD_TYPE_DMF , mpt::CharsetCP437 }, - { MOD_TYPE_PTM , mpt::CharsetCP437 }, - { MOD_TYPE_PSM , mpt::CharsetCP437 }, - { MOD_TYPE_J2B , mpt::CharsetCP437 }, - { MOD_TYPE_IMF , mpt::CharsetCP437 }, - { MOD_TYPE_ULT , mpt::CharsetCP437 }, - { MOD_TYPE_AMS , mpt::CharsetCP437 }, - { MOD_TYPE_AMS2, mpt::CharsetCP437 }, - { MOD_TYPE_DSM , mpt::CharsetCP437 }, - { MOD_TYPE_PLM , mpt::CharsetCP437 }, - // Windows - { MOD_TYPE_MT2 , mpt::CharsetWindows1252}, - { MOD_TYPE_MPT , mpt::CharsetWindows1252}, - // random stuff - { MOD_TYPE_MID , mpt::CharsetASCII }, -}; - - -mpt::Charset CSoundFile::GetCharsetFromModType(MODTYPE modType) -{ - // This is just a rough heuristic. - // It could be improved by adjusting the charset according to the tracker that had been used to save the file. - for(const auto &charsetInfo : ModCharsetInfos) - { - if(charsetInfo.type == modType) - { - return charsetInfo.charset; - } - } - // fallback - return mpt::CharsetASCII; -} - - std::vector CSoundFile::GetSupportedExtensions(bool otherFormats) { std::vector exts; @@ -274,19 +211,6 @@ bool CSoundFile::IsExtensionSupported(const char *ext) } -mpt::ustring CSoundFile::ModTypeToString(MODTYPE modtype) -{ - for(const auto &formatInfo : modFormatInfo) - { - if(formatInfo.format & modtype) - { - return mpt::ToUnicode(mpt::CharsetUTF8, formatInfo.extension); - } - } - return mpt::ustring(); -} - - mpt::ustring CSoundFile::ModContainerTypeToString(MODCONTAINERTYPE containertype) { for(const auto &containerInfo : modContainerInfo) @@ -300,34 +224,6 @@ mpt::ustring CSoundFile::ModContainerTypeToString(MODCONTAINERTYPE containertype } -mpt::ustring CSoundFile::ModTypeToTracker(MODTYPE modtype) -{ - std::set retvals; - mpt::ustring retval; - if(modtype == MOD_TYPE_MOD) - { // special case MOD - return MPT_USTRING("Generic Amiga / PC MOD file"); - } - for(const auto &formatInfo : modFormatInfo) - { - if(formatInfo.format & modtype) - { - mpt::ustring name = formatInfo.name; - if(retvals.find(name) == retvals.end()) - { - retvals.insert(name); - if(!retval.empty()) - { - retval += MPT_USTRING(" / "); - } - retval += name; - } - } - } - return retval; -} - - mpt::ustring CSoundFile::ModContainerTypeToTracker(MODCONTAINERTYPE containertype) { std::set retvals; @@ -337,12 +233,11 @@ mpt::ustring CSoundFile::ModContainerTypeToTracker(MODCONTAINERTYPE containertyp if(containerInfo.format == containertype) { mpt::ustring name = containerInfo.name; - if(retvals.find(name) == retvals.end()) + if(retvals.insert(name).second) { - retvals.insert(name); if(!retval.empty()) { - retval += MPT_USTRING(" / "); + retval += U_(" / "); } retval += name; } @@ -361,9 +256,10 @@ const uint8 ImpulseTrackerPortaVolCmd[16] = 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -// Period table for Protracker octaves 0-5: -const uint16 ProTrackerPeriodTable[6*12] = +// Period table for ProTracker octaves (1-7 in FastTracker 2, also used for file I/O): +const uint16 ProTrackerPeriodTable[7*12] = { + 2*1712,2*1616,2*1524,2*1440,2*1356,2*1280,2*1208,2*1140,2*1076,2*1016,2*960,2*906, 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, 856,808,762,720,678,640,604,570,538,508,480,453, 428,404,381,360,339,320,302,285,269,254,240,226, @@ -401,11 +297,9 @@ const uint8 ModEFxTable[16] = }; // S3M C-4 periods -const uint16 FreqS3MTable[16] = +const uint16 FreqS3MTable[12] = { - 1712,1616,1524,1440,1356,1280, - 1208,1140,1076,1016,960,907, - 0,0,0,0 + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907 }; // S3M FineTune frequencies @@ -856,41 +750,6 @@ static void getsinc(SINC_TYPE *psinc, double beta, double lowpass_factor) } } -#if 0 - -// this code is currently unused - -static double GetSpline(double x, double c0, double c1, double c2, double c3) -{ - double Xo = c1; - double Xa = c0 - Xo; - double Xb = c2 - Xo; - double Ux = (Xb-Xa)/2; - double Vx = (c3 - Xo)/2; - double a = Vx+Ux-2*Xb; - double b = 3*Xb-2*Ux-Vx; - return (((a*x+b)*x)+Ux)*x+Xo; -} - - -static void getdownsample2x(short int *psinc) -{ - for (int i=0; i OPENMPT_NAMESPACE_BEGIN @@ -33,7 +35,7 @@ struct FileTags mpt::ustring genre; - FileTags(); + void SetEncoder(); }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.cpp index 5bf021d84..895430d32 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.cpp @@ -15,6 +15,14 @@ OPENMPT_NAMESPACE_BEGIN +bool UMXFileHeader::IsValid() const +{ + return !std::memcmp(magic, "\xC1\x83\x2A\x9E", 4) + && nameCount != 0 + && exportCount != 0 + && importCount != 0; +} + // Read compressed unreal integers - similar to MIDI integers, but signed values are possible. template diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.h index 856d26698..124d7b975 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UMXTools.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN @@ -27,6 +29,8 @@ struct UMXFileHeader uint32le exportOffset; uint32le importCount; uint32le importOffset; + + bool IsValid() const; }; MPT_BINARY_STRUCT(UMXFileHeader, 36) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp index ddeef2a26..38dbdc3f1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp @@ -10,7 +10,7 @@ #include "stdafx.h" #include "Sndfile.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" #include "../common/version.h" @@ -21,7 +21,6 @@ struct UpgradePatternData { UpgradePatternData(CSoundFile &sf) : sndFile(sf) - , chn(0) , compatPlay(sf.m_playBehaviour[MSF_COMPATIBLE_PLAY]) { } void operator() (ModCommand &m) @@ -222,7 +221,7 @@ struct UpgradePatternData } const CSoundFile &sndFile; - CHANNELINDEX chn; + CHANNELINDEX chn = 0; const bool compatPlay; }; @@ -336,14 +335,14 @@ void CSoundFile::UpgradeModule() if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 01)) { // Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name) - for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++) + for(auto &plugin : m_MixPlugins) { #if defined(MODPLUG_TRACKER) - const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, m_MixPlugins[i].Info.szLibraryName); + const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, plugin.Info.szLibraryName); #else - const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetWindows1252, m_MixPlugins[i].Info.szLibraryName); + const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetWindows1252, plugin.Info.szLibraryName); #endif - mpt::String::Copy(m_MixPlugins[i].Info.szLibraryName, name); + mpt::String::Copy(plugin.Info.szLibraryName, name); } } #endif // NO_PLUGINS @@ -369,9 +368,9 @@ void CSoundFile::UpgradeModule() if(Instruments[i] != nullptr && Instruments[i]->nVolSwing != 0 && Instruments[i]->nMidiChannel != MidiNoChannel) { bool hasSample = false; - for(size_t k = 0; k < CountOf(Instruments[k]->Keyboard); k++) + for(auto smp : Instruments[i]->Keyboard) { - if(Instruments[i]->Keyboard[k] != 0) + if(smp != 0) { hasSample = true; break; @@ -402,6 +401,18 @@ void CSoundFile::UpgradeModule() } } + if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 00, 12)) + { + for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) + { + if(Instruments[i]->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET) + { + m_playBehaviour.set(kLegacyReleaseNode); + break; + } + } + } + Patterns.ForEachModCommand(UpgradePatternData(*this)); // Convert compatibility flags @@ -411,7 +422,7 @@ void CSoundFile::UpgradeModule() struct PlayBehaviourVersion { PlayBehaviour behaviour; - MptVersion::VersionNum version; + Version version; }; if(compatModeIT && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) @@ -466,7 +477,7 @@ void CSoundFile::UpgradeModule() for(const auto &b : behaviours) { - m_playBehaviour.set(b.behaviour, (m_dwLastSavedWithVersion >= b.version || m_dwLastSavedWithVersion == (b.version & 0xFFFF0000))); + m_playBehaviour.set(b.behaviour, (m_dwLastSavedWithVersion >= b.version || m_dwLastSavedWithVersion == b.version.Masked(0xFFFF0000u))); } } else if(compatModeXM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) { @@ -522,10 +533,10 @@ void CSoundFile::UpgradeModule() for(const auto &b : behaviours) { - if(m_dwLastSavedWithVersion < (b.version & 0xFFFF0000)) + if(m_dwLastSavedWithVersion < b.version.Masked(0xFFFF0000u)) m_playBehaviour.reset(b.behaviour); // Full version information available, i.e. not compatibility-exported. - else if(m_dwLastSavedWithVersion > (b.version & 0xFFFF0000) && m_dwLastSavedWithVersion < b.version) + else if(m_dwLastSavedWithVersion > b.version.Masked(0xFFFF0000u) && m_dwLastSavedWithVersion < b.version) m_playBehaviour.reset(b.behaviour); } } else if(GetType() == MOD_TYPE_XM) @@ -537,6 +548,8 @@ void CSoundFile::UpgradeModule() { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, { kFT2TremoloRampWaveform, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, { kFT2PortaUpDownMemory, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, + { kFT2PanSustainRelease, MAKE_VERSION_NUMERIC(1, 28, 00, 09) }, + { kFT2NoteDelayWithoutInstr, MAKE_VERSION_NUMERIC(1, 28, 00, 44) }, }; for(const auto &b : behaviours) @@ -549,13 +562,14 @@ void CSoundFile::UpgradeModule() // We do not store any of these flags in S3M files. static constexpr PlayBehaviourVersion behaviours[] = { - { kST3NoMutedChannels, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kST3EffectMemory, MAKE_VERSION_NUMERIC(1, 20, 00, 00) }, - { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 00) }, - { kST3PortaSampleChange, MAKE_VERSION_NUMERIC(1, 22, 00, 00) }, - { kST3VibratoMemory, MAKE_VERSION_NUMERIC(1, 26, 00, 00) }, - { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 26, 00, 00) }, - { KST3PortaAfterArpeggio, MAKE_VERSION_NUMERIC(1, 27, 00, 00) }, + { kST3NoMutedChannels, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, + { kST3EffectMemory, MAKE_VERSION_NUMERIC(1, 20, 00, 00) }, + { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 00) }, + { kST3PortaSampleChange, MAKE_VERSION_NUMERIC(1, 22, 00, 00) }, + { kST3VibratoMemory, MAKE_VERSION_NUMERIC(1, 26, 00, 00) }, + { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 26, 00, 00) }, + { KST3PortaAfterArpeggio, MAKE_VERSION_NUMERIC(1, 27, 00, 00) }, + { kST3OffsetWithoutInstrument, MAKE_VERSION_NUMERIC(1, 28, 00, 00) }, }; for(const auto &b : behaviours) @@ -565,6 +579,12 @@ void CSoundFile::UpgradeModule() } } + if(GetType() == MOD_TYPE_XM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00)) + { + // This bug was introduced sometime between 1.18.03.00 and 1.19.01.00 + m_playBehaviour.set(kFT2NoteDelayWithoutInstr); + } + if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 27, 00, 27) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 27, 00, 49)) { // OpenMPT 1.27 inserted some IT/FT2 flags before the S3M flags that are never saved to files anyway, to keep the flag IDs a bit more compact. @@ -597,7 +617,22 @@ void CSoundFile::UpgradeModule() // Frequency slides were always in Hz rather than periods in this version range. m_playBehaviour.set(kHertzInLinearMode); } + + if(m_playBehaviour[kITEnvelopePositionHandling] + && m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 23, 01, 02) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 00, 43)) + { + // Bug that effectively clamped the release node to the sustain end + for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) + { + if(Instruments[i]->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET + && Instruments[i]->VolEnv.dwFlags[ENV_SUSTAIN] + && Instruments[i]->VolEnv.nReleaseNode > Instruments[i]->VolEnv.nSustainEnd) + { + m_playBehaviour.set(kReleaseNodePastSustainBug); + break; + } + } + } } - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp index d83935b94..9640159af 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp @@ -12,6 +12,7 @@ #include "Loaders.h" #include "WAVTools.h" #include "Tagging.h" +#include "../common/version.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif @@ -29,6 +30,7 @@ WAVReader::WAVReader(FileReader &inputFile) : file(inputFile) file.Rewind(); RIFFHeader fileHeader; + codePage = 28591; // ISO 8859-1 isDLS = false; subFormat = 0; mayBeCoolEdit16_8 = false; @@ -118,6 +120,9 @@ WAVReader::WAVReader(FileReader &inputFile) : file(inputFile) } } + // Determine string encoding + codePage = GetFileCodePage(chunks); + // Check for loop points, texts, etc... FindMetadataChunks(chunks); @@ -147,13 +152,49 @@ void WAVReader::FindMetadataChunks(ChunkReader::ChunkList &chunks) } -void WAVReader::ApplySampleSettings(ModSample &sample, char (&sampleName)[MAX_SAMPLENAME]) +uint16 WAVReader::GetFileCodePage(ChunkReader::ChunkList &chunks) +{ + FileReader csetChunk = chunks.GetChunk(RIFFChunk::idCSET); + if(!csetChunk.IsValid()) + { + FileReader iSFT = infoChunk.GetChunk(RIFFChunk::idISFT); + if(iSFT.ReadMagic("OpenMPT")) + { + std::string versionString; + iSFT.ReadString(versionString, iSFT.BytesLeft()); + versionString = mpt::String::Trim(versionString); + Version version = Version::Parse(mpt::ToUnicode(mpt::CharsetISO8859_1, versionString)); + if(version && version < MAKE_VERSION_NUMERIC(1,28,00,02)) + { + return 1252; // mpt::CharsetWindows1252; // OpenMPT up to and including 1.28.00.01 wrote metadata in windows-1252 encoding + } else + { + return 28591; // mpt::CharsetISO8859_1; // as per spec + } + } else + { + return 28591; // mpt::CharsetISO8859_1; // as per spec + } + } + if(!csetChunk.CanRead(2)) + { + // chunk not parsable + return 28591; // mpt::CharsetISO8859_1; + } + uint16 codepage = csetChunk.ReadUint16LE(); + return codepage; +} + + +void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, char (&sampleName)[MAX_SAMPLENAME]) { // Read sample name FileReader textChunk = infoChunk.GetChunk(RIFFChunk::idINAM); if(textChunk.IsValid()) { - textChunk.ReadString(sampleName, textChunk.GetLength()); + std::string sampleNameEncoded; + textChunk.ReadString(sampleNameEncoded, textChunk.GetLength()); + mpt::String::Copy(sampleName, mpt::ToCharset(sampleCharset, mpt::ToUnicode(codePage, mpt::CharsetWindows1252, sampleNameEncoded))); } if(isDLS) { @@ -226,7 +267,7 @@ void WAVReader::ApplySampleSettings(ModSample &sample, char (&sampleName)[MAX_SA sample.nPan = std::min(mptInfo.defaultPan, 256); sample.nVolume = std::min(mptInfo.defaultVolume, 256); sample.nGlobalVol = std::min(mptInfo.globalVolume, 64); - sample.nVibType = mptInfo.vibratoType; + sample.nVibType = static_cast(mptInfo.vibratoType.get()); sample.nVibSweep = mptInfo.vibratoSweep; sample.nVibDepth = mptInfo.vibratoDepth; sample.nVibRate = mptInfo.vibratoRate; @@ -234,6 +275,9 @@ void WAVReader::ApplySampleSettings(ModSample &sample, char (&sampleName)[MAX_SA if(xtraChunk.CanRead(MAX_SAMPLENAME)) { // Name present (clipboard only) + // FIXME: When modules can have individual encoding in OpenMPT or when + // internal metadata gets converted to Unicode, we must adjust this to + // also specify encoding. xtraChunk.ReadString(sampleName, MAX_SAMPLENAME); xtraChunk.ReadString(sample.filename, xtraChunk.BytesLeft()); } @@ -291,38 +335,30 @@ void WAVSampleLoop::ConvertToWAV(SmpLength start, SmpLength end, bool bidi) // Output to stream: Initialize with std::ostream*. -WAVWriter::WAVWriter(std::ostream *stream) : s(nullptr), memory(nullptr), memSize(0) +WAVWriter::WAVWriter(std::ostream *stream) : s(stream) { - s = stream; - Init(); + // Skip file header for now + Seek(sizeof(RIFFHeader)); } // Output to clipboard: Initialize with pointer to memory and size of reserved memory. -WAVWriter::WAVWriter(void *mem, size_t size) : s(nullptr), memory(static_cast(mem)), memSize(size) +WAVWriter::WAVWriter(mpt::byte_span data) : memory(data) { - Init(); -} - - -WAVWriter::~WAVWriter() -{ - Finalize(); -} - - -// Reset all file variables. -void WAVWriter::Init() -{ - chunkStartPos = 0; - position = 0; - totalSize = 0; - // Skip file header for now Seek(sizeof(RIFFHeader)); } +WAVWriter::~WAVWriter() noexcept(false) +{ + if(!s || s->good()) + { + Finalize(); + } +} + + // Finalize the file by closing the last open chunk and updating the file header. Returns total size of file. size_t WAVWriter::Finalize() { @@ -337,7 +373,7 @@ size_t WAVWriter::Finalize() Write(fileHeader); s = nullptr; - memory = nullptr; + memory = {}; return totalSize; } @@ -360,7 +396,7 @@ void WAVWriter::FinalizeChunk() if(chunkStartPos != 0) { const size_t chunkSize = position - (chunkStartPos + sizeof(RIFFChunk)); - chunkHeader.length = chunkSize; + chunkHeader.length = mpt::saturate_cast(chunkSize); size_t curPos = position; Seek(chunkStartPos); @@ -398,11 +434,11 @@ void WAVWriter::Write(const void *data, size_t numBytes) if(s != nullptr) { s->write(static_cast(data), numBytes); - } else if(memory != nullptr) + } else if(!memory.empty()) { - if(position <= memSize && numBytes <= memSize - position) + if(position <= memory.size() && numBytes <= memory.size() - position) { - memcpy(memory + position, data, numBytes); + memcpy(memory.data() + position, data, numBytes); } else { // Should never happen - did we calculate a wrong memory size? @@ -463,6 +499,12 @@ void WAVWriter::WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChanne // Write text tags to the file. void WAVWriter::WriteMetatags(const FileTags &tags) { + StartChunk(RIFFChunk::idCSET); + Write(mpt::as_le(uint16(65001))); // code page (UTF-8) + Write(mpt::as_le(uint16(0))); // country code (unset) + Write(mpt::as_le(uint16(0))); // language (unset) + Write(mpt::as_le(uint16(0))); // dialect (unset) + StartChunk(RIFFChunk::idLIST); const char info[] = { 'I', 'N', 'F', 'O' }; WriteArray(info); @@ -483,10 +525,11 @@ void WAVWriter::WriteMetatags(const FileTags &tags) // Write a single tag into a open idLIST chunk void WAVWriter::WriteTag(RIFFChunk::ChunkIdentifiers id, const mpt::ustring &utext) { - std::string text = mpt::ToCharset(mpt::CharsetWindows1252, utext); + std::string text = mpt::ToCharset(mpt::CharsetUTF8, utext); + text = text.substr(0, uint32_max - 1u); if(!text.empty()) { - const size_t length = text.length() + 1; + const uint32 length = mpt::saturate_cast(text.length() + 1); RIFFChunk chunk; chunk.id = static_cast(id); @@ -552,8 +595,7 @@ void WAVWriter::WriteCueInformation(const ModSample &sample) { StartChunk(RIFFChunk::idcue_); { - const uint32 numPoints = SwapBytesLE(static_cast(CountOf(sample.cues))); - Write(numPoints); + Write(mpt::as_le(static_cast(CountOf(sample.cues)))); } for(uint32 i = 0; i < CountOf(sample.cues); i++) { @@ -576,6 +618,11 @@ void WAVWriter::WriteExtraInformation(const ModSample &sample, MODTYPE modType, if(sampleName != nullptr) { // Write sample name (clipboard only) + + // FIXME: When modules can have individual encoding in OpenMPT or when + // internal metadata gets converted to Unicode, we must adjust this to + // also specify encoding. + char name[MAX_SAMPLENAME]; mpt::String::Write(name, sampleName, MAX_SAMPLENAME); WriteArray(name); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h index 3747c4dff..8d3e6e4e0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "ChunkReader.h" #include "Loaders.h" #include "../common/mptUUID.h" @@ -24,10 +26,10 @@ struct RIFFHeader // 32-Bit chunk identifiers enum RIFFMagic { - idRIFF = MAGIC4LE('R','I','F','F'), // magic for WAV files - idLIST = MAGIC4LE('L','I','S','T'), // magic for samples in DLS banks - idWAVE = MAGIC4LE('W','A','V','E'), // type for WAV files - idwave = MAGIC4LE('w','a','v','e'), // type for samples in DLS banks + idRIFF = MagicLE("RIFF"), // magic for WAV files + idLIST = MagicLE("LIST"), // magic for samples in DLS banks + idWAVE = MagicLE("WAVE"), // type for WAV files + idwave = MagicLE("wave"), // type for samples in DLS banks }; uint32le magic; // RIFF (in WAV files) or LIST (in DLS banks) @@ -44,33 +46,34 @@ struct RIFFChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idfmt_ = MAGIC4LE('f','m','t',' '), // Sample format information - iddata = MAGIC4LE('d','a','t','a'), // Sample data - idpcm_ = MAGIC4LE('p','c','m',' '), // IMA ADPCM samples - idfact = MAGIC4LE('f','a','c','t'), // Compressed samples - idsmpl = MAGIC4LE('s','m','p','l'), // Sampler and loop information - idinst = MAGIC4LE('i','n','s','t'), // Instrument information - idLIST = MAGIC4LE('L','I','S','T'), // List of chunks - idxtra = MAGIC4LE('x','t','r','a'), // OpenMPT extra infomration - idcue_ = MAGIC4LE('c','u','e',' '), // Cue points - idwsmp = MAGIC4LE('w','s','m','p'), // DLS bank samples + idfmt_ = MagicLE("fmt "), // Sample format information + iddata = MagicLE("data"), // Sample data + idpcm_ = MagicLE("pcm "), // IMA ADPCM samples + idfact = MagicLE("fact"), // Compressed samples + idsmpl = MagicLE("smpl"), // Sampler and loop information + idinst = MagicLE("inst"), // Instrument information + idLIST = MagicLE("LIST"), // List of chunks + idxtra = MagicLE("xtra"), // OpenMPT extra infomration + idcue_ = MagicLE("cue "), // Cue points + idwsmp = MagicLE("wsmp"), // DLS bank samples + idCSET = MagicLE("CSET"), // Character Set id____ = 0x00000000, // Found when loading buggy MPT samples // Identifiers in "LIST" chunk - idINAM = MAGIC4LE('I','N','A','M'), // title - idISFT = MAGIC4LE('I','S','F','T'), // software - idICOP = MAGIC4LE('I','C','O','P'), // copyright - idIART = MAGIC4LE('I','A','R','T'), // artist - idIPRD = MAGIC4LE('I','P','R','D'), // product (album) - idICMT = MAGIC4LE('I','C','M','T'), // comment - idIENG = MAGIC4LE('I','E','N','G'), // engineer - idISBJ = MAGIC4LE('I','S','B','J'), // subject - idIGNR = MAGIC4LE('I','G','N','R'), // genre - idICRD = MAGIC4LE('I','C','R','D'), // date created + idINAM = MagicLE("INAM"), // title + idISFT = MagicLE("ISFT"), // software + idICOP = MagicLE("ICOP"), // copyright + idIART = MagicLE("IART"), // artist + idIPRD = MagicLE("IPRD"), // product (album) + idICMT = MagicLE("ICMT"), // comment + idIENG = MagicLE("IENG"), // engineer + idISBJ = MagicLE("ISBJ"), // subject + idIGNR = MagicLE("IGNR"), // genre + idICRD = MagicLE("ICRD"), // date created - idYEAR = MAGIC4LE('Y','E','A','R'), // year - idTRCK = MAGIC4LE('T','R','C','K'), // track number - idTURL = MAGIC4LE('T','U','R','L'), // url + idYEAR = MagicLE("YEAR"), // year + idTRCK = MagicLE("TRCK"), // track number + idTURL = MagicLE("TURL"), // url }; uint32le id; // See ChunkIdentifiers @@ -288,9 +291,12 @@ protected: FileReader::off_t sampleLength; WAVFormatChunk formatInfo; uint16 subFormat; + uint16 codePage; bool isDLS; bool mayBeCoolEdit16_8; + uint16 GetFileCodePage(ChunkReader::ChunkList &chunks); + public: WAVReader(FileReader &inputFile); @@ -316,7 +322,7 @@ public: SmpLength GetSampleLength() const { return mpt::saturate_cast(sampleLength); } // Apply sample settings from file (loop points, MPT extra settings, ...) to a sample. - void ApplySampleSettings(ModSample &sample, char (&sampleName)[MAX_SAMPLENAME]); + void ApplySampleSettings(ModSample &sample, mpt::Charset charset, char (&sampleCharset)[MAX_SAMPLENAME]); }; @@ -326,30 +332,29 @@ class WAVWriter { protected: // When writing to a stream: Stream pointer - std::ostream *s; + std::ostream *s = nullptr; // When writing to memory: Memory address + length - uint8 *memory; - size_t memSize; + mpt::byte_span memory; // Cursor position - size_t position; + size_t position = 0; // Total number of bytes written to file / memory - size_t totalSize; + size_t totalSize = 0; // Currently written chunk - size_t chunkStartPos; + size_t chunkStartPos = 0; RIFFChunk chunkHeader; public: // Output to stream: Initialize with std::ostream*. WAVWriter(std::ostream *stream); // Output to clipboard: Initialize with pointer to memory and size of reserved memory. - WAVWriter(void *mem, size_t size); + WAVWriter(mpt::byte_span data); - ~WAVWriter(); + ~WAVWriter() noexcept(false); // Check if anything can be written to the file. - bool IsValid() const { return s != nullptr || memory != nullptr; } + bool IsValid() const { return s != nullptr || !memory.empty(); } // Finalize the file by closing the last open chunk and updating the file header. Returns total size of file. size_t Finalize(); @@ -368,6 +373,7 @@ public: template void Write(const T &data) { + MPT_STATIC_ASSERT((mpt::is_binary_safe::value)); Write(&data, sizeof(T)); } @@ -396,7 +402,6 @@ public: void WriteExtraInformation(const ModSample &sample, MODTYPE modType, const char *sampleName = nullptr); protected: - void Init(); // Seek to a position in file. void Seek(size_t pos); // End current chunk by updating the chunk header and writing a padding byte if necessary. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.cpp index c8db29672..6c38a4f27 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.cpp @@ -20,11 +20,11 @@ OPENMPT_NAMESPACE_BEGIN // -> !!! stolen from modplug-xmms sourceforge project !!! -float CWindowedFIR::coef( int _PCnr, float _POfs, float _PCut, int _PWidth, int _PType ) //float _PPos, float _PFc, int _PLen ) +double CWindowedFIR::coef( int _PCnr, double _POfs, double _PCut, int _PWidth, int _PType ) //float _PPos, float _PFc, int _PLen ) { double _LWidthM1 = _PWidth-1; double _LWidthM1Half = 0.5*_LWidthM1; - double _LPosU = ((double)_PCnr - _POfs); + double _LPosU = (_PCnr - _POfs); double _LPos = _LPosU-_LWidthM1Half; double _LPIdl = 2.0*M_zPI/_LWidthM1; double _LWc,_LSi; @@ -67,32 +67,32 @@ float CWindowedFIR::coef( int _PCnr, float _POfs, float _PCut, int _PWidth, int _LPos *= M_zPI; _LSi = sin(_PCut*_LPos)/_LPos; } - return (float)(_LWc*_LSi); + return (_LWc*_LSi); } void CWindowedFIR::InitTable(double WFIRCutoff, uint8 WFIRType) { int _LPcl; - float _LPcllen = (float)(1L<(WFIRCutoff); + double _LPcllen = (double)(1L<WFIR_QUANTSCALE)?WFIR_QUANTSCALE:_LCoef) ); #else - float _LCoef = _LCoefs[_LCc] * _LGain; - lut[_LIdx+_LCc] = _LCoef; + double _LCoef = _LCoefs[_LCc] * _LGain; + lut[_LIdx+_LCc] = (float)_LCoef; #endif // MPT_INTMIXER } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.h b/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.h index 83d1a9ff8..28cadbaef 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WindowedFIR.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + #include "Mixer.h" OPENMPT_NAMESPACE_BEGIN @@ -34,7 +36,7 @@ OPENMPT_NAMESPACE_BEGIN #ifdef MPT_INTMIXER // quantizer scale of window coefs - only required for integer mixing #define WFIR_QUANTBITS 15 -#define WFIR_QUANTSCALE float(1L<(std::min(12u, mptEnv.size())); + numPoints = static_cast(std::min(12u, mptEnv.size())); // Envelope Data for(uint8 i = 0; i < numPoints; i++) @@ -145,7 +145,7 @@ void XMInstrument::ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoi if(i > 0 && mptEnv[i].tick < mptEnv[i - 1].tick) { // libmikmod code says: "Some broken XM editing program will only save the low byte of the position - // value. Try to compensate by adding the missing high byte" - I guess that's what this code is for. + // value. Try to compensate by adding the missing high byte." // Note: It appears that MPT 1.07's XI instrument saver omitted the high byte of envelope nodes. // This might be the source for some broken envelopes in IT and XM files. @@ -221,7 +221,7 @@ void XMInstrument::ApplyAutoVibratoToXM(const ModSample &mptSmp, MODTYPE fromTyp // Apply auto-vibrato settings from file to a sample. void XMInstrument::ApplyAutoVibratoToMPT(ModSample &mptSmp) const { - mptSmp.nVibType = vibType; + mptSmp.nVibType = static_cast(vibType.get()); mptSmp.nVibSweep = vibSweep; mptSmp.nVibDepth = vibDepth; mptSmp.nVibRate = vibRate; @@ -290,7 +290,7 @@ void XIInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibi mpt::String::Write(name, mptIns.name); eof = 0x1A; - const std::string openMptTrackerName = MptVersion::GetOpenMPTVersionStr(); + const std::string openMptTrackerName = mpt::ToCharset(mpt::CharsetCP437, Version::Current().GetOpenMPTVersionString()); mpt::String::Write(trackerName, openMptTrackerName); version = 0x102; @@ -410,8 +410,6 @@ void XMSample::ConvertToMPT(ModSample &mptSmp) const } } - mptSmp.SanitizeLoops(); - strcpy(mptSmp.filename, ""); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h index a51913578..7ea408def 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp index bdfcb34ff..0e923b59b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp @@ -22,11 +22,14 @@ #endif +//#define J2B_LOG + + OPENMPT_NAMESPACE_BEGIN // First off, a nice vibrato translation LUT. -static const uint8 j2bAutoVibratoTrans[] = +static const VibratoType j2bAutoVibratoTrans[] = { VIB_SINE, VIB_SQUARE, VIB_RAMP_UP, VIB_RAMP_DOWN, VIB_RANDOM, }; @@ -37,8 +40,10 @@ struct J2BFileHeader { // Magic Bytes // 32-Bit J2B header identifiers - static const uint32 magicDEADBEAF = 0xAFBEADDEu; - static const uint32 magicDEADBABE = 0xBEBAADDEu; + enum : uint32 { + magicDEADBEAF = 0xAFBEADDEu, + magicDEADBABE = 0xBEBAADDEu + }; char signature[4]; // MUSE uint32le deadbeaf; // 0xDEADBEAF (AM) or 0xDEADBABE (AMFF) @@ -58,17 +63,17 @@ struct AMFFRiffChunk // 32-Bit chunk identifiers enum ChunkIdentifiers { - idRIFF = MAGIC4LE('R','I','F','F'), - idAMFF = MAGIC4LE('A','M','F','F'), - idAM__ = MAGIC4LE('A','M',' ',' '), - idMAIN = MAGIC4LE('M','A','I','N'), - idINIT = MAGIC4LE('I','N','I','T'), - idORDR = MAGIC4LE('O','R','D','R'), - idPATT = MAGIC4LE('P','A','T','T'), - idINST = MAGIC4LE('I','N','S','T'), - idSAMP = MAGIC4LE('S','A','M','P'), - idAI__ = MAGIC4LE('A','I',' ',' '), - idAS__ = MAGIC4LE('A','S',' ',' '), + idRIFF = MagicLE("RIFF"), + idAMFF = MagicLE("AMFF"), + idAM__ = MagicLE("AM "), + idMAIN = MagicLE("MAIN"), + idINIT = MagicLE("INIT"), + idORDR = MagicLE("ORDR"), + idPATT = MagicLE("PATT"), + idINST = MagicLE("INST"), + idSAMP = MagicLE("SAMP"), + idAI__ = MagicLE("AI "), + idAS__ = MagicLE("AS "), }; uint32le id; // See ChunkIdentifiers @@ -254,7 +259,7 @@ struct AMFFSampleHeader mptSmp.nLoopEnd = loopEnd; mptSmp.nC5Speed = sampleRate; - if(instrHeader.vibratoType < CountOf(j2bAutoVibratoTrans)) + if(instrHeader.vibratoType < mpt::size(j2bAutoVibratoTrans)) mptSmp.nVibType = j2bAutoVibratoTrans[instrHeader.vibratoType]; mptSmp.nVibSweep = static_cast(instrHeader.vibratoSweep); mptSmp.nVibRate = static_cast(instrHeader.vibratoRate / 16); @@ -499,7 +504,6 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou if(channels == 0) return false; - PatternRow rowBase = sndFile.Patterns[pat].GetRow(0); ROWINDEX row = 0; while(row < numRows && chunk.CanRead(1)) @@ -509,11 +513,10 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou if(flags == rowDone) { row++; - rowBase = sndFile.Patterns[pat].GetRow(row); continue; } - ModCommand &m = rowBase[std::min((flags & channelMask), channels - 1)]; + ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min((flags & channelMask), channels - 1)); if(flags & dataFlag) { @@ -528,9 +531,9 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou m.command = amEffTrans[command]; } else { -#ifdef DEBUG +#ifdef J2B_LOG Log(mpt::format("J2B: Unknown command: 0x%1, param 0x%2")(mpt::fmt::HEX0<2>(command), mpt::fmt::HEX0<2>(m.param))); -#endif // DEBUG +#endif m.command = CMD_NONE; } @@ -733,12 +736,10 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultTempo.Set(mainChunk.tempo); m_nDefaultGlobalVolume = mainChunk.globalvolume * 2; - m_madeWithTracker = MPT_USTRING("Galaxy Sound System ("); - if(isAM) - m_madeWithTracker += MPT_USTRING("new version)"); - else - m_madeWithTracker += MPT_USTRING("old version)"); - + m_modFormat.formatName = isAM ? UL_("Galaxy Sound System (new version)") : UL_("Galaxy Sound System (old version)"); + m_modFormat.type = U_("j2b"); + m_modFormat.charset = mpt::CharsetCP437; + mpt::String::Read(m_songName, mainChunk.songname); // It seems like there's no way to differentiate between diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp index ea321081c..1890a89ca 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp @@ -21,7 +21,7 @@ namespace ModSpecs // Force built-in integer operations. // C++11 constexpr operations on the enum value_type would also solve this. -#define SongFlag FlagSet::store_type +#define SongFlag(x) (FlagSet::store_type(0) | x) MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = @@ -55,7 +55,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = 3999, // SamplesMax 255, // instrumentMax mixLevels1_17RC3, // defaultMixLevels - SongFlag(0) | SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX, // Supported song flags + SongFlag(SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX), // Supported song flags 200, // Max MIDI mapping directives MAX_ENVPOINTS, // Envelope point count true, // Has notecut. @@ -83,7 +83,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications mod_ = { MOD_TYPE_MOD, // Internal MODTYPE value "mod", // File extension - 37, // Minimum note index + 25, // Minimum note index 108, // Maximum note index 128, // Pattern max. 128, // Order max. @@ -105,7 +105,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications mod_ = 31, // SamplesMax 0, // instrumentMax mixLevelsCompatible, // defaultMixLevels - SongFlag(0) | SONG_PT_MODE | SONG_AMIGALIMITS | SONG_ISAMIGA, // Supported song flags + SongFlag(SONG_PT_MODE | SONG_AMIGALIMITS | SONG_ISAMIGA), // Supported song flags 0, // Max MIDI mapping directives 0, // No instrument envelopes false, // No notecut. @@ -153,7 +153,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications xm_ = 128 * 16, // SamplesMax (actually 16 per instrument) 128, // instrumentMax mixLevelsCompatibleFT2, // defaultMixLevels - SongFlag(0) | SONG_LINEARSLIDES, // Supported song flags + SongFlag(SONG_LINEARSLIDES), // Supported song flags 0, // Max MIDI mapping directives 12, // Envelope point count false, // No notecut. @@ -201,7 +201,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications xmEx_ = MAX_SAMPLES - 1, // SamplesMax (actually 32 per instrument(256 * 32 = 8192), but limited to MAX_SAMPLES = 4000) 255, // instrumentMax mixLevelsCompatibleFT2, // defaultMixLevels - SongFlag(0) | SONG_LINEARSLIDES | SONG_EXFILTERRANGE, // Supported song flags + SongFlag(SONG_LINEARSLIDES | SONG_EXFILTERRANGE), // Supported song flags 200, // Max MIDI mapping directives 12, // Envelope point count false, // No notecut. @@ -248,7 +248,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications s3m_ = 99, // SamplesMax 0, // instrumentMax mixLevelsCompatible, // defaultMixLevels - SongFlag(0) | SONG_FASTVOLSLIDES | SONG_AMIGALIMITS | SONG_S3MOLDVIBRATO, // Supported song flags + SongFlag(SONG_FASTVOLSLIDES | SONG_AMIGALIMITS | SONG_S3MOLDVIBRATO), // Supported song flags 0, // Max MIDI mapping directives 0, // No instrument envelopes true, // Has notecut. @@ -296,7 +296,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications s3mEx_ = 99, // SamplesMax 0, // instrumentMax mixLevelsCompatible, // defaultMixLevels - SongFlag(0) | SONG_FASTVOLSLIDES | SONG_AMIGALIMITS, // Supported song flags + SongFlag(SONG_FASTVOLSLIDES | SONG_AMIGALIMITS), // Supported song flags 0, // Max MIDI mapping directives 0, // No instrument envelopes true, // Has notecut. @@ -343,7 +343,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications it_ = 99, // SamplesMax 99, // instrumentMax mixLevelsCompatible, // defaultMixLevels - SongFlag(0) | SONG_LINEARSLIDES | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX, // Supported song flags + SongFlag(SONG_LINEARSLIDES | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX), // Supported song flags 0, // Max MIDI mapping directives 25, // Envelope point count true, // Has notecut. @@ -390,7 +390,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications itEx_ = 3999, // SamplesMax 255, // instrumentMax mixLevelsCompatible, // defaultMixLevels - SongFlag(0) | SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX, // Supported song flags + SongFlag(SONG_LINEARSLIDES | SONG_EXFILTERRANGE | SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX), // Supported song flags 200, // Max MIDI mapping directives 25, // Envelope point count true, // Has notecut. @@ -436,11 +436,11 @@ MODTYPE CModSpecifications::ExtensionToType(std::string ext) ext.erase(0, 1); } ext = mpt::ToLowerCaseAscii(ext); - for(std::size_t i = 0; i < CountOf(ModSpecs::Collection); i++) + for(const auto &spec : ModSpecs::Collection) { - if(ext == ModSpecs::Collection[i]->fileExtension) + if(ext == spec->fileExtension) { - return ModSpecs::Collection[i]->internalType; + return spec->internalType; } } return MOD_TYPE_NONE; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.h b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.h index c7c01d704..605a5dc77 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "Snd_defs.h" #include "modcommand.h" // ModCommand:: #include "../soundlib/SoundFilePlayConfig.h" // mixlevel constants. @@ -22,7 +24,7 @@ struct CModSpecifications { /// Returns modtype corresponding to given file extension. The extension string /// may begin with or without dot, e.g. both ".it" and "it" will be handled correctly. - static MODTYPE ExtensionToType(std::string ext); + static MODTYPE ExtensionToType(std::string ext); // (encoded in UTF8) // Return true if format supports given note. bool HasNote(ModCommand::NOTE note) const; @@ -34,7 +36,7 @@ struct CModSpecifications // NOTE: If changing order, update all initializations in .cpp file. MODTYPE internalType; // Internal MODTYPE value - const char *fileExtension; // File extension without dot. + const char *fileExtension; // File extension without dot (encoded in UTF8). ModCommand::NOTE noteMin; // Minimum note index (index starts from 1) ModCommand::NOTE noteMax; // Maximum note index (index starts from 1) PATTERNINDEX patternsMax; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp index 2105ac782..4d11e0091 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp @@ -79,6 +79,7 @@ void ModCommand::ExtendedMODtoS3MEffect() command = CMD_S3MCMDEX; switch(param & 0xF0) { + case 0x00: command = CMD_NONE; break; // No filter control case 0x10: command = CMD_PORTAMENTOUP; param |= 0xF0; break; case 0x20: command = CMD_PORTAMENTODOWN; param |= 0xF0; break; case 0x30: param = (param & 0x0F) | 0x10; break; @@ -91,7 +92,7 @@ void ModCommand::ExtendedMODtoS3MEffect() case 0xB0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param |= 0xF0; } else command = CMD_NONE; break; case 0xC0: if(param == 0xC0) { command = CMD_NONE; note = NOTE_NOTECUT; } break; // this does different things in IT and ST3 case 0xD0: if(param == 0xD0) { command = CMD_NONE; } break; // ditto - // rest are the same + // rest are the same or handled elsewhere } } @@ -111,11 +112,12 @@ void ModCommand::ExtendedS3MtoMODEffect() case 0x40: param = (param & 0x0F) | 0x70; break; case 0x50: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X5x case 0x60: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X6x + case 0x80: command = CMD_PANNING8; param = (param & 0x0F) * 0x11; break; // FT2 does actually not support E8x case 0x90: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X9x case 0xA0: command = CMD_XFINEPORTAUPDOWN; break; // map to unused XAx case 0xB0: param = (param & 0x0F) | 0x60; break; case 0x70: command = CMD_NONE; break; // No NNA / envelope control in MOD/XM format - // rest are the same + // rest are the same or handled elsewhere } } @@ -319,12 +321,12 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd { if(note == NOTE_NOTECUT) { - // convert note cut to EC0 if possible or volume command otherwise (MOD/XM has no real way of cutting notes that cannot be "undone" by volume commands) + // convert note cut to C00 if possible or volume command otherwise (MOD/XM has no real way of cutting notes that cannot be "undone" by volume commands) note = NOTE_NONE; if(command == CMD_NONE || !newTypeIsXM) { - command = CMD_MODCMDEX; - param = 0xC0; + command = CMD_VOLUME; + param = 0; } else { volcmd = VOLCMD_VOLUME; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h index f9d717fa9..da5f14a6f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "Snd_defs.h" #include "../common/misc_util.h" @@ -18,15 +20,15 @@ OPENMPT_NAMESPACE_BEGIN class CSoundFile; // Note definitions -#define NOTE_NONE (ModCommand::NOTE(0)) -#define NOTE_MIN (ModCommand::NOTE(1)) -#define NOTE_MAX (ModCommand::NOTE(120)) // Defines maximum notevalue(with index starting from 1) as well as maximum number of notes. +#define NOTE_NONE (ModCommand::NOTE(0)) // Empty note cell +#define NOTE_MIN (ModCommand::NOTE(1)) // Minimum note value +#define NOTE_MAX (ModCommand::NOTE(120)) // Maximum note value #define NOTE_MIDDLEC (ModCommand::NOTE(5 * 12 + NOTE_MIN)) -#define NOTE_KEYOFF (ModCommand::NOTE(0xFF)) // 255 -#define NOTE_NOTECUT (ModCommand::NOTE(0xFE)) // 254 -#define NOTE_FADE (ModCommand::NOTE(0xFD)) // 253, IT's action for illegal notes - DO NOT SAVE AS 253 as this is IT's internal representation of "no note"! -#define NOTE_PC (ModCommand::NOTE(0xFC)) // 252, Param Control 'note'. Changes param value on first tick. -#define NOTE_PCS (ModCommand::NOTE(0xFB)) // 251, Param Control (Smooth) 'note'. Interpolates param value during the whole row. +#define NOTE_KEYOFF (ModCommand::NOTE(0xFF)) // === (Note Off, releases envelope / fades samples, stops plugin note) +#define NOTE_NOTECUT (ModCommand::NOTE(0xFE)) // ^^^ (Cuts sample / stops all plugin notes) +#define NOTE_FADE (ModCommand::NOTE(0xFD)) // ~~~ (Fades samples, stops plugin note) +#define NOTE_PC (ModCommand::NOTE(0xFC)) // Param Control 'note'. Changes param value on first tick. +#define NOTE_PCS (ModCommand::NOTE(0xFB)) // Param Control (Smooth) 'note'. Interpolates param value during the whole row. #define NOTE_MAX_SPECIAL NOTE_KEYOFF #define NOTE_MIN_SPECIAL NOTE_PCS @@ -126,10 +128,10 @@ public: // Defines the maximum value for column data when interpreted as 2-byte value // (for example volcmd and vol). The valid value range is [0, maxColumnValue]. - static const int maxColumnValue = 999; + enum : int { maxColumnValue = 999 }; // Returns empty modcommand. - static ModCommand Empty() { ModCommand m = { 0, 0, VOLCMD_NONE, CMD_NONE, 0, 0 }; return m; } + static ModCommand Empty() { return ModCommand(); } bool operator==(const ModCommand& mc) const { @@ -165,8 +167,8 @@ public: bool IsInstrPlug() const { return IsPcNote(); } // Returns true if and only if note is NOTE_PC or NOTE_PCS. - bool IsPcNote() const { return note == NOTE_PC || note == NOTE_PCS; } - static bool IsPcNote(const NOTE note_id) { return note_id == NOTE_PC || note_id == NOTE_PCS; } + bool IsPcNote() const { return IsPcNote(note); } + static bool IsPcNote(NOTE note) { return note == NOTE_PC || note == NOTE_PCS; } // Returns true if and only if note is a valid musical note. bool IsNote() const { return IsInRange(note, NOTE_MIN, NOTE_MAX); } @@ -209,12 +211,12 @@ public: static bool CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 ¶m2); public: - uint8 note; - uint8 instr; - uint8 volcmd; - uint8 command; - uint8 vol; - uint8 param; + uint8 note = NOTE_NONE; + uint8 instr = 0; + uint8 volcmd = VOLCMD_NONE; + uint8 command = CMD_NONE; + uint8 vol = 0; + uint8 param = 0; }; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp index 1e97c09f2..2d61e1a6d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp @@ -1,8 +1,8 @@ /* - * ModSmp_Ctrl.cpp + * modsmp_ctrl.cpp * --------------- * Purpose: Basic sample editing code (resizing, adding silence, normalizing, ...). - * Notes : Could be merged with ModSample.h / ModSample.cpp at some point. + * Notes : Most of this stuff is not required in libopenmpt and should be moved to tracker-specific files. The rest could be merged into struct ModSample. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -20,9 +20,9 @@ OPENMPT_NAMESPACE_BEGIN namespace ctrlSmp { -void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength nNewLength, CSoundFile &sndFile) +void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, CSoundFile &sndFile) { - void * const pOldSmp = smp.pSample; + void * const pOldSmp = smp.samplev(); FlagSet setFlags, resetFlags; setFlags.set(CHN_16BIT, smp.uFlags[CHN_16BIT]); @@ -33,20 +33,20 @@ void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength nNewLength, CriticalSection cs; - ctrlChn::ReplaceSample(sndFile, smp, pNewSample, nNewLength, setFlags, resetFlags); - smp.pSample = pNewSample; - smp.nLength = nNewLength; + ctrlChn::ReplaceSample(sndFile, smp, pNewSample, newLength, setFlags, resetFlags); + smp.pData.pSample = pNewSample; + smp.nLength = newLength; ModSample::FreeSample(pOldSmp); } -SmpLength InsertSilence(ModSample &smp, const SmpLength nSilenceLength, const SmpLength nStartFrom, CSoundFile &sndFile) +SmpLength InsertSilence(ModSample &smp, const SmpLength silenceLength, const SmpLength startFrom, CSoundFile &sndFile) { - if(nSilenceLength == 0 || nSilenceLength > MAX_SAMPLE_LENGTH || smp.nLength > MAX_SAMPLE_LENGTH - nSilenceLength || nStartFrom > smp.nLength) + if(silenceLength == 0 || silenceLength > MAX_SAMPLE_LENGTH || smp.nLength > MAX_SAMPLE_LENGTH - silenceLength || startFrom > smp.nLength) return smp.nLength; - const bool wasEmpty = smp.nLength == 0 || smp.pSample == nullptr; - const SmpLength newLength = smp.nLength + nSilenceLength; + const bool wasEmpty = !smp.HasSampleData(); + const SmpLength newLength = smp.nLength + silenceLength; char *pNewSmp = nullptr; @@ -57,25 +57,25 @@ SmpLength InsertSilence(ModSample &smp, const SmpLength nSilenceLength, const Sm if(!wasEmpty) { // Copy over old sample - const SmpLength silenceOffset = nStartFrom * smp.GetBytesPerSample(); - const SmpLength silenceBytes = nSilenceLength * smp.GetBytesPerSample(); - if(nStartFrom > 0) + const SmpLength silenceOffset = startFrom * smp.GetBytesPerSample(); + const SmpLength silenceBytes = silenceLength * smp.GetBytesPerSample(); + if(startFrom > 0) { - memcpy(pNewSmp, smp.pSample, silenceOffset); + memcpy(pNewSmp, smp.samplev(), silenceOffset); } - if(nStartFrom < smp.nLength) + if(startFrom < smp.nLength) { - memcpy(pNewSmp + silenceOffset + silenceBytes, static_cast(smp.pSample) + silenceOffset, smp.GetSampleSizeInBytes() - silenceOffset); + memcpy(pNewSmp + silenceOffset + silenceBytes, smp.sampleb() + silenceOffset, smp.GetSampleSizeInBytes() - silenceOffset); } // Update loop points if necessary. - if(smp.nLoopStart >= nStartFrom) smp.nLoopStart += nSilenceLength; - if(smp.nLoopEnd >= nStartFrom) smp.nLoopEnd += nSilenceLength; - if(smp.nSustainStart >= nStartFrom) smp.nSustainStart += nSilenceLength; - if(smp.nSustainEnd >= nStartFrom) smp.nSustainEnd += nSilenceLength; - for(std::size_t i = 0; i < CountOf(smp.cues); i++) + if(smp.nLoopStart >= startFrom) smp.nLoopStart += silenceLength; + if(smp.nLoopEnd >= startFrom) smp.nLoopEnd += silenceLength; + if(smp.nSustainStart >= startFrom) smp.nSustainStart += silenceLength; + if(smp.nSustainEnd >= startFrom) smp.nSustainEnd += silenceLength; + for(auto &cue : smp.cues) { - if(smp.cues[i] >= nStartFrom) smp.cues[i] += nSilenceLength; + if(cue >= startFrom) cue += silenceLength; } } else { @@ -115,7 +115,7 @@ SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSou return smp.nLength; } const uint8 bps = smp.GetBytesPerSample(); - memmove(smp.pSample8 + selStart * bps, smp.pSample8 + selEnd * bps, (smp.nLength - selEnd) * bps); + memmove(smp.sampleb() + selStart * bps, smp.sampleb() + selEnd * bps, (smp.nLength - selEnd) * bps); smp.nLength -= (selEnd - selStart); // Did loops or cue points cover the deleted selection? @@ -125,9 +125,9 @@ SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSou if(smp.nLoopEnd == 0) smp.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); if(smp.nSustainEnd == 0) smp.uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); - for(std::size_t i = 0; i < CountOf(smp.cues); i++) + for(auto &cue : smp.cues) { - Util::DeleteItem(selStart, selEnd - 1, smp.cues[i]); + Util::DeleteItem(selStart, selEnd - 1, cue); } smp.PrecomputeLoops(sndFile); @@ -135,41 +135,41 @@ SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSou } -SmpLength ResizeSample(ModSample &smp, const SmpLength nNewLength, CSoundFile &sndFile) +SmpLength ResizeSample(ModSample &smp, const SmpLength newLength, CSoundFile &sndFile) { // Invalid sample size - if(nNewLength > MAX_SAMPLE_LENGTH || nNewLength == smp.nLength) + if(newLength > MAX_SAMPLE_LENGTH || newLength == smp.nLength) return smp.nLength; // New sample will be bigger so we'll just use "InsertSilence" as it's already there. - if(nNewLength > smp.nLength) - return InsertSilence(smp, nNewLength - smp.nLength, smp.nLength, sndFile); + if(newLength > smp.nLength) + return InsertSilence(smp, newLength - smp.nLength, smp.nLength, sndFile); // Else: Shrink sample - const SmpLength nNewSmpBytes = nNewLength * smp.GetBytesPerSample(); + const SmpLength newSmpBytes = newLength * smp.GetBytesPerSample(); - void *pNewSmp = ModSample::AllocateSample(nNewLength, smp.GetBytesPerSample()); + void *pNewSmp = ModSample::AllocateSample(newLength, smp.GetBytesPerSample()); if(pNewSmp == nullptr) return smp.nLength; //Sample allocation failed. // Copy over old data and replace sample by the new one - memcpy(pNewSmp, smp.pSample, nNewSmpBytes); - ReplaceSample(smp, pNewSmp, nNewLength, sndFile); + memcpy(pNewSmp, smp.sampleb(), newSmpBytes); + ReplaceSample(smp, pNewSmp, newLength, sndFile); // Adjust loops - if(smp.nLoopStart > nNewLength) + if(smp.nLoopStart > newLength) { smp.nLoopStart = smp.nLoopEnd = 0; smp.uFlags.reset(CHN_LOOP); } - if(smp.nLoopEnd > nNewLength) smp.nLoopEnd = nNewLength; - if(smp.nSustainStart > nNewLength) + if(smp.nLoopEnd > newLength) smp.nLoopEnd = newLength; + if(smp.nSustainStart > newLength) { smp.nSustainStart = smp.nSustainEnd = 0; smp.uFlags.reset(CHN_SUSTAINLOOP); } - if(smp.nSustainEnd > nNewLength) smp.nSustainEnd = nNewLength; + if(smp.nSustainEnd > newLength) smp.nSustainEnd = newLength; PrecomputeLoops(smp, sndFile); @@ -259,7 +259,7 @@ void PrecomputeLoopsImpl(ModSample &smp, const CSoundFile &sndFile) const int numChannels = smp.GetNumChannels(); const int copySamples = numChannels * InterpolationMaxLookahead; - T *sampleData = static_cast(smp.pSample); + T *sampleData = reinterpret_cast(smp.samplev()); T *afterSampleStart = sampleData + smp.nLength * numChannels; T *loopLookAheadStart = afterSampleStart + copySamples; T *sustainLookAheadStart = loopLookAheadStart + 4 * copySamples; @@ -300,7 +300,7 @@ void PrecomputeLoopsImpl(ModSample &smp, const CSoundFile &sndFile) bool PrecomputeLoops(ModSample &smp, CSoundFile &sndFile, bool updateChannels) { - if(smp.nLength == 0 || smp.pSample == nullptr) + if(!smp.HasSampleData()) return false; smp.SanitizeLoops(); @@ -408,14 +408,14 @@ void ResetSamples(CSoundFile &sndFile, ResetFlag resetflag, SAMPLEINDEX minSampl sample.nVibDepth = 0; sample.nVibRate = 0; sample.nVibSweep = 0; - sample.nVibType = 0; + sample.nVibType = VIB_SINE; sample.uFlags.reset(CHN_PANNING | SMP_NODEFAULTVOLUME); break; case SmpResetVibrato: sample.nVibDepth = 0; sample.nVibRate = 0; sample.nVibSweep = 0; - sample.nVibType = 0; + sample.nVibType = VIB_SINE; break; default: break; @@ -428,7 +428,7 @@ namespace { struct OffsetData { - double dMax, dMin, dOffset; + double max = 0.0, min = 0.0, offset = 0.0; }; // Returns maximum sample amplitude for given sample type (int8/int16). @@ -438,102 +438,97 @@ namespace // Calculates DC offset and returns struct with DC offset, max and min values. // DC offset value is average of [-1.0, 1.0[-normalized offset values. template - OffsetData CalculateOffset(const T *pStart, const SmpLength nLength) + OffsetData CalculateOffset(const T *pStart, const SmpLength length) { - OffsetData offsetVals = {0,0,0}; + OffsetData offsetVals; - if(nLength < 1) + if(length < 1) return offsetVals; - const double dMaxAmplitude = GetMaxAmplitude(); - - double dMax = -1, dMin = 1, dSum = 0; + const double maxAmplitude = GetMaxAmplitude(); + double max = -1, min = 1, sum = 0; const T *p = pStart; - for(SmpLength i = 0; i < nLength; i++, p++) + for(SmpLength i = 0; i < length; i++, p++) { - const double dVal = double(*p) / dMaxAmplitude; - dSum += dVal; - if(dVal > dMax) dMax = dVal; - if(dVal < dMin) dMin = dVal; + const double val = double(*p) / maxAmplitude; + sum += val; + if(val > max) max = val; + if(val < min) min = val; } - offsetVals.dMax = dMax; - offsetVals.dMin = dMin; - offsetVals.dOffset = (-dSum / (double)(nLength)); + offsetVals.max = max; + offsetVals.min = min; + offsetVals.offset = (-sum / (double)(length)); return offsetVals; } template - void RemoveOffsetAndNormalize(T *pStart, const SmpLength nLength, const double dOffset, const double dAmplify) + void RemoveOffsetAndNormalize(T *pStart, const SmpLength length, const double offset, const double amplify) { T *p = pStart; - for(SmpLength i = 0; i < nLength; i++, p++) + for(SmpLength i = 0; i < length; i++, p++) { - double dVal = (*p) * dAmplify + dOffset; - *p = mpt::saturate_cast(dVal); + double var = (*p) * amplify + offset; + *p = mpt::saturate_round(var); } } } // Remove DC offset -float RemoveDCOffset(ModSample &smp, - SmpLength iStart, - SmpLength iEnd, - const MODTYPE modtype, - CSoundFile &sndFile) +double RemoveDCOffset(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) { if(!smp.HasSampleData()) return 0; - if (iEnd > smp.nLength) iEnd = smp.nLength; - if (iStart > iEnd) iStart = iEnd; - if (iStart == iEnd) + if(end > smp.nLength) end = smp.nLength; + if(start > end) start = end; + if(start == end) { - iStart = 0; - iEnd = smp.nLength; + start = 0; + end = smp.nLength; } - iStart *= smp.GetNumChannels(); - iEnd *= smp.GetNumChannels(); + start *= smp.GetNumChannels(); + end *= smp.GetNumChannels(); - const double dMaxAmplitude = (smp.GetElementarySampleSize() == 2) ? GetMaxAmplitude() : GetMaxAmplitude(); + const double maxAmplitude = (smp.GetElementarySampleSize() == 2) ? GetMaxAmplitude() : GetMaxAmplitude(); // step 1: Calculate offset. - OffsetData oData = {0,0,0}; + OffsetData oData; if(smp.GetElementarySampleSize() == 2) - oData = CalculateOffset(smp.pSample16 + iStart, iEnd - iStart); + oData = CalculateOffset(smp.sample16() + start, end - start); else if(smp.GetElementarySampleSize() == 1) - oData = CalculateOffset(smp.pSample8 + iStart, iEnd - iStart); + oData = CalculateOffset(smp.sample8() + start, end - start); + else + return 0; - double dMin = oData.dMin, dMax = oData.dMax, dOffset = oData.dOffset; + double offset = oData.offset; - const float fReportOffset = (float)dOffset; - - if((int)(dOffset * dMaxAmplitude) == 0) + if((int)(offset * maxAmplitude) == 0) return 0; // those will be changed... - dMax += dOffset; - dMin += dOffset; + oData.max += offset; + oData.min += offset; // ... and that might cause distortion, so we will normalize this. - const double dAmplify = 1 / std::max(dMax, -dMin); + const double amplify = 1 / std::max(oData.max, -oData.min); // step 2: centralize + normalize sample - dOffset *= dMaxAmplitude * dAmplify; + offset *= maxAmplitude * amplify; if(smp.GetElementarySampleSize() == 2) - RemoveOffsetAndNormalize(smp.pSample16 + iStart, iEnd - iStart, dOffset, dAmplify); + RemoveOffsetAndNormalize(smp.sample16() + start, end - start, offset, amplify); else if(smp.GetElementarySampleSize() == 1) - RemoveOffsetAndNormalize(smp.pSample8 + iStart, iEnd - iStart, dOffset, dAmplify); + RemoveOffsetAndNormalize(smp.sample8() + start, end - start, offset, amplify); // step 3: adjust global vol (if available) - if((modtype & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (iStart == 0) && (iEnd == smp.nLength * smp.GetNumChannels())) + if((sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (start == 0) && (end == smp.nLength * smp.GetNumChannels())) { CriticalSection cs; - smp.nGlobalVol = std::min(Util::Round(smp.nGlobalVol / dAmplify), uint16(64)); + smp.nGlobalVol = std::min(mpt::saturate_round(smp.nGlobalVol / amplify), uint16(64)); for(auto &chn : sndFile.m_PlayState.Chn) { if(chn.pModSample == &smp) @@ -545,7 +540,7 @@ float RemoveDCOffset(ModSample &smp, PrecomputeLoops(smp, sndFile, false); - return fReportOffset; + return oData.offset; } @@ -559,24 +554,24 @@ static void ReverseSampleImpl(T *pStart, const SmpLength nLength) } // Reverse sample data -bool ReverseSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile) +bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) { if(!smp.HasSampleData()) return false; - if(iEnd == 0 || iStart > smp.nLength || iEnd > smp.nLength) + if(end == 0 || start > smp.nLength || end > smp.nLength) { - iStart = 0; - iEnd = smp.nLength; + start = 0; + end = smp.nLength; } - if(iEnd - iStart < 2) return false; + if(end - start < 2) return false; STATIC_ASSERT(MaxSamplingPointSize <= 4); if(smp.GetBytesPerSample() == 4) // 16 bit stereo - ReverseSampleImpl(static_cast(smp.pSample) + iStart, iEnd - iStart); + ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); else if(smp.GetBytesPerSample() == 2) // 16 bit mono / 8 bit stereo - ReverseSampleImpl(smp.pSample16 + iStart, iEnd - iStart); + ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); else if(smp.GetBytesPerSample() == 1) // 8 bit mono - ReverseSampleImpl(smp.pSample8 + iStart, iEnd - iStart); + ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); else return false; @@ -586,30 +581,30 @@ bool ReverseSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile template -static void UnsignSampleImpl(T *pStart, const SmpLength nLength) +static void UnsignSampleImpl(T *pStart, const SmpLength length) { const T offset = (std::numeric_limits::min)(); - for(SmpLength i = 0; i < nLength; i++) + for(SmpLength i = 0; i < length; i++) { pStart[i] += offset; } } // Virtually unsign sample data -bool UnsignSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile) +bool UnsignSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) { if(!smp.HasSampleData()) return false; - if(iEnd == 0 || iStart > smp.nLength || iEnd > smp.nLength) + if(end == 0 || start > smp.nLength || end > smp.nLength) { - iStart = 0; - iEnd = smp.nLength; + start = 0; + end = smp.nLength; } - iStart *= smp.GetNumChannels(); - iEnd *= smp.GetNumChannels(); + start *= smp.GetNumChannels(); + end *= smp.GetNumChannels(); if(smp.GetElementarySampleSize() == 2) - UnsignSampleImpl(smp.pSample16 + iStart, iEnd - iStart); + UnsignSampleImpl(smp.sample16() + start, end - start); else if(smp.GetElementarySampleSize() == 1) - UnsignSampleImpl(smp.pSample8 + iStart, iEnd - iStart); + UnsignSampleImpl(smp.sample8() + start, end - start); else return false; @@ -619,29 +614,29 @@ bool UnsignSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile & template -static void InvertSampleImpl(T *pStart, const SmpLength nLength) +static void InvertSampleImpl(T *pStart, const SmpLength length) { - for(SmpLength i = 0; i < nLength; i++) + for(SmpLength i = 0; i < length; i++) { pStart[i] = ~pStart[i]; } } // Invert sample data (flip by 180 degrees) -bool InvertSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile) +bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) { if(!smp.HasSampleData()) return false; - if(iEnd == 0 || iStart > smp.nLength || iEnd > smp.nLength) + if(end == 0 || start > smp.nLength || end > smp.nLength) { - iStart = 0; - iEnd = smp.nLength; + start = 0; + end = smp.nLength; } - iStart *= smp.GetNumChannels(); - iEnd *= smp.GetNumChannels(); + start *= smp.GetNumChannels(); + end *= smp.GetNumChannels(); if(smp.GetElementarySampleSize() == 2) - InvertSampleImpl(smp.pSample16 + iStart, iEnd - iStart); + InvertSampleImpl(smp.sample16() + start, end - start); else if(smp.GetElementarySampleSize() == 1) - InvertSampleImpl(smp.pSample8 + iStart, iEnd - iStart); + InvertSampleImpl(smp.sample8() + start, end - start); else return false; @@ -687,12 +682,12 @@ bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterlo if(smp.GetElementarySampleSize() == 2) { - XFadeSampleImpl(smp.pSample16 + start, smp.pSample16 + end, smp.pSample16 + end, fadeLength, e); - if(afterloopFade) XFadeSampleImpl(smp.pSample16 + afterloopEnd, smp.pSample16 + afterloopStart, smp.pSample16 + afterloopEnd, afterLoopLength, e); + XFadeSampleImpl(smp.sample16() + start, smp.sample16() + end, smp.sample16() + end, fadeLength, e); + if(afterloopFade) XFadeSampleImpl(smp.sample16() + afterloopEnd, smp.sample16() + afterloopStart, smp.sample16() + afterloopEnd, afterLoopLength, e); } else if(smp.GetElementarySampleSize() == 1) { - XFadeSampleImpl(smp.pSample8 + start, smp.pSample8 + end, smp.pSample8 + end, fadeLength, e); - if(afterloopFade) XFadeSampleImpl(smp.pSample8 + afterloopEnd, smp.pSample8 + afterloopStart, smp.pSample8 + afterloopEnd, afterLoopLength, e); + XFadeSampleImpl(smp.sample8() + start, smp.sample8() + end, smp.sample8() + end, fadeLength, e); + if(afterloopFade) XFadeSampleImpl(smp.sample8() + afterloopEnd, smp.sample8() + afterloopStart, smp.sample8() + afterloopEnd, afterLoopLength, e); } else return false; @@ -730,9 +725,9 @@ bool SilenceSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &s for(uint8 chn = 0; chn < numChn; chn++) { if(smp.GetElementarySampleSize() == 2) - SilenceSampleImpl(smp.pSample16 + start * numChn + chn, length, numChn, fromStart, toEnd); + SilenceSampleImpl(smp.sample16() + start * numChn + chn, length, numChn, fromStart, toEnd); else if(smp.GetElementarySampleSize() == 1) - SilenceSampleImpl(smp.pSample8 + start * numChn + chn, length, numChn, fromStart, toEnd); + SilenceSampleImpl(smp.sample8() + start * numChn + chn, length, numChn, fromStart, toEnd); else return false; } @@ -763,12 +758,12 @@ bool StereoSepSample(ModSample &smp, SmpLength start, SmpLength end, double sepa const SmpLength length = end - start; const uint8 numChn = smp.GetNumChannels(); - const int32 sep32 = Util::Round(separation * (65536.0 / 100.0)); + const int32 sep32 = mpt::saturate_round(separation * (65536.0 / 100.0)); if(smp.GetElementarySampleSize() == 2) - StereoSepSampleImpl(smp.pSample16 + start * numChn, length, sep32); + StereoSepSampleImpl(smp.sample16() + start * numChn, length, sep32); else if(smp.GetElementarySampleSize() == 1) - StereoSepSampleImpl(smp.pSample8 + start * numChn, length, sep32); + StereoSepSampleImpl(smp.sample8() + start * numChn, length, sep32); else return false; @@ -783,7 +778,7 @@ static void ConvertStereoToMonoMixImpl(T *pDest, const SmpLength length) const T *pEnd = pDest + length; for(T *pSource = pDest; pDest != pEnd; pDest++, pSource += 2) { - *pDest = (pSource[0] + pSource[1] + 1) >> 1; + *pDest = static_cast(mpt::rshift_signed(pSource[0] + pSource[1] + 1, 1)); } } @@ -808,9 +803,9 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers if(conversionMode == mixChannels) { if(smp.GetElementarySampleSize() == 2) - ConvertStereoToMonoMixImpl(smp.pSample16, smp.nLength); + ConvertStereoToMonoMixImpl(smp.sample16(), smp.nLength); else if(smp.GetElementarySampleSize() == 1) - ConvertStereoToMonoMixImpl(smp.pSample8, smp.nLength); + ConvertStereoToMonoMixImpl(smp.sample8(), smp.nLength); else return false; } else @@ -820,9 +815,9 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers conversionMode = onlyLeft; } if(smp.GetElementarySampleSize() == 2) - ConvertStereoToMonoOneChannelImpl(smp.pSample16 + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); + ConvertStereoToMonoOneChannelImpl(smp.sample16() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); else if(smp.GetElementarySampleSize() == 1) - ConvertStereoToMonoOneChannelImpl(smp.pSample8 + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); + ConvertStereoToMonoOneChannelImpl(smp.sample8() + (conversionMode == onlyLeft ? 0 : 1), smp.nLength); else return false; } @@ -867,9 +862,9 @@ bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile) } if(smp.GetElementarySampleSize() == 2) - ConvertMonoToStereoImpl(smp.pSample16, (int16 *)newSample, smp.nLength); + ConvertMonoToStereoImpl(smp.sample16(), (int16 *)newSample, smp.nLength); else if(smp.GetElementarySampleSize() == 1) - ConvertMonoToStereoImpl(smp.pSample8, (int8 *)newSample, smp.nLength); + ConvertMonoToStereoImpl(smp.sample8(), (int8 *)newSample, smp.nLength); else return false; @@ -888,11 +883,12 @@ bool ConvertTo8Bit(ModSample &smp, CSoundFile &sndFile) if(!smp.HasSampleData() || smp.GetElementarySampleSize() != 2) return false; - CopySample, SC::DecodeIdentity > >(smp.pSample8, smp.nLength * smp.GetNumChannels(), 1, smp.pSample16, smp.GetSampleSizeInBytes(), 1); + CopySample, SC::DecodeIdentity > >(reinterpret_cast(smp.samplev()), smp.nLength * smp.GetNumChannels(), 1, smp.sample16(), smp.GetSampleSizeInBytes(), 1); smp.uFlags.reset(CHN_16BIT); - for(auto &chn : sndFile.m_PlayState.Chn) if(chn.pModSample == &smp) + for(auto &chn : sndFile.m_PlayState.Chn) { - chn.dwFlags.reset(CHN_16BIT); + if(chn.pModSample == &smp) + chn.dwFlags.reset(CHN_16BIT); } smp.PrecomputeLoops(sndFile, false); @@ -910,7 +906,7 @@ bool ConvertTo16Bit(ModSample &smp, CSoundFile &sndFile) if(newSample == nullptr) return false; - CopySample, SC::DecodeIdentity > >(newSample, smp.nLength * smp.GetNumChannels(), 1, smp.pSample8, smp.GetSampleSizeInBytes(), 1); + CopySample, SC::DecodeIdentity > >(newSample, smp.nLength * smp.GetNumChannels(), 1, smp.sample8(), smp.GetSampleSizeInBytes(), 1); smp.uFlags.set(CHN_16BIT); ctrlSmp::ReplaceSample(smp, newSample, smp.nLength, sndFile); smp.PrecomputeLoops(sndFile, false); @@ -928,41 +924,40 @@ namespace ctrlChn void ReplaceSample( CSoundFile &sndFile, const ModSample &sample, const void * const pNewSample, - const SmpLength nNewLength, + const SmpLength newLength, FlagSet setFlags, FlagSet resetFlags) { const bool periodIsFreq = sndFile.PeriodsAreFrequencies(); - auto begin = sndFile.m_PlayState.Chn, end = sndFile.m_PlayState.Chn + CountOf(sndFile.m_PlayState.Chn); - for (auto chn = begin; chn != end; chn++) + for(auto &chn : sndFile.m_PlayState.Chn) { - if (chn->pModSample == &sample) + if(chn.pModSample == &sample) { - if (chn->pCurrentSample != nullptr) - chn->pCurrentSample = pNewSample; - if (chn->position.GetUInt() > nNewLength) - chn->position.Set(0); - if (chn->nLength > 0) - LimitMax(chn->nLength, nNewLength); - if(chn->InSustainLoop()) + if(chn.pCurrentSample != nullptr) + chn.pCurrentSample = pNewSample; + if(chn.position.GetUInt() > newLength) + chn.position.Set(0); + if(chn.nLength > 0) + LimitMax(chn.nLength, newLength); + if(chn.InSustainLoop()) { - chn->nLoopStart = sample.nSustainStart; - chn->nLoopEnd = sample.nSustainEnd; + chn.nLoopStart = sample.nSustainStart; + chn.nLoopEnd = sample.nSustainEnd; } else { - chn->nLoopStart = sample.nLoopStart; - chn->nLoopEnd = sample.nLoopEnd; + chn.nLoopStart = sample.nLoopStart; + chn.nLoopEnd = sample.nLoopEnd; } - chn->dwFlags.set(setFlags); - chn->dwFlags.reset(resetFlags); - if(chn->nC5Speed && sample.nC5Speed && !sndFile.UseFinetuneAndTranspose()) + chn.dwFlags.set(setFlags); + chn.dwFlags.reset(resetFlags); + if(chn.nC5Speed && sample.nC5Speed && !sndFile.UseFinetuneAndTranspose()) { if(periodIsFreq) - chn->nPeriod = Util::muldivr_unsigned(chn->nPeriod, sample.nC5Speed, chn->nC5Speed); + chn.nPeriod = Util::muldivr_unsigned(chn.nPeriod, sample.nC5Speed, chn.nC5Speed); else - chn->nPeriod = Util::muldivr_unsigned(chn->nPeriod, chn->nC5Speed, sample.nC5Speed); + chn.nPeriod = Util::muldivr_unsigned(chn.nPeriod, chn.nC5Speed, sample.nC5Speed); } - chn->nC5Speed = sample.nC5Speed; + chn.nC5Speed = sample.nC5Speed; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h index d99fbe249..28df8030d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h @@ -1,8 +1,8 @@ /* - * ModSmp_Ctrl.h + * modsmp_ctrl.h * ------------- * Purpose: Basic sample editing code (resizing, adding silence, normalizing, ...). - * Notes : Could be merged with ModSample.h / ModSample.cpp at some point. + * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN class CSoundFile; struct ModSample; @@ -33,7 +35,7 @@ enum ResetFlag // Insert silence to given location. // Note: Is currently implemented only for inserting silence to the beginning and to the end of the sample. // Return: Length of the new sample. -SmpLength InsertSilence(ModSample &smp, const SmpLength nSilenceLength, const SmpLength nStartFrom, CSoundFile &sndFile); +SmpLength InsertSilence(ModSample &smp, const SmpLength silenceLength, const SmpLength startFrom, CSoundFile &sndFile); // Remove part of a sample [selStart, selEnd[. // Note: Removed memory is not freed. @@ -43,10 +45,10 @@ SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSou // Change sample size. // Note: If resized sample is bigger, silence will be added to the sample's tail. // Return: Length of the new sample. -SmpLength ResizeSample(ModSample &smp, const SmpLength nNewLength, CSoundFile &sndFile); +SmpLength ResizeSample(ModSample &smp, const SmpLength newLength, CSoundFile &sndFile); // Replaces sample in 'smp' with given sample and frees the old sample. -void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength nNewLength, CSoundFile &sndFile); +void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, CSoundFile &sndFile); // Update loop wrap-around buffers bool PrecomputeLoops(ModSample &smp, CSoundFile &sndFile, bool updateChannels = true); @@ -59,25 +61,19 @@ void ResetSamples(CSoundFile &sndFile, ResetFlag resetflag, SAMPLEINDEX minSampl // Remove DC offset and normalize. // Return: If DC offset was removed, returns original offset value, zero otherwise. -float RemoveDCOffset(ModSample &smp, - SmpLength iStart, // Start position (for partial DC offset removal). - SmpLength iEnd, // End position (for partial DC offset removal). - const MODTYPE modtype, // Used to determine whether to adjust global or default volume - // to keep volume level the same given the normalization. - // Volume adjustment is not done if this param is MOD_TYPE_NONE. - CSoundFile &sndFile); // Passed to AdjustEndOfSample. +double RemoveDCOffset(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Amplify / fade sample data -bool AmplifySample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile, double amplifyStart, double amplifyEnd); +bool AmplifySample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile, double amplifyStart, double amplifyEnd); // Reverse sample data -bool ReverseSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile); +bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Virtually unsign sample data -bool UnsignSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile); +bool UnsignSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Invert sample data (flip by 180 degrees) -bool InvertSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile); +bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Crossfade sample data to create smooth loops bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterloopFade, bool useSustainLoop, CSoundFile &sndFile); @@ -117,7 +113,7 @@ namespace ctrlChn void ReplaceSample( CSoundFile &sndFile, const ModSample &sample, const void * const pNewSample, - const SmpLength nNewLength, + const SmpLength newLength, FlagSet setFlags, FlagSet resetFlags); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h new file mode 100644 index 000000000..ab10f7f47 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h @@ -0,0 +1,1331 @@ +// This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker). +// It was released by Shayde/Reality into the public domain. +// Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied. + +/* + + The Opal OPL3 emulator. + + Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. + + Missing features compared to a real OPL3: + + - Timers/interrupts + - OPL3 enable bit (it defaults to always on) + - CSW mode + - Test register + - Percussion mode + +*/ + + + +#include + + + +//================================================================================================== +// Opal class. +//================================================================================================== +class Opal { + + class Channel; + + // Various constants + enum { + OPL3SampleRate = 49716, + NumChannels = 18, + NumOperators = 36, + + EnvOff = -1, + EnvAtt, + EnvDec, + EnvSus, + EnvRel, + }; + + // A single FM operator + class Operator { + + public: + Operator(); + void SetMaster(Opal *opal) { Master = opal; } + void SetChannel(Channel *chan) { Chan = chan; } + + int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0); + + void SetKeyOn(bool on); + void SetTremoloEnable(bool on); + void SetVibratoEnable(bool on); + void SetSustainMode(bool on); + void SetEnvelopeScaling(bool on); + void SetFrequencyMultiplier(uint16_t scale); + void SetKeyScale(uint16_t scale); + void SetOutputLevel(uint16_t level); + void SetAttackRate(uint16_t rate); + void SetDecayRate(uint16_t rate); + void SetSustainLevel(uint16_t level); + void SetReleaseRate(uint16_t rate); + void SetWaveform(uint16_t wave); + + void ComputeRates(); + void ComputeKeyScaleLevel(); + + protected: + Opal * Master; // Master object + Channel * Chan; // Owning channel + uint32_t Phase; // The current offset in the selected waveform + uint16_t Waveform; // The waveform id this operator is using + uint16_t FreqMultTimes2; // Frequency multiplier * 2 + int EnvelopeStage; // Which stage the envelope is at (see Env* enums above) + int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest + uint16_t OutputLevel; // 0 - $FF + uint16_t AttackRate; + uint16_t DecayRate; + uint16_t SustainLevel; + uint16_t ReleaseRate; + uint16_t AttackShift; + uint16_t AttackMask; + uint16_t AttackAdd; + const uint16_t *AttackTab; + uint16_t DecayShift; + uint16_t DecayMask; + uint16_t DecayAdd; + const uint16_t *DecayTab; + uint16_t ReleaseShift; + uint16_t ReleaseMask; + uint16_t ReleaseAdd; + const uint16_t *ReleaseTab; + uint16_t KeyScaleShift; + uint16_t KeyScaleLevel; + int16_t Out[2]; + bool KeyOn; + bool KeyScaleRate; // Affects envelope rate scaling + bool SustainMode; // Whether to sustain during the sustain phase, or release instead + bool TremoloEnable; + bool VibratoEnable; + }; + + // A single channel, which can contain two or more operators + class Channel { + + public: + Channel(); + void SetMaster(Opal *opal) { Master = opal; } + void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) { + Op[0] = a; + Op[1] = b; + Op[2] = c; + Op[3] = d; + if (a) + a->SetChannel(this); + if (b) + b->SetChannel(this); + if (c) + c->SetChannel(this); + if (d) + d->SetChannel(this); + } + + void Output(int16_t &left, int16_t &right); + void SetEnable(bool on) { Enable = on; } + void SetChannelPair(Channel *pair) { ChannelPair = pair; } + + void SetFrequencyLow(uint16_t freq); + void SetFrequencyHigh(uint16_t freq); + void SetKeyOn(bool on); + void SetOctave(uint16_t oct); + void SetLeftEnable(bool on); + void SetRightEnable(bool on); + void SetFeedback(uint16_t val); + void SetModulationType(uint16_t type); + + uint16_t GetFreq() const { return Freq; } + uint16_t GetOctave() const { return Octave; } + uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } + uint16_t GetModulationType() const { return ModulationType; } + + void ComputeKeyScaleNumber(); + + protected: + void ComputePhaseStep(); + + Operator * Op[4]; + + Opal * Master; // Master object + uint16_t Freq; // Frequency; actually it's a phase stepping value + uint16_t Octave; // Also known as "block" in Yamaha parlance + uint32_t PhaseStep; + uint16_t KeyScaleNumber; + uint16_t FeedbackShift; + uint16_t ModulationType; + Channel * ChannelPair; + bool Enable; + bool LeftEnable, RightEnable; + }; + + public: + Opal(int sample_rate); + Opal(const Opal &) = delete; + Opal(Opal &&) = delete; + ~Opal(); + + void SetSampleRate(int sample_rate); + void Port(uint16_t reg_num, uint8_t val); + void Sample(int16_t *left, int16_t *right); + + protected: + void Init(int sample_rate); + void Output(int16_t &left, int16_t &right); + + int32_t SampleRate; + int32_t SampleAccum; + int16_t LastOutput[2], CurrOutput[2]; + Channel Chan[NumChannels]; + Operator Op[NumOperators]; +// uint16_t ExpTable[256]; +// uint16_t LogSinTable[256]; + uint16_t Clock; + uint16_t TremoloClock; + uint16_t TremoloLevel; + uint16_t VibratoTick; + uint16_t VibratoClock; + bool NoteSel; + bool TremoloDepth; + bool VibratoDepth; + + static const uint16_t RateTables[4][8]; + static const uint16_t ExpTable[256]; + static const uint16_t LogSinTable[256]; +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::RateTables[4][8] = { + { 1, 0, 1, 0, 1, 0, 1, 0 }, + { 1, 0, 1, 0, 0, 0, 1, 0 }, + { 1, 0, 0, 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::ExpTable[0x100] = { + 1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937, + 932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854, + 849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774, + 770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698, + 693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625, + 621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555, + 551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488, + 484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424, + 420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363, + 359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304, + 300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248, + 244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194, + 190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142, + 139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, + 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, + 42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::LogSinTable[0x100] = { + 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, + 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, + 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, + 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, + 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, + 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, + 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, + 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, + 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, + 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, + 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, + 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, + 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; + + + +//================================================================================================== +// This is the temporary code for generating the above tables. Maths and data from this nice +// reverse-engineering effort: +// +// https://docs.google.com/document/d/18IGx18NQY_Q1PJVZ-bHywao9bhsDoAqoIn1rIm42nwo/edit +//================================================================================================== +#if 0 +#include + +void GenerateTables() { + + // Build the exponentiation table (reversed from the official OPL3 ROM) + FILE *fd = fopen("exptab.txt", "wb"); + if (fd) { + for (int i = 0; i < 0x100; i++) { + int v = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; + if (i & 15) + fprintf(fd, " %4d,", v); + else + fprintf(fd, "\n\t%4d,", v); + } + fclose(fd); + } + + // Build the log-sin table + fd = fopen("sintab.txt", "wb"); + if (fd) { + for (int i = 0; i < 0x100; i++) { + int v = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + if (i & 15) + fprintf(fd, " %4d,", v); + else + fprintf(fd, "\n\t%4d,", v); + } + fclose(fd); + } +} +#endif + + + +//================================================================================================== +// Constructor/destructor. +//================================================================================================== +Opal::Opal(int sample_rate) { + + Init(sample_rate); +} +//-------------------------------------------------------------------------------------------------- +Opal::~Opal() { +} + + + +//================================================================================================== +// Initialise the emulation. +//================================================================================================== +void Opal::Init(int sample_rate) { + + Clock = 0; + TremoloClock = 0; + VibratoTick = 0; + VibratoClock = 0; + NoteSel = false; + TremoloDepth = false; + VibratoDepth = false; + +// // Build the exponentiation table (reversed from the official OPL3 ROM) +// for (int i = 0; i < 0x100; i++) +// ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; +// +// // Build the log-sin table +// for (int i = 0; i < 0x100; i++) +// LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + + // Let sub-objects know where to find us + for (int i = 0; i < NumOperators; i++) + Op[i].SetMaster(this); + + for (int i = 0; i < NumChannels; i++) + Chan[i].SetMaster(this); + + // Add the operators to the channels. Note, some channels can't use all the operators + // FIXME: put this into a separate routine + const int chan_ops[] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32, + }; + + for (int i = 0; i < NumChannels; i++) { + Channel *chan = &Chan[i]; + int op = chan_ops[i]; + if (i < 3 || (i >= 9 && i < 12)) + chan->SetOperators(&Op[op], &Op[op + 3], &Op[op + 6], &Op[op + 9]); + else + chan->SetOperators(&Op[op], &Op[op + 3], 0, 0); + } + + // Initialise the operator rate data. We can't do this in the Operator constructor as it + // relies on referencing the master and channel objects + for (int i = 0; i < NumOperators; i++) + Op[i].ComputeRates(); + + SetSampleRate(sample_rate); +} + + + +//================================================================================================== +// Change the sample rate. +//================================================================================================== +void Opal::SetSampleRate(int sample_rate) { + + // Sanity + if (sample_rate == 0) + sample_rate = OPL3SampleRate; + + SampleRate = sample_rate; + SampleAccum = 0; + LastOutput[0] = LastOutput[1] = 0; + CurrOutput[0] = CurrOutput[1] = 0; +} + + + +//================================================================================================== +// Write a value to an OPL3 register. +//================================================================================================== +void Opal::Port(uint16_t reg_num, uint8_t val) { + + static const int8_t op_lookup[] = { + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + uint16_t type = reg_num & 0xE0; + + // Is it BD, the one-off register stuck in the middle of the register array? + if (reg_num == 0xBD) { + TremoloDepth = (val & 0x80) != 0; + VibratoDepth = (val & 0x40) != 0; + return; + } + + // Global registers + if (type == 0x00) { + + // 4-OP enables + if (reg_num == 0x104) { + + // Enable/disable channels based on which 4-op enables + uint8_t mask = 1; + for (int i = 0; i < 6; i++, mask <<= 1) { + + // The 4-op channels are 0, 1, 2, 9, 10, 11 + uint16_t chan = static_cast(i < 3 ? i : i + 6); + Channel *primary = &Chan[chan]; + Channel *secondary = &Chan[chan + 3]; + + if (val & mask) { + + // Let primary channel know it's controlling the secondary channel + primary->SetChannelPair(secondary); + + // Turn off the second channel in the pair + secondary->SetEnable(false); + + } else { + + // Let primary channel know it's no longer controlling the secondary channel + primary->SetChannelPair(0); + + // Turn on the second channel in the pair + secondary->SetEnable(true); + } + } + + // CSW / Note-sel + } else if (reg_num == 0x08) { + + NoteSel = (val & 0x40) != 0; + + // Get the channels to recompute the Key Scale No. as this varies based on NoteSel + for (int i = 0; i < NumChannels; i++) + Chan[i].ComputeKeyScaleNumber(); + } + + // Channel registers + } else if (type >= 0xA0 && type <= 0xC0) { + + // Convert to channel number + int chan_num = reg_num & 15; + + // Valid channel? + if (chan_num >= 9) + return; + + // Is it the other bank of channels? + if (reg_num & 0x100) + chan_num += 9; + + Channel &chan = Chan[chan_num]; + + // Do specific registers + switch (reg_num & 0xF0) { + + // Frequency low + case 0xA0: { + chan.SetFrequencyLow(val); + break; + } + + // Key-on / Octave / Frequency High + case 0xB0: { + chan.SetKeyOn((val & 0x20) != 0); + chan.SetOctave(val >> 2 & 7); + chan.SetFrequencyHigh(val & 3); + break; + } + + // Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type + case 0xC0: { + chan.SetRightEnable((val & 0x20) != 0); + chan.SetLeftEnable((val & 0x10) != 0); + chan.SetFeedback(val >> 1 & 7); + chan.SetModulationType(val & 1); + break; + } + } + + // Operator registers + } else if ((type >= 0x20 && type <= 0x80) || type == 0xE0) { + + // Convert to operator number + int op_num = op_lookup[reg_num & 0x1F]; + + // Valid register? + if (op_num < 0) + return; + + // Is it the other bank of operators? + if (reg_num & 0x100) + op_num += 18; + + Operator &op = Op[op_num]; + + // Do specific registers + switch (type) { + + // Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier + case 0x20: { + op.SetTremoloEnable((val & 0x80) != 0); + op.SetVibratoEnable((val & 0x40) != 0); + op.SetSustainMode((val & 0x20) != 0); + op.SetEnvelopeScaling((val & 0x10) != 0); + op.SetFrequencyMultiplier(val & 15); + break; + } + + // Key Scale / Output Level + case 0x40: { + op.SetKeyScale(val >> 6); + op.SetOutputLevel(val & 0x3F); + break; + } + + // Attack Rate / Decay Rate + case 0x60: { + op.SetAttackRate(val >> 4); + op.SetDecayRate(val & 15); + break; + } + + // Sustain Level / Release Rate + case 0x80: { + op.SetSustainLevel(val >> 4); + op.SetReleaseRate(val & 15); + break; + } + + // Waveform + case 0xE0: { + op.SetWaveform(val & 7); + break; + } + } + } +} + + + +//================================================================================================== +// Generate sample. Every time you call this you will get two signed 16-bit samples (one for each +// stereo channel) which will sound correct when played back at the sample rate given when the +// class was constructed. +//================================================================================================== +void Opal::Sample(int16_t *left, int16_t *right) { + + // If the destination sample rate is higher than the OPL3 sample rate, we need to skip ahead + while (SampleAccum >= SampleRate) { + + LastOutput[0] = CurrOutput[0]; + LastOutput[1] = CurrOutput[1]; + + Output(CurrOutput[0], CurrOutput[1]); + + SampleAccum -= SampleRate; + } + + // Mix with the partial accumulation + int32_t omblend = SampleRate - SampleAccum; + *left = static_cast((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate); + *right = static_cast((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate); + + SampleAccum += OPL3SampleRate; +} + + + +//================================================================================================== +// Produce final output from the chip. This is at the OPL3 sample-rate. +//================================================================================================== +void Opal::Output(int16_t &left, int16_t &right) { + + int32_t leftmix = 0, rightmix = 0; + + // Sum the output of each channel + for (int i = 0; i < NumChannels; i++) { + + int16_t chanleft, chanright; + Chan[i].Output(chanleft, chanright); + + leftmix += chanleft; + rightmix += chanright; + } + + // Clamp + if (leftmix < -0x8000) + left = -0x8000; + else if (leftmix > 0x7FFF) + left = 0x7FFF; + else + left = static_cast(leftmix); + + if (rightmix < -0x8000) + right = -0x8000; + else if (rightmix > 0x7FFF) + right = 0x7FFF; + else + right = static_cast(rightmix); + + Clock++; + + // Tremolo. According to this post, the OPL3 tremolo is a 13,440 sample length triangle wave + // with a peak at 26 and a trough at 0 and is simply added to the logarithmic level accumulator + // http://forums.submarine.org.uk/phpBB/viewtopic.php?f=9&t=1171 + TremoloClock = (TremoloClock + 1) % 13440; + TremoloLevel = ((TremoloClock < 13440 / 2) ? TremoloClock : 13440 - TremoloClock) / 256; + if (!TremoloDepth) + TremoloLevel >>= 2; + + // Vibrato. This appears to be a 8 sample long triangle wave with a magnitude of the three + // high bits of the channel frequency, positive and negative, divided by two if the vibrato + // depth is zero. It is only cycled every 1,024 samples. + VibratoTick++; + if (VibratoTick >= 1024) { + VibratoTick = 0; + VibratoClock = (VibratoClock + 1) & 7; + } +} + + + +//================================================================================================== +// Channel constructor. +//================================================================================================== +Opal::Channel::Channel() { + + Master = 0; + Freq = 0; + Octave = 0; + PhaseStep = 0; + KeyScaleNumber = 0; + FeedbackShift = 0; + ModulationType = 0; + ChannelPair = 0; + Enable = true; +} + + + +//================================================================================================== +// Produce output from channel. +//================================================================================================== +void Opal::Channel::Output(int16_t &left, int16_t &right) { + + // Has the channel been disabled? This is usually a result of the 4-op enables being used to + // disable the secondary channel in each 4-op pair + if (!Enable) { + left = right = 0; + return; + } + + int16_t vibrato = (Freq >> 7) & 7; + if (!Master->VibratoDepth) + vibrato >>= 1; + + // 0 3 7 3 0 -3 -7 -3 + uint16_t clk = Master->VibratoClock; + if (!(clk & 3)) + vibrato = 0; // Position 0 and 4 is zero + else { + if (clk & 1) + vibrato >>= 1; // Odd positions are half the magnitude + if (clk & 4) + vibrato = -vibrato; // The second half positions are negative + } + + vibrato <<= Octave; + + // Combine individual operator outputs + int16_t out, acc; + + // Running in 4-op mode? + if (ChannelPair) { + + // Get the secondary channel's modulation type. This is the only thing from the secondary + // channel that is used + if (ChannelPair->GetModulationType() == 0) { + + if (ModulationType == 0) { + + // feedback -> modulator -> modulator -> modulator -> carrier + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + + } else { + + // (feedback -> carrier) + (modulator -> modulator -> carrier) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + } + + } else { + + if (ModulationType == 0) { + + // (feedback -> modulator -> carrier) + (modulator -> carrier) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + + } else { + + // (feedback -> carrier) + (modulator -> carrier) + carrier + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out += Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + } + } + + } else { + + // Standard 2-op mode + if (ModulationType == 0) { + + // Frequency modulation (well, phase modulation technically) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + + } else { + + // Additive + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out += Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato); + } + } + + left = LeftEnable ? out : 0; + right = RightEnable ? out : 0; +} + + + +//================================================================================================== +// Set phase step for operators using this channel. +//================================================================================================== +void Opal::Channel::SetFrequencyLow(uint16_t freq) { + + Freq = (Freq & 0x300) | (freq & 0xFF); + ComputePhaseStep(); +} +//-------------------------------------------------------------------------------------------------- +void Opal::Channel::SetFrequencyHigh(uint16_t freq) { + + Freq = (Freq & 0xFF) | ((freq & 3) << 8); + ComputePhaseStep(); + + // Only the high bits of Freq affect the Key Scale No. + ComputeKeyScaleNumber(); +} + + + +//================================================================================================== +// Set the octave of the channel (0 to 7). +//================================================================================================== +void Opal::Channel::SetOctave(uint16_t oct) { + + Octave = oct & 7; + ComputePhaseStep(); + ComputeKeyScaleNumber(); +} + + + +//================================================================================================== +// Keys the channel on/off. +//================================================================================================== +void Opal::Channel::SetKeyOn(bool on) { + + Op[0]->SetKeyOn(on); + Op[1]->SetKeyOn(on); +} + + + +//================================================================================================== +// Enable left stereo channel. +//================================================================================================== +void Opal::Channel::SetLeftEnable(bool on) { + + LeftEnable = on; +} + + + +//================================================================================================== +// Enable right stereo channel. +//================================================================================================== +void Opal::Channel::SetRightEnable(bool on) { + + RightEnable = on; +} + + + +//================================================================================================== +// Set the channel feedback amount. +//================================================================================================== +void Opal::Channel::SetFeedback(uint16_t val) { + + FeedbackShift = val ? 9 - val : 0; +} + + + +//================================================================================================== +// Set frequency modulation/additive modulation +//================================================================================================== +void Opal::Channel::SetModulationType(uint16_t type) { + + ModulationType = type; +} + + + +//================================================================================================== +// Compute the stepping factor for the operator waveform phase based on the frequency and octave +// values of the channel. +//================================================================================================== +void Opal::Channel::ComputePhaseStep() { + + PhaseStep = uint32_t(Freq) << Octave; +} + + + +//================================================================================================== +// Compute the key scale number and key scale levels. +// +// From the Yamaha data sheet this is the block/octave number as bits 3-1, with bit 0 coming from +// the MSB of the frequency if NoteSel is 1, and the 2nd MSB if NoteSel is 0. +//================================================================================================== +void Opal::Channel::ComputeKeyScaleNumber() { + + uint16_t lsb = Master->NoteSel ? Freq >> 9 : (Freq >> 8) & 1; + KeyScaleNumber = Octave << 1 | lsb; + + // Get the channel operators to recompute their rates as they're dependent on this number. They + // also need to recompute their key scale level + for (int i = 0; i < 4; i++) { + + if (!Op[i]) + continue; + + Op[i]->ComputeRates(); + Op[i]->ComputeKeyScaleLevel(); + } +} + + + +//================================================================================================== +// Operator constructor. +//================================================================================================== +Opal::Operator::Operator() { + + Master = 0; + Chan = 0; + Phase = 0; + Waveform = 0; + FreqMultTimes2 = 1; + EnvelopeStage = EnvOff; + EnvelopeLevel = 0x1FF; + AttackRate = 0; + DecayRate = 0; + SustainLevel = 0; + ReleaseRate = 0; + KeyScaleShift = 0; + KeyScaleLevel = 0; + Out[0] = Out[1] = 0; + KeyOn = false; + KeyScaleRate = false; + SustainMode = false; + TremoloEnable = false; + VibratoEnable = false; +} + + + +//================================================================================================== +// Produce output from operator. +//================================================================================================== +int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) { + + // Advance wave phase + if (VibratoEnable) + phase_step += vibrato; + Phase += (phase_step * FreqMultTimes2) / 2; + + uint16_t level = (EnvelopeLevel + OutputLevel + KeyScaleLevel + (TremoloEnable ? Master->TremoloLevel : 0)) << 3; + + switch (EnvelopeStage) { + + // Attack stage + case EnvAtt: { + if (AttackRate == 0) + break; + if (AttackMask && (Master->Clock & AttackMask)) + break; + uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3; + EnvelopeLevel += add; + if (EnvelopeLevel <= 0) { + EnvelopeLevel = 0; + EnvelopeStage = EnvDec; + } + break; + } + + // Decay stage + case EnvDec: { + if (DecayMask && (Master->Clock & DecayMask)) + break; + if (DecayRate != 0) { + uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; + EnvelopeLevel += add; + } + if (EnvelopeLevel >= SustainLevel) { + EnvelopeLevel = SustainLevel; + EnvelopeStage = EnvSus; + } + break; + } + + // Sustain stage + case EnvSus: { + + if (SustainMode) + break; + + // Note: fall-through! + MPT_FALLTHROUGH; + } + + // Release stage + case EnvRel: { + if (ReleaseRate == 0) + break; + if (ReleaseMask && (Master->Clock & ReleaseMask)) + break; + uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; + EnvelopeLevel += add; + if (EnvelopeLevel >= 0x1FF) { + EnvelopeLevel = 0x1FF; + EnvelopeStage = EnvOff; + Out[0] = Out[1] = 0; + return 0; + } + break; + } + + // Envelope, and therefore the operator, is not running + default: + Out[0] = Out[1] = 0; + return 0; + } + + // Feedback? In that case we modulate by a blend of the last two samples + if (fbshift) + mod += (Out[0] + Out[1]) >> fbshift; + + uint16_t phase = static_cast(Phase >> 10) + mod; + uint16_t offset = phase & 0xFF; + uint16_t logsin; + bool negate = false; + + switch (Waveform) { + + //------------------------------------ + // Standard sine wave + //------------------------------------ + case 0: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + negate = (phase & 0x200) != 0; + break; + + //------------------------------------ + // Half sine wave + //------------------------------------ + case 1: + if (phase & 0x200) + offset = 0; + else if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Positive sine wave + //------------------------------------ + case 2: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Quarter positive sine wave + //------------------------------------ + case 3: + if (phase & 0x100) + offset = 0; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed sine wave + //------------------------------------ + case 4: + if (phase & 0x200) + offset = 0; + + else { + + if (phase & 0x80) + offset ^= 0xFF; + + offset = (offset + offset) & 0xFF; + negate = (phase & 0x100) != 0; + } + + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed positive sine wave + //------------------------------------ + case 5: + if (phase & 0x200) + offset = 0; + + else { + + offset = (offset + offset) & 0xFF; + if (phase & 0x80) + offset ^= 0xFF; + } + + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Square wave + //------------------------------------ + case 6: + logsin = 0; + negate = (phase & 0x200) != 0; + break; + + //------------------------------------ + // Exponentiation wave + //------------------------------------ + default: + logsin = phase & 0x1FF; + if (phase & 0x200) { + logsin ^= 0x1FF; + negate = true; + } + logsin <<= 3; + break; + } + + uint16_t mix = logsin + level; + if (mix > 0x1FFF) + mix = 0x1FFF; + + // From the OPLx decapsulated docs: + // "When such a table is used for calculation of the exponential, the table is read at the + // position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the + // significand of the floating point output and the yet unused MSB's of the input are the + // exponent of the floating point output." + int16_t v = (Master->ExpTable[mix & 0xFF] + 1024u) >> (mix >> 8u); + v += v; + if (negate) + v = ~v; + + // Keep last two results for feedback calculation + Out[1] = Out[0]; + Out[0] = v; + + return v; +} + + + +//================================================================================================== +// Trigger operator. +//================================================================================================== +void Opal::Operator::SetKeyOn(bool on) { + + // Already on/off? + if (KeyOn == on) + return; + KeyOn = on; + + if (on) { + + // The highest attack rate is instant; it bypasses the attack phase + if (AttackRate == 15) { + EnvelopeStage = EnvDec; + EnvelopeLevel = 0; + } else + EnvelopeStage = EnvAtt; + + Phase = 0; + + } else { + + // Stopping current sound? + if (EnvelopeStage != EnvOff && EnvelopeStage != EnvRel) + EnvelopeStage = EnvRel; + } +} + + + +//================================================================================================== +// Enable amplitude vibrato. +//================================================================================================== +void Opal::Operator::SetTremoloEnable(bool on) { + + TremoloEnable = on; +} + + + +//================================================================================================== +// Enable frequency vibrato. +//================================================================================================== +void Opal::Operator::SetVibratoEnable(bool on) { + + VibratoEnable = on; +} + + + +//================================================================================================== +// Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to +// sustain, otherwise release. +//================================================================================================== +void Opal::Operator::SetSustainMode(bool on) { + + SustainMode = on; +} + + + +//================================================================================================== +// Key scale rate. Sets how much the Key Scaling Number affects the envelope rates. +//================================================================================================== +void Opal::Operator::SetEnvelopeScaling(bool on) { + + KeyScaleRate = on; + ComputeRates(); +} + + + +//================================================================================================== +// Multiplies the phase frequency. +//================================================================================================== +void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { + + // Needs to be multiplied by two (and divided by two later when we use it) because the first + // entry is actually .5 + const uint16_t mul_times_2[] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30, + }; + + FreqMultTimes2 = mul_times_2[scale & 15]; +} + + + +//================================================================================================== +// Attenuates output level towards higher pitch. +//================================================================================================== +void Opal::Operator::SetKeyScale(uint16_t scale) { + + static const uint8_t kslShift[4] = { 15, 1, 2, 0 }; + KeyScaleShift = kslShift[scale]; + ComputeKeyScaleLevel(); +} + + + +//================================================================================================== +// Sets the output level (volume) of the operator. +//================================================================================================== +void Opal::Operator::SetOutputLevel(uint16_t level) { + + OutputLevel = level * 4; +} + + + +//================================================================================================== +// Operator attack rate. +//================================================================================================== +void Opal::Operator::SetAttackRate(uint16_t rate) { + + AttackRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Operator decay rate. +//================================================================================================== +void Opal::Operator::SetDecayRate(uint16_t rate) { + + DecayRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Operator sustain level. +//================================================================================================== +void Opal::Operator::SetSustainLevel(uint16_t level) { + + SustainLevel = level < 15 ? level : 31; + SustainLevel *= 16; +} + + + +//================================================================================================== +// Operator release rate. +//================================================================================================== +void Opal::Operator::SetReleaseRate(uint16_t rate) { + + ReleaseRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Assign the waveform this operator will use. +//================================================================================================== +void Opal::Operator::SetWaveform(uint16_t wave) { + + Waveform = wave & 7; +} + + + +//================================================================================================== +// Compute actual rate from register rate. From the Yamaha data sheet: +// +// Actual rate = Rate value * 4 + Rof, if Rate value = 0, actual rate = 0 +// +// Rof is set as follows depending on the KSR setting: +// +// Key scale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// KSR = 0 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 +// KSR = 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// +// Note: zero rates are infinite, and are treated separately elsewhere +//================================================================================================== +void Opal::Operator::ComputeRates() { + + int combined_rate = AttackRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + int rate_high = combined_rate >> 2; + int rate_low = combined_rate & 3; + + AttackShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); + AttackMask = (1 << AttackShift) - 1; + AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + AttackTab = Master->RateTables[rate_low]; + + // Attack rate of 15 is always instant + if (AttackRate == 15) + AttackAdd = 0xFFF; + + combined_rate = DecayRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + DecayShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); + DecayMask = (1 << DecayShift) - 1; + DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + DecayTab = Master->RateTables[rate_low]; + + combined_rate = ReleaseRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + ReleaseShift = static_cast(rate_high < 12 ? 12 - rate_high : 0); + ReleaseMask = (1 << ReleaseShift) - 1; + ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + ReleaseTab = Master->RateTables[rate_low]; +} + + + +//================================================================================================== +// Compute the operator's key scale level. This changes based on the channel frequency/octave and +// operator key scale value. +//================================================================================================== +void Opal::Operator::ComputeKeyScaleLevel() { + + static const uint8_t levtab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32, + 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, + 0, 0, 0, 20, 32, 44, 52, 60, 64, 72, 76, 80, 84, 88, 92, 96, + 0, 0, 32, 52, 64, 76, 84, 92, 96, 104, 108, 112, 116, 120, 124, 128, + 0, 32, 64, 84, 96, 108, 116, 124, 128, 136, 140, 144, 148, 152, 156, 160, + 0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192, + 0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224, + }; + + // This uses a combined value of the top four bits of frequency with the octave/block + uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6); + KeyScaleLevel = levtab[i] >> KeyScaleShift; +} diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp index 99e3a7f70..4d419ec8a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp @@ -138,6 +138,19 @@ void CPattern::Deallocate() } +CPattern& CPattern::operator= (const CPattern &pat) +{ + m_ModCommands = pat.m_ModCommands; + m_Rows = pat.m_Rows; + m_RowsPerBeat = pat.m_RowsPerBeat; + m_RowsPerMeasure = pat.m_RowsPerMeasure; + m_tempoSwing = pat.m_tempoSwing; + m_PatternName = pat.m_PatternName; + return *this; +} + + + bool CPattern::operator== (const CPattern &other) const { return GetNumRows() == other.GetNumRows() @@ -249,7 +262,7 @@ bool CPattern::SetName(const char *newName, size_t maxChars) { return false; } - m_PatternName.assign(newName, strnlen(newName, maxChars)); + m_PatternName.assign(newName, mpt::strnlen(newName, maxChars)); return true; } @@ -463,7 +476,7 @@ void ReadData(std::istream& iStrm, CPattern& pat, const size_t nSize = 0); void WriteModPattern(std::ostream& oStrm, const CPattern& pat) { srlztn::SsbWrite ssb(oStrm); - ssb.BeginWrite(FileIdPattern, MptVersion::num); + ssb.BeginWrite(FileIdPattern, Version::Current().GetRawVersion()); ssb.WriteItem(pat, "data", &WriteData); // pattern time signature if(pat.GetOverrideSignature()) @@ -482,7 +495,7 @@ void WriteModPattern(std::ostream& oStrm, const CPattern& pat) void ReadModPattern(std::istream& iStrm, CPattern& pat, const size_t) { srlztn::SsbRead ssb(iStrm); - ssb.BeginRead(FileIdPattern, MptVersion::num); + ssb.BeginRead(FileIdPattern, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; ssb.ReadItem(pat, "data", &ReadData); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h index 541613423..e4c383d21 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include "modcommand.h" #include "Snd_defs.h" @@ -30,23 +32,10 @@ class CPattern friend class CPatternContainer; public: -//BEGIN: OPERATORS - CPattern& operator= (const CPattern &pat) - { - m_ModCommands = pat.m_ModCommands; - m_Rows = pat.m_Rows; - m_RowsPerBeat = pat.m_RowsPerBeat; - m_RowsPerMeasure = pat.m_RowsPerMeasure; - m_tempoSwing = pat.m_tempoSwing; - m_PatternName = pat.m_PatternName; - return *this; - } - + CPattern& operator= (const CPattern &pat); bool operator== (const CPattern &other) const; bool operator!= (const CPattern &other) const { return !(*this == other); } -//END: OPERATORS -//BEGIN: INTERFACE METHODS public: ModCommand* GetpModCommand(const ROWINDEX r, const CHANNELINDEX c) { return &m_ModCommands[r * GetNumChannels() + c]; } const ModCommand* GetpModCommand(const ROWINDEX r, const CHANNELINDEX c) const { return &m_ModCommands[r * GetNumChannels() + c]; } @@ -107,7 +96,7 @@ public: return SetName(buffer, bufferSize); } - std::string GetName() const { return m_PatternName; }; + std::string GetName() const { return m_PatternName; } #ifdef MODPLUG_TRACKER // Double number of rows @@ -120,8 +109,6 @@ public: // Write some kind of effect data to the pattern bool WriteEffect(EffectWriter &settings); -//END: INTERFACE METHODS - typedef std::vector::iterator iterator; typedef std::vector::const_iterator const_iterator; @@ -133,7 +120,7 @@ public: const_iterator end() const { return m_ModCommands.end(); } const_iterator cend() const { return m_ModCommands.cend(); } - CPattern(CPatternContainer& patCont) : m_rPatternContainer(patCont) {}; + CPattern(CPatternContainer& patCont) : m_rPatternContainer(patCont) {} CPattern(const CPattern &) = default; CPattern(CPattern &&) noexcept = default; @@ -145,7 +132,6 @@ protected: const ModCommand& GetModCommand(ROWINDEX r, CHANNELINDEX c) const { return m_ModCommands[r * GetNumChannels() + c]; } -//BEGIN: DATA protected: std::vector m_ModCommands; ROWINDEX m_Rows = 0; @@ -154,7 +140,6 @@ protected: TempoSwing m_tempoSwing; std::string m_PatternName; CPatternContainer& m_rPatternContainer; -//END: DATA }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp index 4d084a590..ff69ca0f4 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp @@ -144,15 +144,11 @@ void CPatternContainer::OnModTypeChanged(const MODTYPE /*oldtype*/) PATTERNINDEX CPatternContainer::GetNumPatterns() const { - if(Size() == 0) + for(PATTERNINDEX pat = Size(); pat > 0; pat--) { - return 0; - } - for(PATTERNINDEX nPat = Size(); nPat > 0; nPat--) - { - if(IsValidPat(nPat - 1)) + if(IsValidPat(pat - 1)) { - return nPat; + return pat; } } return 0; @@ -180,7 +176,7 @@ PATTERNINDEX CPatternContainer::GetNumNamedPatterns() const void WriteModPatterns(std::ostream& oStrm, const CPatternContainer& patc) { srlztn::SsbWrite ssb(oStrm); - ssb.BeginWrite(FileIdPatterns, MptVersion::num); + ssb.BeginWrite(FileIdPatterns, Version::Current().GetRawVersion()); const PATTERNINDEX nPatterns = patc.Size(); uint16 nCount = 0; for(uint16 i = 0; i < nPatterns; i++) if (patc[i].IsValid()) @@ -196,7 +192,7 @@ void WriteModPatterns(std::ostream& oStrm, const CPatternContainer& patc) void ReadModPatterns(std::istream& iStrm, CPatternContainer& patc, const size_t) { srlztn::SsbRead ssb(iStrm); - ssb.BeginRead(FileIdPatterns, MptVersion::num); + ssb.BeginRead(FileIdPatterns, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; PATTERNINDEX nPatterns = patc.Size(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.h index c9030146b..bc9efd12b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "pattern.h" #include diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp index dee5c7811..06d8d62e0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp @@ -124,7 +124,7 @@ void DigiBoosterEcho::SetParameter(PlugParamIndex index, PlugParamValue value) { if(index < kEchoNumParameters) { - m_chunk.param[index] = Util::Round(value * 255.0f); + m_chunk.param[index] = mpt::saturate_round(value * 255.0f); RecalculateEchoParams(); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp index 43389934a..cb36c4fad 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp @@ -116,7 +116,7 @@ void LFOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames) { if(m_outputToCC) { - plugin->MidiSend(MIDIEvents::CC(static_cast(m_outputParam & 0x7F), static_cast((m_outputParam >> 8) & 0x0F), Util::Round(value * 127.0f))); + plugin->MidiSend(MIDIEvents::CC(static_cast(m_outputParam & 0x7F), static_cast((m_outputParam >> 8) & 0x0F), mpt::saturate_round(value * 127.0f))); } else { plugin->SetParameter(m_outputParam, static_cast(value)); @@ -218,43 +218,43 @@ bool LFOPlugin::MidiSend(uint32 midiCode) } -bool LFOPlugin::MidiSysexSend(const void *message, uint32 length) +bool LFOPlugin::MidiSysexSend(mpt::const_byte_span sysex) { if(IMixPlugin *plugin = GetOutputPlugin()) - return plugin->MidiSysexSend(message, length); + return plugin->MidiSysexSend(sysex); else return true; } -void LFOPlugin::MidiCC(uint8 nMidiCh, MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) +void LFOPlugin::MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) { if(IMixPlugin *plugin = GetOutputPlugin()) { - plugin->MidiCC(nMidiCh, nController, nParam, trackChannel); + plugin->MidiCC(nController, nParam, trackChannel); } } -void LFOPlugin::MidiPitchBend(uint8 nMidiCh, int32 increment, int8 pwd) +void LFOPlugin::MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackChannel) { if(IMixPlugin *plugin = GetOutputPlugin()) { - plugin->MidiPitchBend(nMidiCh, increment, pwd); + plugin->MidiPitchBend(increment, pwd, trackChannel); } } -void LFOPlugin::MidiVibrato(uint8 nMidiCh, int32 depth, int8 pwd) +void LFOPlugin::MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel) { if(IMixPlugin *plugin = GetOutputPlugin()) { - plugin->MidiVibrato(nMidiCh, depth, pwd); + plugin->MidiVibrato(depth, pwd, trackChannel); } } -void LFOPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, uint16 note, uint16 vol, CHANNELINDEX trackChannel) +void LFOPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) { if(ModCommand::IsNote(static_cast(note)) && vol > 0) { @@ -262,7 +262,7 @@ void LFOPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, ui } if(IMixPlugin *plugin = GetOutputPlugin()) { - plugin->MidiCommand(nMidiCh, nMidiProg, wMidiBank, note, vol, trackChannel); + plugin->MidiCommand(instr, note, vol, trackChannel); } } @@ -276,10 +276,10 @@ void LFOPlugin::HardAllNotesOff() } -bool LFOPlugin::IsNotePlaying(uint32 note, uint32 midiChn, uint32 trackerChn) +bool LFOPlugin::IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) { if(IMixPlugin *plugin = GetOutputPlugin()) - return plugin->IsNotePlaying(note, midiChn, trackerChn); + return plugin->IsNotePlaying(note, trackerChn); else return false; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h index 3e5c24abe..2a444746f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #ifndef NO_PLUGINS #include "PlugInterface.h" @@ -64,7 +66,7 @@ protected: mpt::fast_prng m_PRNG; #ifdef MODPLUG_TRACKER - static const int WM_PARAM_UDPATE = WM_USER + 500; + enum : int { WM_PARAM_UDPATE = WM_USER + 500 }; #endif public: @@ -83,13 +85,13 @@ public: // MIDI event handling (mostly passing it through to the follow-up plugin) bool MidiSend(uint32 midiCode) override; - bool MidiSysexSend(const void *message, uint32 length) override; - void MidiCC(uint8 nMidiCh, MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) override; - void MidiPitchBend(uint8 nMidiCh, int32 increment, int8 pwd) override; - void MidiVibrato(uint8 nMidiCh, int32 depth, int8 pwd) override; - void MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override; + bool MidiSysexSend(mpt::const_byte_span sysex) override; + void MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) override; + void MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackChannel) override; + void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel) override; + void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override; void HardAllNotesOff() override; - bool IsNotePlaying(uint32 note, uint32 midiChn, uint32 trackerChn) override; + bool IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) override; int32 GetNumPrograms() const override { return 0; } int32 GetCurrentProgram() override { return 0; } @@ -142,7 +144,7 @@ protected: IMixPlugin *GetOutputPlugin() const; public: - static LFOWaveform ParamToWaveform(float param) { return static_cast(Util::Round(param * 32.0f)); } + static LFOWaveform ParamToWaveform(float param) { return static_cast(mpt::saturate_round(param * 32.0f)); } static float WaveformToParam(LFOWaveform waveform) { return waveform / 32.0f; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h index 29e5030cd..c4db88829 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN #ifndef NO_VST diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp index eb3937122..8b82a0ab2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp @@ -40,28 +40,11 @@ const CModDoc *IMixPlugin::GetModDoc() const { return m_SndFile.GetpModDoc(); } IMixPlugin::IMixPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) - : m_pNext(nullptr) - , m_pPrev(nullptr) - , m_Factory(factory) + : m_Factory(factory) , m_SndFile(sndFile) , m_pMixStruct(mixStruct) -#ifdef MODPLUG_TRACKER - , m_pEditor(nullptr) -#endif // MODPLUG_TRACKER - , m_fGain(1.0f) - , m_nSlot(0) - , m_isSongPlaying(false) - , m_isResumed(false) - , m_recordAutomation(false) - , m_passKeypressesToPlug(false) - , m_recordMIDIOut(false) { m_MixState.pMixBuffer = (mixsample_t *)((((intptr_t)m_MixBuffer) + 7) & ~7); - - m_MixState.dwFlags = 0; - m_MixState.nVolDecayL = 0; - m_MixState.nVolDecayR = 0; - while(m_pMixStruct != &(m_SndFile.m_MixPlugins[m_nSlot]) && m_nSlot < MAX_MIXPLUGINS - 1) { m_nSlot++; @@ -151,9 +134,9 @@ CString IMixPlugin::GetFormattedProgramName(int32 index) CString formattedName; if(rawname[0] >= 0 && rawname[0] < _T(' ')) - formattedName.Format(_T("%02u - Program %u"), index, index); + formattedName = mpt::cformat(_T("%1 - Program %2"))(mpt::cfmt::dec0<2>(index), index); else - formattedName.Format(_T("%02u - %s"), index, rawname.GetString()); + formattedName = mpt::cformat(_T("%1 - %2"))(mpt::cfmt::dec0<2>(index), rawname); return formattedName; } @@ -223,7 +206,7 @@ double IMixPlugin::GetOutputLatency() const } -void IMixPlugin::ProcessMixOps(float * MPT_RESTRICT pOutL, float * MPT_RESTRICT pOutR, float * MPT_RESTRICT leftPlugOutput, float * MPT_RESTRICT rightPlugOutput, uint32 numFrames) const +void IMixPlugin::ProcessMixOps(float * MPT_RESTRICT pOutL, float * MPT_RESTRICT pOutR, float * MPT_RESTRICT leftPlugOutput, float * MPT_RESTRICT rightPlugOutput, uint32 numFrames) { /* float *leftPlugOutput; float *rightPlugOutput; @@ -667,9 +650,10 @@ bool IMixPlugin::SaveProgram() TrackerSettings::Instance().PathPluginPresets.SetWorkingDir(dlg.GetWorkingDirectory()); } - bool bank = (dlg.GetExtension() == MPT_PATHSTRING("fxb")); + bool bank = (dlg.GetExtension() == P_("fxb")); - mpt::fstream f(dlg.GetFirstFile(), std::ios::out | std::ios::trunc | std::ios::binary); + mpt::SafeOutputFile sf(dlg.GetFirstFile(), std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave)); + mpt::ofstream& f = sf; if(f.good() && VSTPresets::SaveFile(f, *this, bank)) { return true; @@ -744,12 +728,12 @@ bool IMixPlugin::LoadProgram(mpt::PathString fileName) IMidiPlugin::IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) + , m_MidiCh{{}} { - MemsetZero(m_MidiCh); - for(int ch = 0; ch < 16; ch++) + for(auto &chn : m_MidiCh) { - m_MidiCh[ch].midiPitchBendPos = EncodePitchBendParam(MIDIEvents::pitchBendCentre); // centre pitch bend on all channels - m_MidiCh[ch].ResetProgram(); + chn.midiPitchBendPos = EncodePitchBendParam(MIDIEvents::pitchBendCentre); // centre pitch bend on all channels + chn.ResetProgram(); } } @@ -766,22 +750,31 @@ void IMidiPlugin::ApplyPitchWheelDepth(int32 &value, int8 pwd) } -void IMidiPlugin::MidiCC(uint8 nMidiCh, MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX /*trackChannel*/) +// Get the MIDI channel currently associated with a given tracker channel +uint8 IMidiPlugin::GetMidiChannel(CHANNELINDEX trackChannel) const +{ + return m_SndFile.GetBestMidiChannel(trackChannel); +} + + +void IMidiPlugin::MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) { //Error checking LimitMax(nController, MIDIEvents::MIDICC_end); LimitMax(nParam, uint8(127)); + auto midiCh = GetMidiChannel(trackChannel); if(m_SndFile.m_playBehaviour[kMIDICCBugEmulation]) - MidiSend(MIDIEvents::Event(MIDIEvents::evControllerChange, nMidiCh, nParam, static_cast(nController))); // param and controller are swapped (old broken implementation) + MidiSend(MIDIEvents::Event(MIDIEvents::evControllerChange, midiCh, nParam, static_cast(nController))); // param and controller are swapped (old broken implementation) else - MidiSend(MIDIEvents::CC(nController, nMidiCh, nParam)); + MidiSend(MIDIEvents::CC(nController, midiCh, nParam)); } // Bend MIDI pitch for given MIDI channel using fine tracker param (one unit = 1/64th of a note step) -void IMidiPlugin::MidiPitchBend(uint8 nMidiCh, int32 increment, int8 pwd) +void IMidiPlugin::MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn) { + auto midiCh = GetMidiChannel(trackerChn); if(m_SndFile.m_playBehaviour[kOldMIDIPitchBends]) { // OpenMPT Legacy: Old pitch slides never were really accurate, but setting the PWD to 13 in plugins would give the closest results. @@ -793,78 +786,79 @@ void IMidiPlugin::MidiPitchBend(uint8 nMidiCh, int32 increment, int8 pwd) ApplyPitchWheelDepth(increment, pwd); } - int32 newPitchBendPos = (increment + m_MidiCh[nMidiCh].midiPitchBendPos) & vstPitchBendMask; + int32 newPitchBendPos = (increment + m_MidiCh[midiCh].midiPitchBendPos) & vstPitchBendMask; Limit(newPitchBendPos, EncodePitchBendParam(MIDIEvents::pitchBendMin), EncodePitchBendParam(MIDIEvents::pitchBendMax)); - MidiPitchBend(nMidiCh, newPitchBendPos); + MidiPitchBend(midiCh, newPitchBendPos); } // Set MIDI pitch for given MIDI channel using fixed point pitch bend value (converted back to 0-16383 MIDI range) -void IMidiPlugin::MidiPitchBend(uint8 nMidiCh, int32 newPitchBendPos) +void IMidiPlugin::MidiPitchBend(uint8 midiCh, int32 newPitchBendPos) { MPT_ASSERT(EncodePitchBendParam(MIDIEvents::pitchBendMin) <= newPitchBendPos && newPitchBendPos <= EncodePitchBendParam(MIDIEvents::pitchBendMax)); - m_MidiCh[nMidiCh].midiPitchBendPos = newPitchBendPos; - MidiSend(MIDIEvents::PitchBend(nMidiCh, DecodePitchBendParam(newPitchBendPos))); + m_MidiCh[midiCh].midiPitchBendPos = newPitchBendPos; + MidiSend(MIDIEvents::PitchBend(midiCh, DecodePitchBendParam(newPitchBendPos))); } // Apply vibrato effect through pitch wheel commands on a given MIDI channel. -void IMidiPlugin::MidiVibrato(uint8 nMidiCh, int32 depth, int8 pwd) +void IMidiPlugin::MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn) { + auto midiCh = GetMidiChannel(trackerChn); depth = EncodePitchBendParam(depth); - if(depth != 0 || (m_MidiCh[nMidiCh].midiPitchBendPos & vstVibratoFlag)) + if(depth != 0 || (m_MidiCh[midiCh].midiPitchBendPos & vstVibratoFlag)) { ApplyPitchWheelDepth(depth, pwd); // Temporarily add vibrato offset to current pitch - int32 newPitchBendPos = (depth + m_MidiCh[nMidiCh].midiPitchBendPos) & vstPitchBendMask; + int32 newPitchBendPos = (depth + m_MidiCh[midiCh].midiPitchBendPos) & vstPitchBendMask; Limit(newPitchBendPos, EncodePitchBendParam(MIDIEvents::pitchBendMin), EncodePitchBendParam(MIDIEvents::pitchBendMax)); - MidiSend(MIDIEvents::PitchBend(nMidiCh, DecodePitchBendParam(newPitchBendPos))); + MidiSend(MIDIEvents::PitchBend(midiCh, DecodePitchBendParam(newPitchBendPos))); } // Update vibrato status if(depth != 0) - { - m_MidiCh[nMidiCh].midiPitchBendPos |= vstVibratoFlag; - } else - { - m_MidiCh[nMidiCh].midiPitchBendPos &= ~vstVibratoFlag; - } + m_MidiCh[midiCh].midiPitchBendPos |= vstVibratoFlag; + else + m_MidiCh[midiCh].midiPitchBendPos &= ~vstVibratoFlag; } -void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, uint16 note, uint16 vol, CHANNELINDEX trackChannel) +void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) { - PlugInstrChannel &channel = m_MidiCh[nMidiCh]; + auto midiCh = GetMidiChannel(trackChannel); + PlugInstrChannel &channel = m_MidiCh[midiCh]; - bool bankChanged = (channel.currentBank != --wMidiBank) && (wMidiBank < 0x4000); - bool progChanged = (channel.currentProgram != --nMidiProg) && (nMidiProg < 0x80); + uint16 midiBank = instr.wMidiBank - 1; + uint8 midiProg = instr.nMidiProgram - 1; + bool bankChanged = (channel.currentBank != midiBank) && (midiBank < 0x4000); + bool progChanged = (channel.currentProgram != midiProg) && (midiProg < 0x80); //get vol in [0,128[ - uint8 volume = static_cast(std::min(vol / 2, 127)); + uint8 volume = static_cast(std::min(vol / 2u, 127u)); // Bank change if(bankChanged) { - uint8 high = static_cast(wMidiBank >> 7); - uint8 low = static_cast(wMidiBank & 0x7F); + uint8 high = static_cast(midiBank >> 7); + uint8 low = static_cast(midiBank & 0x7F); - //GetSoundFile()->ProcessMIDIMacro(trackChannel, false, GetSoundFile()->m_MidiCfg.szMidiGlb[MIDIOUT_BANKSEL], 0); - MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Coarse, nMidiCh, high)); - MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Fine, nMidiCh, low)); + //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.szMidiGlb[MIDIOUT_BANKSEL], 0, m_nSlot + 1); + MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Coarse, midiCh, high)); + MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_BankSelect_Fine, midiCh, low)); - channel.currentBank = wMidiBank; + channel.currentBank = midiBank; } // Program change // According to the MIDI specs, a bank change alone doesn't have to change the active program - it will only change the bank of subsequent program changes. // Thus we send program changes also if only the bank has changed. - if(progChanged || (nMidiProg < 0x80 && bankChanged)) + if(progChanged || (midiProg < 0x80 && bankChanged)) { - channel.currentProgram = nMidiProg; - //GetSoundFile()->ProcessMIDIMacro(trackChannel, false, GetSoundFile()->m_MidiCfg.szMidiGlb[MIDIOUT_PROGRAM], 0); - MidiSend(MIDIEvents::ProgramChange(nMidiCh, nMidiProg)); + channel.currentProgram = midiProg; + //m_SndFile.ProcessMIDIMacro(trackChannel, false, m_SndFile.m_MidiCfg.szMidiGlb[MIDIOUT_PROGRAM], 0, m_nSlot + 1); + MidiSend(MIDIEvents::ProgramChange(midiCh, midiProg)); } @@ -875,7 +869,7 @@ void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, if(channel.noteOnMap[i][trackChannel]) { channel.noteOnMap[i][trackChannel]--; - MidiSend(MIDIEvents::NoteOff(nMidiCh, i, 0)); + MidiSend(MIDIEvents::NoteOff(midiCh, i, 0)); } } @@ -884,14 +878,14 @@ void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, // Also less likely to cause a VST event buffer overflow. else if(note == NOTE_NOTECUT) // ^^ { - MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllNotesOff, nMidiCh, 0)); - MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllSoundOff, nMidiCh, 0)); + MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllNotesOff, midiCh, 0)); + MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllSoundOff, midiCh, 0)); // Turn off all notes for(uint8 i = 0; i < CountOf(channel.noteOnMap); i++) { channel.noteOnMap[i][trackChannel] = 0; - MidiSend(MIDIEvents::NoteOff(nMidiCh, i, volume)); + MidiSend(MIDIEvents::NoteOff(midiCh, i, volume)); } } @@ -905,7 +899,7 @@ void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, // Some VSTis need a note off for each instance of a note on, e.g. fabfilter. while(channel.noteOnMap[i][trackChannel]) { - MidiSend(MIDIEvents::NoteOff(nMidiCh, i, volume)); + MidiSend(MIDIEvents::NoteOff(midiCh, i, volume)); channel.noteOnMap[i][trackChannel]--; } } @@ -918,9 +912,9 @@ void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, // Reset pitch bend on each new note, tracker style. // This is done if the pitch wheel has been moved or there was a vibrato on the previous row (in which case the "vstVibratoFlag" bit of the pitch bend memory is set) - if(m_MidiCh[nMidiCh].midiPitchBendPos != EncodePitchBendParam(MIDIEvents::pitchBendCentre)) + if(m_MidiCh[midiCh].midiPitchBendPos != EncodePitchBendParam(MIDIEvents::pitchBendCentre)) { - MidiPitchBend(nMidiCh, EncodePitchBendParam(MIDIEvents::pitchBendCentre)); + MidiPitchBend(midiCh, EncodePitchBendParam(MIDIEvents::pitchBendCentre)); } // count instances of active notes. @@ -931,15 +925,15 @@ void IMidiPlugin::MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, if(channel.noteOnMap[note][trackChannel] < uint8_max) channel.noteOnMap[note][trackChannel]++; - MidiSend(MIDIEvents::NoteOn(nMidiCh, static_cast(note), volume)); + MidiSend(MIDIEvents::NoteOn(midiCh, static_cast(note), volume)); } } -bool IMidiPlugin::IsNotePlaying(uint32 note, uint32 midiChn, uint32 trackerChn) +bool IMidiPlugin::IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) { note -= NOTE_MIN; - return (m_MidiCh[midiChn].noteOnMap[note][trackerChn] != 0); + return (m_MidiCh[GetMidiChannel(trackerChn)].noteOnMap[note][trackerChn] != 0); } @@ -967,7 +961,7 @@ void IMidiPlugin::ReceiveMidi(uint32 midiCode) } -void IMidiPlugin::ReceiveSysex(const void *message, uint32 length) +void IMidiPlugin::ReceiveSysex(mpt::const_byte_span sysex) { ResetSilence(); @@ -978,7 +972,7 @@ void IMidiPlugin::ReceiveSysex(const void *message, uint32 length) { IMixPlugin *plugin = m_SndFile.m_MixPlugins[receiver].pMixPlugin; // Add all events to the plugin's queue. - plugin->MidiSysexSend(message, length); + plugin->MidiSysexSend(sysex); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h index 1ce569680..bb5531264 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #ifndef NO_PLUGINS #include "../../soundlib/Snd_defs.h" @@ -22,6 +24,7 @@ OPENMPT_NAMESPACE_BEGIN struct VSTPluginLib; struct SNDMIXPLUGIN; +struct ModInstrument; class CSoundFile; class CModDoc; class CAbstractVstEditor; @@ -31,17 +34,15 @@ struct SNDMIXPLUGINSTATE // dwFlags flags enum PluginStateFlags { - psfMixReady = 0x01, // Set when cleared - psfHasInput = 0x02, // Set when plugin has non-silent input - psfSilenceBypass = 0x04, // Bypass because of silence detection + psfMixReady = 0x01, // Set when cleared + psfHasInput = 0x02, // Set when plugin has non-silent input + psfSilenceBypass = 0x04, // Bypass because of silence detection }; - mixsample_t *pMixBuffer; // Stereo effect send buffer - uint32 dwFlags; // PluginStateFlags - uint32 inputSilenceCount; // How much silence has been processed? (for plugin auto-turnoff) - mixsample_t nVolDecayL, nVolDecayR; // End of sample click removal - - SNDMIXPLUGINSTATE() { memset(this, 0, sizeof(*this)); } + mixsample_t *pMixBuffer = nullptr; // Stereo effect send buffer + uint32 dwFlags = 0; // PluginStateFlags + uint32 inputSilenceCount = 0; // How much silence has been processed? (for plugin auto-turnoff) + mixsample_t nVolDecayL = 0, nVolDecayR = 0; // End of sample click removal void ResetSilence() { @@ -57,12 +58,12 @@ class IMixPlugin friend class CAbstractVstEditor; protected: - IMixPlugin *m_pNext, *m_pPrev; + IMixPlugin *m_pNext = nullptr, *m_pPrev = nullptr; VSTPluginLib &m_Factory; CSoundFile &m_SndFile; SNDMIXPLUGIN *m_pMixStruct; #ifdef MODPLUG_TRACKER - CAbstractVstEditor *m_pEditor; + CAbstractVstEditor *m_pEditor = nullptr; #endif // MODPLUG_TRACKER public: @@ -72,16 +73,16 @@ public: protected: mixsample_t m_MixBuffer[MIXBUFFERSIZE * 2 + 2]; // Stereo interleaved input (sample mixer renders here) - float m_fGain; - PLUGINDEX m_nSlot; + float m_fGain = 1.0f; + PLUGINDEX m_nSlot = 0; - bool m_isSongPlaying : 1; - bool m_isResumed : 1; + bool m_isSongPlaying = false; + bool m_isResumed = false; public: - bool m_recordAutomation : 1; - bool m_passKeypressesToPlug : 1; - bool m_recordMIDIOut : 1; + bool m_recordAutomation = false; + bool m_passKeypressesToPlug = false; + bool m_recordMIDIOut = false; protected: virtual ~IMixPlugin(); @@ -134,19 +135,19 @@ public: // Restore parameters from module file virtual void RestoreAllParameters(int32 program); virtual void Process(float *pOutL, float *pOutR, uint32 numFrames) = 0; - void ProcessMixOps(float *pOutL, float *pOutR, float *leftPlugOutput, float *rightPlugOutput, uint32 numFrames) const; + void ProcessMixOps(float *pOutL, float *pOutR, float *leftPlugOutput, float *rightPlugOutput, uint32 numFrames); // Render silence and return the highest resulting output level virtual float RenderSilence(uint32 numSamples); // MIDI event handling virtual bool MidiSend(uint32 /*midiCode*/) { return true; } - virtual bool MidiSysexSend(const void * /*message*/, uint32 /*length*/) { return true; } - virtual void MidiCC(uint8 /*nMidiCh*/, MIDIEvents::MidiCC /*nController*/, uint8 /*nParam*/, CHANNELINDEX /*trackChannel*/) { } - virtual void MidiPitchBend(uint8 /*nMidiCh*/, int32 /*increment*/, int8 /*pwd*/) { } - virtual void MidiVibrato(uint8 /*nMidiCh*/, int32 /*depth*/, int8 /*pwd*/) { } - virtual void MidiCommand(uint8 /*nMidiCh*/, uint8 /*nMidiProg*/, uint16 /*wMidiBank*/, uint16 /*note*/, uint16 /*vol*/, CHANNELINDEX /*trackChannel*/) { } + virtual bool MidiSysexSend(mpt::const_byte_span /*sysex*/) { return true; } + virtual void MidiCC(MIDIEvents::MidiCC /*nController*/, uint8 /*nParam*/, CHANNELINDEX /*trackChannel*/) { } + virtual void MidiPitchBend(int32 /*increment*/, int8 /*pwd*/, CHANNELINDEX /*trackChannel*/) { } + virtual void MidiVibrato(int32 /*depth*/, int8 /*pwd*/, CHANNELINDEX /*trackerChn*/) { } + virtual void MidiCommand(const ModInstrument &/*instr*/, uint16 /*note*/, uint16 /*vol*/, CHANNELINDEX /*trackChannel*/) { } virtual void HardAllNotesOff() { } - virtual bool IsNotePlaying(uint32 /*note*/, uint32 /*midiChn*/, uint32 /*trackerChn*/) { return false; } + virtual bool IsNotePlaying(uint32 /*note*/, CHANNELINDEX /*trackerChn*/) { return false; } // Modify parameter by given amount. Only needs to be re-implemented if plugin architecture allows this to be performed atomically. virtual void ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff); @@ -223,7 +224,7 @@ public: inline void IMixPlugin::ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff) { - float val = GetParameter(nIndex) + diff; + PlugParamValue val = GetParameter(nIndex) + diff; Limit(val, PlugParamValue(0), PlugParamValue(1)); SetParameter(nIndex, val); } @@ -252,26 +253,29 @@ protected: void ResetProgram() { currentProgram = 0; currentBank = 0; } }; - PlugInstrChannel m_MidiCh[16]; // MIDI channel state + std::array m_MidiCh; // MIDI channel state public: IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); - virtual void MidiCC(uint8 nMidiCh, MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel); - virtual void MidiPitchBend(uint8 nMidiCh, int32 increment, int8 pwd); - virtual void MidiVibrato(uint8 nMidiCh, int32 depth, int8 pwd); - virtual void MidiCommand(uint8 nMidiCh, uint8 nMidiProg, uint16 wMidiBank, uint16 note, uint16 vol, CHANNELINDEX trackChannel); - virtual bool IsNotePlaying(uint32 note, uint32 midiChn, uint32 trackerChn); + void MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) override; + void MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn) override; + void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn) override; + void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override; + bool IsNotePlaying(uint32 note, CHANNELINDEX trackerChn) override; protected: // Plugin wants to send MIDI to OpenMPT virtual void ReceiveMidi(uint32 midiCode); - virtual void ReceiveSysex(const void *message, uint32 length); + virtual void ReceiveSysex(mpt::const_byte_span sysex); + + // Get the MIDI channel currently associated with a given tracker channel + virtual uint8 GetMidiChannel(CHANNELINDEX trackChannel) const; // Converts a 14-bit MIDI pitch bend position to our internal pitch bend position representation - static int32 EncodePitchBendParam(int32 position) { return (position << vstPitchBendShift); } + static constexpr int32 EncodePitchBendParam(int32 position) { return (position << vstPitchBendShift); } // Converts the internal pitch bend position to a 14-bit MIDI pitch bend position - static int16 DecodePitchBendParam(int32 position) { return static_cast(position >> vstPitchBendShift); } + static constexpr int16 DecodePitchBendParam(int32 position) { return static_cast(position >> vstPitchBendShift); } // Apply Pitch Wheel Depth (PWD) to some MIDI pitch bend value. static inline void ApplyPitchWheelDepth(int32 &value, int8 pwd); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginEventQueue.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginEventQueue.h deleted file mode 100644 index d8099c680..000000000 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginEventQueue.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * PluginEventQueue.h - * ------------------ - * Purpose: Alternative, easy to use implementation of the VST event queue mechanism. - * Notes : Modelled after an idea from http://www.kvraudio.com/forum/viewtopic.php?p=3043807#p3043807 - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - -#include -#include "../common/mptMutex.h" - -// Copied packing options from affectx.h -#if TARGET_API_MAC_CARBON - #ifdef __LP64__ - #pragma options align=power - #else - #pragma options align=mac68k - #endif -#elif defined __BORLANDC__ - #pragma -a8 -#elif defined(__GNUC__) - #pragma pack(push,8) -#elif defined(WIN32) || defined(__FLAT__) - #pragma pack(push) - #pragma pack(8) -#endif - -OPENMPT_NAMESPACE_BEGIN - -// Alternative, easy to use implementation of VstEvents struct. -template -class PluginEventQueue -{ -protected: - - // Although originally all event types were apparently supposed to be of the same size, this is not the case on 64-Bit systems. - // VstMidiSysexEvent contains 3 pointers, so the struct size differs between 32-Bit and 64-Bit systems. - union Event - { - VstEvent event; - VstMidiEvent midi; - VstMidiSysexEvent sysex; - }; - - // The first three member variables of this class mustn't be changed, to be compatible with the original VSTEvents struct. - VstInt32 numEvents; ///< number of Events in array - VstIntPtr reserved; ///< zero (Reserved for future use) - VstEvent *events[N]; ///< event pointer array - // Here we store our events. - std::deque eventQueue; - // Since plugins can also add events to the queue (even from a different thread than the processing thread), - // we need to ensure that reading and writing is never done in parallel. - mpt::mutex criticalSection; - -public: - - PluginEventQueue() - { - numEvents = 0; - reserved = 0; - MemsetZero(events); - } - - // Get the number of events that are currently in the output buffer. - // It is possible that there are more events left in the queue if the output buffer is full. - size_t GetNumEvents() - { - return numEvents; - } - - // Get the number of events that are currently queued, but not in the output buffer. - size_t GetNumQueuedEvents() - { - return eventQueue.size() - numEvents; - } - - // Add a VST event to the queue. Returns true on success. - // Set insertFront to true to prioritise this event (i.e. add it at the front of the queue instead of the back) - bool Enqueue(const VstEvent *event, bool insertFront = false) - { - MPT_ASSERT(event->type != kVstSysExType || event->byteSize == sizeof(VstMidiSysexEvent)); - MPT_ASSERT(event->type != kVstMidiType || event->byteSize == sizeof(VstMidiEvent)); - - Event copyEvent; - size_t copySize = std::min(size_t(event->byteSize), sizeof(copyEvent)); - // randomid by Insert Piz Here sends events of type kVstMidiType, but with a claimed size of 24 bytes instead of 32. - if(event->type == kVstSysExType) - copySize = sizeof(VstMidiSysexEvent); - else if(event->type == kVstMidiType) - copySize = sizeof(VstMidiEvent); - memcpy(©Event, event, copySize); - - if(event->type == kVstSysExType) - { - // SysEx messages need to be copied, as the space used for the dump might be freed in the meantime. - VstMidiSysexEvent &e = copyEvent.sysex; - e.sysexDump = new (std::nothrow) char[e.dumpBytes]; - if(e.sysexDump == nullptr) - { - return false; - } - memcpy(e.sysexDump, reinterpret_cast(event)->sysexDump, e.dumpBytes); - } - - MPT_LOCK_GUARD lock(criticalSection); - if(insertFront) - eventQueue.push_front(copyEvent); - else - eventQueue.push_back(copyEvent); - return true; - } - - // Set up the queue for transmitting to the plugin. Returns number of elements that are going to be transmitted. - VstInt32 Finalise() - { - MPT_LOCK_GUARD lock(criticalSection); - numEvents = static_cast(std::min(eventQueue.size(), N)); - for(VstInt32 i = 0; i < numEvents; i++) - { - events[i] = &eventQueue[i].event; - } - return numEvents; - } - - // Remove transmitted events from the queue - void Clear() - { - MPT_LOCK_GUARD lock(criticalSection); - if(numEvents) - { - // Release temporarily allocated buffer for SysEx messages - for(std::deque::iterator e = eventQueue.begin(); e != eventQueue.begin() + numEvents; e++) - { - if(e->event.type == kVstSysExType) - { - delete[] e->sysex.sysexDump; - } - } - eventQueue.erase(eventQueue.begin(), eventQueue.begin() + numEvents); - numEvents = 0; - } - } - -}; - -#if TARGET_API_MAC_CARBON - #pragma options align=reset -#elif defined(WIN32) || defined(__FLAT__) || defined(__GNUC__) - #pragma pack(pop) -#elif defined __BORLANDC__ - #pragma -a- -#endif - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp index c29d84bce..786bb7590 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp @@ -35,7 +35,7 @@ #include "../../mptrack/plugins/MidiInOut.h" #endif // MODPLUG_TRACKER -#include "../../common/StringFixer.h" +#include "../../common/mptStringBuffer.h" #include "../Sndfile.h" #include "../Loaders.h" @@ -64,7 +64,7 @@ OPENMPT_NAMESPACE_BEGIN //#define DMO_LOG #ifdef MODPLUG_TRACKER -static const MPT_UCHAR_TYPE *const cacheSection = MPT_ULITERAL("PluginCache"); +static const MPT_UCHAR_TYPE *const cacheSection = UL_("PluginCache"); #endif // MODPLUG_TRACKER @@ -72,7 +72,7 @@ uint8 VSTPluginLib::GetDllBits(bool fromCache) const { // Built-in plugins are always native. if(dllPath.empty()) - return sizeof(void *) * CHAR_BIT; + return mpt::arch_bits; #ifndef NO_VST if(!dllBits || !fromCache) { @@ -97,7 +97,7 @@ void VSTPluginLib::WriteToCache() const const std::string crcName = dllPath.ToUTF8(); const uint32 crc = mpt::crc32(crcName); - const mpt::ustring IDs = mpt::ufmt::HEX0<8>(SwapBytesLE(pluginId1)) + mpt::ufmt::HEX0<8>(SwapBytesLE(pluginId2)) + mpt::ufmt::HEX0<8>(SwapBytesLE(crc)); + const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc); mpt::PathString writePath = dllPath; if(theApp.IsPortableMode()) @@ -106,8 +106,8 @@ void VSTPluginLib::WriteToCache() const } cacheFile.Write(cacheSection, writePath.ToUnicode(), IDs); - cacheFile.Write(cacheSection, IDs + MPT_USTRING(".Vendor"), vendor); - cacheFile.Write(cacheSection, IDs + MPT_USTRING(".Flags"), EncodeCacheFlags()); + cacheFile.Write(cacheSection, IDs + U_(".Vendor"), vendor); + cacheFile.Write(cacheSection, IDs + U_(".Flags"), EncodeCacheFlags()); } #endif // MODPLUG_TRACKER @@ -132,18 +132,14 @@ bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile) CVstPluginManager::CVstPluginManager() -#ifndef NO_DMO - : MustUnInitilizeCOM(false) -#endif { - - #ifndef NO_DMO - HRESULT COMinit = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if(COMinit == S_OK || COMinit == S_FALSE) - { - MustUnInitilizeCOM = true; - } - #endif +#ifndef NO_DMO + HRESULT COMinit = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if(COMinit == S_OK || COMinit == S_FALSE) + { + MustUnInitilizeCOM = true; + } +#endif // Hard-coded "plugins" static constexpr struct @@ -156,19 +152,19 @@ CVstPluginManager::CVstPluginManager() } BuiltInPlugins[] = { // DirectX Media Objects Emulation - { DMO::Chorus::Create, "{EFE6629C-81F7-4281-BD91-C9D604A95AF6}", "Chorus", kDmoMagic, 0xEFE6629C, VSTPluginLib::catDMO, false, false }, - { DMO::Compressor::Create, "{EF011F79-4000-406D-87AF-BFFB3FC39D57}", "Compressor", kDmoMagic, 0xEF011F79, VSTPluginLib::catDMO, false, false }, - { DMO::Distortion::Create, "{EF114C90-CD1D-484E-96E5-09CFAF912A21}", "Distortion", kDmoMagic, 0xEF114C90, VSTPluginLib::catDMO, false, false }, - { DMO::Echo::Create, "{EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D}", "Echo", kDmoMagic, 0xEF3E932C, VSTPluginLib::catDMO, false, false }, - { DMO::Flanger::Create, "{EFCA3D92-DFD8-4672-A603-7420894BAD98}", "Flanger", kDmoMagic, 0xEFCA3D92, VSTPluginLib::catDMO, false, false }, - { DMO::Gargle::Create, "{DAFD8210-5711-4B91-9FE3-F75B7AE279BF}", "Gargle", kDmoMagic, 0xDAFD8210, VSTPluginLib::catDMO, false, false }, - { DMO::I3DL2Reverb::Create, "{EF985E71-D5C7-42D4-BA4D-2D073E2E96F4}", "I3DL2Reverb", kDmoMagic, 0xEF985E71, VSTPluginLib::catDMO, false, false }, - { DMO::ParamEq::Create, "{120CED89-3BF4-4173-A132-3CB406CF3231}", "ParamEq", kDmoMagic, 0x120CED89, VSTPluginLib::catDMO, false, false }, - { DMO::WavesReverb::Create, "{87FC0268-9A55-4360-95AA-004A1D9DE26C}", "WavesReverb", kDmoMagic, 0x87FC0268, VSTPluginLib::catDMO, false, false }, + { DMO::Chorus::Create, "{EFE6629C-81F7-4281-BD91-C9D604A95AF6}", "Chorus", kDmoMagic, 0xEFE6629C, VSTPluginLib::catDMO, false, false }, + { DMO::Compressor::Create, "{EF011F79-4000-406D-87AF-BFFB3FC39D57}", "Compressor", kDmoMagic, 0xEF011F79, VSTPluginLib::catDMO, false, false }, + { DMO::Distortion::Create, "{EF114C90-CD1D-484E-96E5-09CFAF912A21}", "Distortion", kDmoMagic, 0xEF114C90, VSTPluginLib::catDMO, false, false }, + { DMO::Echo::Create, "{EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D}", "Echo", kDmoMagic, 0xEF3E932C, VSTPluginLib::catDMO, false, false }, + { DMO::Flanger::Create, "{EFCA3D92-DFD8-4672-A603-7420894BAD98}", "Flanger", kDmoMagic, 0xEFCA3D92, VSTPluginLib::catDMO, false, false }, + { DMO::Gargle::Create, "{DAFD8210-5711-4B91-9FE3-F75B7AE279BF}", "Gargle", kDmoMagic, 0xDAFD8210, VSTPluginLib::catDMO, false, false }, + { DMO::I3DL2Reverb::Create, "{EF985E71-D5C7-42D4-BA4D-2D073E2E96F4}", "I3DL2Reverb", kDmoMagic, 0xEF985E71, VSTPluginLib::catDMO, false, false }, + { DMO::ParamEq::Create, "{120CED89-3BF4-4173-A132-3CB406CF3231}", "ParamEq", kDmoMagic, 0x120CED89, VSTPluginLib::catDMO, false, false }, + { DMO::WavesReverb::Create, "{87FC0268-9A55-4360-95AA-004A1D9DE26C}", "WavesReverb", kDmoMagic, 0x87FC0268, VSTPluginLib::catDMO, false, false }, // DigiBooster Pro Echo DSP - { DigiBoosterEcho::Create, "", "DigiBooster Pro Echo", MAGIC4LE('D','B','M','0'), MAGIC4LE('E','c','h','o'), VSTPluginLib::catRoomFx, false, true }, + { DigiBoosterEcho::Create, "", "DigiBooster Pro Echo", MagicLE("DBM0"), MagicLE("Echo"), VSTPluginLib::catRoomFx, false, true }, // LFO - { LFOPlugin::Create, "", "LFO", MAGIC4LE('O','M','P','T'), MAGIC4LE('L','F','O',' '), VSTPluginLib::catGenerator, false, true }, + { LFOPlugin::Create, "", "LFO", MagicLE("OMPT"), MagicLE("LFO "), VSTPluginLib::catGenerator, false, true }, #ifdef MODPLUG_TRACKER { MidiInOut::Create, "", "MIDI Input Output", PLUGMAGIC('V','s','t','P'), PLUGMAGIC('M','M','I','D'), VSTPluginLib::catSynth, true, true }, #endif // MODPLUG_TRACKER @@ -209,13 +205,13 @@ CVstPluginManager::~CVstPluginManager() } delete plug; } - #ifndef NO_DMO - if(MustUnInitilizeCOM) - { - CoUninitialize(); - MustUnInitilizeCOM = false; - } - #endif +#ifndef NO_DMO + if(MustUnInitilizeCOM) + { + CoUninitialize(); + MustUnInitilizeCOM = false; + } +#endif } @@ -228,45 +224,51 @@ bool CVstPluginManager::IsValidPlugin(const VSTPluginLib *pLib) const void CVstPluginManager::EnumerateDirectXDMOs() { #ifndef NO_DMO - const mpt::UUID knownDMOs[] = +#if MPT_MSVC_BEFORE(2017,0) + // VS2015 crashes if knownDMOs is constexpr. + const +#else + constexpr +#endif + mpt::UUID knownDMOs[] = { - MPT_UUID(745057C7,F353,4F2D,A7EE,58434477730E), // AEC (Acoustic echo cancellation, not usable) - MPT_UUID(EFE6629C,81F7,4281,BD91,C9D604A95AF6), // Chorus - MPT_UUID(EF011F79,4000,406D,87AF,BFFB3FC39D57), // Compressor - MPT_UUID(EF114C90,CD1D,484E,96E5,09CFAF912A21), // Distortion - MPT_UUID(EF3E932C,D40B,4F51,8CCF,3F98F1B29D5D), // Echo - MPT_UUID(EFCA3D92,DFD8,4672,A603,7420894BAD98), // Flanger - MPT_UUID(DAFD8210,5711,4B91,9FE3,F75B7AE279BF), // Gargle - MPT_UUID(EF985E71,D5C7,42D4,BA4D,2D073E2E96F4), // I3DL2Reverb - MPT_UUID(120CED89,3BF4,4173,A132,3CB406CF3231), // ParamEq - MPT_UUID(87FC0268,9A55,4360,95AA,004A1D9DE26C), // WavesReverb - MPT_UUID(F447B69E,1884,4A7E,8055,346F74D6EDB3), // Resampler DMO (not usable) + "745057C7-F353-4F2D-A7EE-58434477730E"_uuid, // AEC (Acoustic echo cancellation, not usable) + "EFE6629C-81F7-4281-BD91-C9D604A95AF6"_uuid, // Chorus + "EF011F79-4000-406D-87AF-BFFB3FC39D57"_uuid, // Compressor + "EF114C90-CD1D-484E-96E5-09CFAF912A21"_uuid, // Distortion + "EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D"_uuid, // Echo + "EFCA3D92-DFD8-4672-A603-7420894BAD98"_uuid, // Flanger + "DAFD8210-5711-4B91-9FE3-F75B7AE279BF"_uuid, // Gargle + "EF985E71-D5C7-42D4-BA4D-2D073E2E96F4"_uuid, // I3DL2Reverb + "120CED89-3BF4-4173-A132-3CB406CF3231"_uuid, // ParamEq + "87FC0268-9A55-4360-95AA-004A1D9DE26C"_uuid, // WavesReverb + "F447B69E-1884-4A7E-8055-346F74D6EDB3"_uuid, // Resampler DMO (not usable) }; HKEY hkEnum; - WCHAR keyname[128]; + TCHAR keyname[128]; LONG cr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("software\\classes\\DirectShow\\MediaObjects\\Categories\\f3602b3f-0592-48df-a4cd-674721e7ebeb"), 0, KEY_READ, &hkEnum); DWORD index = 0; while (cr == ERROR_SUCCESS) { - if ((cr = RegEnumKeyW(hkEnum, index, keyname, CountOf(keyname))) == ERROR_SUCCESS) + if ((cr = RegEnumKey(hkEnum, index, keyname, CountOf(keyname))) == ERROR_SUCCESS) { CLSID clsid; - std::wstring formattedKey = std::wstring(L"{") + std::wstring(keyname) + std::wstring(L"}"); + mpt::winstring formattedKey = mpt::winstring(_T("{")) + mpt::winstring(keyname) + mpt::winstring(_T("}")); if(Util::VerifyStringToCLSID(formattedKey, clsid)) { if(std::find(std::begin(knownDMOs), std::end(knownDMOs), clsid) == std::end(knownDMOs)) { HKEY hksub; - formattedKey = std::wstring(L"software\\classes\\DirectShow\\MediaObjects\\") + std::wstring(keyname); - if (RegOpenKeyW(HKEY_LOCAL_MACHINE, formattedKey.c_str(), &hksub) == ERROR_SUCCESS) + formattedKey = mpt::winstring(_T("software\\classes\\DirectShow\\MediaObjects\\")) + mpt::winstring(keyname); + if (RegOpenKey(HKEY_LOCAL_MACHINE, formattedKey.c_str(), &hksub) == ERROR_SUCCESS) { - WCHAR name[64]; + TCHAR name[64]; DWORD datatype = REG_SZ; DWORD datasize = sizeof(name); - if(ERROR_SUCCESS == RegQueryValueExW(hksub, nullptr, 0, &datatype, (LPBYTE)name, &datasize)) + if(ERROR_SUCCESS == RegQueryValueEx(hksub, nullptr, 0, &datatype, (LPBYTE)name, &datasize)) { mpt::String::SetNullTerminator(name); @@ -285,7 +287,7 @@ void CVstPluginManager::EnumerateDirectXDMOs() delete plug; } #ifdef DMO_LOG - Log(mpt::format(L"Found \"%1\" clsid=%2\n")(plug->libraryName, plug->dllPath)); + Log(mpt::format(U_("Found \"%1\" clsid=%2\n"))(plug->libraryName, plug->dllPath)); #endif } } @@ -303,11 +305,11 @@ void CVstPluginManager::EnumerateDirectXDMOs() // Extract instrument and category information from plugin. #ifndef NO_VST -static void GetPluginInformation(AEffect *effect, VSTPluginLib &library) +static void GetPluginInformation(Vst::AEffect *effect, VSTPluginLib &library) { unsigned long exception = 0; - library.category = static_cast(CVstPlugin::DispatchSEH(effect, effGetPlugCategory, 0, 0, nullptr, 0, exception)); - library.isInstrument = ((effect->flags & effFlagsIsSynth) || !effect->numInputs); + library.category = static_cast(CVstPlugin::DispatchSEH(effect, Vst::effGetPlugCategory, 0, 0, nullptr, 0, exception)); + library.isInstrument = ((effect->flags & Vst::effFlagsIsSynth) || !effect->numInputs); if(library.isInstrument) { @@ -319,7 +321,7 @@ static void GetPluginInformation(AEffect *effect, VSTPluginLib &library) #ifdef MODPLUG_TRACKER std::vector s(256, 0); - CVstPlugin::DispatchSEH(effect, effGetVendorString, 0, 0, s.data(), 0, exception); + CVstPlugin::DispatchSEH(effect, Vst::effGetVendorString, 0, 0, s.data(), 0, exception); library.vendor = mpt::ToCString(mpt::CharsetLocale, s.data()); #endif // MODPLUG_TRACKER } @@ -328,7 +330,7 @@ static void GetPluginInformation(AEffect *effect, VSTPluginLib &library) #ifdef MODPLUG_TRACKER // Add a plugin to the list of known plugins. -VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const mpt::ustring &tags, bool fromCache, const bool checkFileExistence, std::wstring *const errStr) +VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const mpt::ustring &tags, bool fromCache, bool *fileFound) { const mpt::PathString fileName = dllPath.GetFileName(); @@ -338,9 +340,9 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const if(!dllPath.CompareNoCase(dllPath, dupePlug->dllPath)) return dupePlug; } - if(checkFileExistence && errStr != nullptr && !dllPath.IsFile()) + if(fileFound != nullptr) { - *errStr += L"\nUnable to find " + dllPath.ToWide(); + *fileFound = dllPath.IsFile(); } // Look if the plugin info is stored in the PluginCache @@ -348,12 +350,12 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const { SettingsContainer & cacheFile = theApp.GetPluginCache(); // First try finding the full path - mpt::ustring IDs = cacheFile.Read(cacheSection, dllPath.ToUnicode(), MPT_USTRING("")); + mpt::ustring IDs = cacheFile.Read(cacheSection, dllPath.ToUnicode(), U_("")); if(IDs.length() < 16) { // If that didn't work out, find relative path mpt::PathString relPath = theApp.AbsolutePathToRelative(dllPath); - IDs = cacheFile.Read(cacheSection, relPath.ToUnicode(), MPT_USTRING("")); + IDs = cacheFile.Read(cacheSection, relPath.ToUnicode(), U_("")); } if(IDs.length() >= 16) @@ -380,9 +382,9 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const } } - const mpt::ustring flagKey = IDs + MPT_USTRING(".Flags"); + const mpt::ustring flagKey = IDs + U_(".Flags"); plug->DecodeCacheFlags(cacheFile.Read(cacheSection, flagKey, 0)); - plug->vendor = cacheFile.Read(cacheSection, IDs + MPT_USTRING(".Vendor"), CString()); + plug->vendor = cacheFile.Read(cacheSection, IDs + U_(".Vendor"), CString()); #ifdef VST_LOG Log("Plugin \"%s\" found in PluginCache\n", plug->libraryName.ToLocale().c_str()); @@ -397,7 +399,7 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const } // If this key contains a file name on program launch, a plugin previously crashed OpenMPT. - theApp.GetSettings().Write(MPT_USTRING("VST Plugins"), MPT_USTRING("FailedPlugin"), dllPath, SettingWriteThrough); + theApp.GetSettings().Write(U_("VST Plugins"), U_("FailedPlugin"), dllPath, SettingWriteThrough); bool validPlug = false; @@ -411,11 +413,11 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const unsigned long exception = 0; // Always scan plugins in a separate process HINSTANCE hLib = NULL; - AEffect *pEffect = CVstPlugin::LoadPlugin(*plug, hLib, true); + Vst::AEffect *pEffect = CVstPlugin::LoadPlugin(*plug, hLib, true); - if(pEffect != nullptr && pEffect->magic == kEffectMagic && pEffect->dispatcher != nullptr) + if(pEffect != nullptr && pEffect->magic == Vst::kEffectMagic && pEffect->dispatcher != nullptr) { - CVstPlugin::DispatchSEH(pEffect, effOpen, 0, 0, 0, 0, exception); + CVstPlugin::DispatchSEH(pEffect, Vst::effOpen, 0, 0, 0, 0, exception); plug->pluginId1 = pEffect->magic; plug->pluginId2 = pEffect->uniqueID; @@ -432,7 +434,7 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const pEffect->flags, pEffect->realQualities, pEffect->offQualities); #endif // VST_LOG - CVstPlugin::DispatchSEH(pEffect, effClose, 0, 0, 0, 0, exception); + CVstPlugin::DispatchSEH(pEffect, Vst::effClose, 0, 0, 0, 0, exception); validPlug = true; } @@ -440,12 +442,12 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const FreeLibrary(hLib); if(exception != 0) { - CVstPluginManager::ReportPlugException(mpt::format(L"Exception %1 while trying to load plugin \"%2\"!\n")(mpt::wfmt::HEX<8>(exception), plug->libraryName)); + CVstPluginManager::ReportPlugException(mpt::format(U_("Exception %1 while trying to load plugin \"%2\"!\n"))(mpt::ufmt::HEX0<8>(exception), plug->libraryName)); } #endif // NO_VST // Now it should be safe to assume that this plugin loaded properly. :) - theApp.GetSettings().Remove(MPT_USTRING("VST Plugins"), MPT_USTRING("FailedPlugin")); + theApp.GetSettings().Remove(U_("VST Plugins"), U_("FailedPlugin")); // If OK, write the information in PluginCache if(validPlug) @@ -544,19 +546,19 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd mpt::PathString fullPath = TrackerSettings::Instance().PathPlugins.GetDefaultDir(); if(fullPath.empty()) { - fullPath = theApp.GetAppDirPath() + MPT_PATHSTRING("Plugins\\"); + fullPath = theApp.GetAppDirPath() + P_("Plugins\\"); } - fullPath += mpt::PathString::FromUTF8(mixPlugin.GetLibraryName()) + MPT_PATHSTRING(".dll"); + fullPath += mpt::PathString::FromUTF8(mixPlugin.GetLibraryName()) + P_(".dll"); pFound = AddPlugin(fullPath); if(!pFound) { // Try plugin cache (search for library name) SettingsContainer &cacheFile = theApp.GetPluginCache(); - mpt::ustring IDs = cacheFile.Read(cacheSection, mpt::ToUnicode(mpt::CharsetUTF8, mixPlugin.GetLibraryName()), MPT_USTRING("")); + mpt::ustring IDs = cacheFile.Read(cacheSection, mpt::ToUnicode(mpt::CharsetUTF8, mixPlugin.GetLibraryName()), U_("")); if(IDs.length() >= 16) { - fullPath = cacheFile.Read(cacheSection, IDs, MPT_PATHSTRING("")); + fullPath = cacheFile.Read(cacheSection, IDs, P_("")); if(!fullPath.empty()) { fullPath = theApp.RelativePathToAbsolute(fullPath); @@ -570,15 +572,15 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd } #ifndef NO_VST - if(pFound && mixPlugin.Info.dwPluginId1 == kEffectMagic) + if(pFound && mixPlugin.Info.dwPluginId1 == Vst::kEffectMagic) { - AEffect *pEffect = nullptr; + Vst::AEffect *pEffect = nullptr; HINSTANCE hLibrary = nullptr; bool validPlugin = false; pEffect = CVstPlugin::LoadPlugin(*pFound, hLibrary, TrackerSettings::Instance().bridgeAllPlugins); - if(pEffect != nullptr && pEffect->dispatcher != nullptr && pEffect->magic == kEffectMagic) + if(pEffect != nullptr && pEffect->dispatcher != nullptr && pEffect->magic == Vst::kEffectMagic) { validPlugin = true; @@ -597,7 +599,7 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd if(!validPlugin) { FreeLibrary(hLibrary); - CVstPluginManager::ReportPlugException(mpt::format(L"Unable to create plugin \"%1\"!\n")(pFound->libraryName)); + CVstPluginManager::ReportPlugException(mpt::format(U_("Unable to create plugin \"%1\"!\n"))(pFound->libraryName)); } return validPlugin; } else @@ -639,21 +641,14 @@ void CVstPluginManager::OnIdle() } -void CVstPluginManager::ReportPlugException(const std::string &msg) -{ - Reporting::Notification(msg.c_str()); -#ifdef VST_LOG - Log("%s", msg.c_str()); -#endif -} - -void CVstPluginManager::ReportPlugException(const std::wstring &msg) +void CVstPluginManager::ReportPlugException(const mpt::ustring &msg) { Reporting::Notification(msg); #ifdef VST_LOG Log(mpt::ToUnicode(msg)); #endif } + #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h index 919327329..45c3e5c71 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h @@ -10,7 +10,7 @@ #pragma once -#include +#include "BuildSettings.h" OPENMPT_NAMESPACE_BEGIN @@ -27,23 +27,23 @@ struct SNDMIXPLUGIN; struct VSTPluginLib { public: - enum PluginCategory + enum PluginCategory : uint8 { // Same plugin categories as defined in VST SDK catUnknown = 0, - catEffect, // Simple Effect - catSynth, // VST Instrument (Synths, samplers,...) - catAnalysis, // Scope, Tuner, ... - catMastering, // Dynamics, ... - catSpacializer, // Panners, ... - catRoomFx, // Delays and Reverbs - catSurroundFx, // Dedicated surround processor - catRestoration, // Denoiser, ... - catOfflineProcess, // Offline Process - catShell, // Plug-in is container of other plug-ins - catGenerator, // Tone Generator, ... + catEffect, // Simple Effect + catSynth, // VST Instrument (Synths, samplers,...) + catAnalysis, // Scope, Tuner, ... + catMastering, // Dynamics, ... + catSpacializer, // Panners, ... + catRoomFx, // Delays and Reverbs + catSurroundFx, // Dedicated surround processor + catRestoration, // Denoiser, ... + catOfflineProcess, // Offline Process + catShell, // Plug-in is container of other plug-ins + catGenerator, // Tone Generator, ... // Custom categories - catDMO, // DirectX media object plugin + catDMO, // DirectX media object plugin numCategories, }; @@ -51,22 +51,22 @@ public: public: typedef IMixPlugin* (*CreateProc)(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); - IMixPlugin *pPluginsList; // Pointer to first plugin instance (this instance carries pointers to other instances) - CreateProc Create; // Factory to call for this plugin - mpt::PathString libraryName; // Display name - mpt::PathString dllPath; // Full path name + IMixPlugin *pPluginsList = nullptr; // Pointer to first plugin instance (this instance carries pointers to other instances) + CreateProc Create; // Factory to call for this plugin + mpt::PathString libraryName; // Display name + mpt::PathString dllPath; // Full path name #ifdef MODPLUG_TRACKER - mpt::ustring tags; // User tags + mpt::ustring tags; // User tags CString vendor; #endif // MODPLUG_TRACKER - int32 pluginId1; // Plugin type (kEffectMagic, kDmoMagic, ...) - int32 pluginId2; // Plugin unique ID - PluginCategory category; + int32 pluginId1 = 0; // Plugin type (kEffectMagic, kDmoMagic, ...) + int32 pluginId2 = 0; // Plugin unique ID + PluginCategory category = catUnknown; const bool isBuiltIn : 1; bool isInstrument : 1; bool useBridge : 1, shareBridgeInstance : 1; protected: - mutable uint8 dllBits; + mutable uint8 dllBits = 0; public: VSTPluginLib(CreateProc factoryProc, bool isBuiltIn, const mpt::PathString &dllPath, const mpt::PathString &libraryName @@ -74,27 +74,24 @@ public: , const mpt::ustring &tags = mpt::ustring(), const CString &vendor = CString() #endif // MODPLUG_TRACKER ) - : pPluginsList(nullptr) - , Create(factoryProc) + : Create(factoryProc) , libraryName(libraryName), dllPath(dllPath) #ifdef MODPLUG_TRACKER , tags(tags) , vendor(vendor) #endif // MODPLUG_TRACKER - , pluginId1(0), pluginId2(0) , category(catUnknown) , isBuiltIn(isBuiltIn), isInstrument(false) , useBridge(false), shareBridgeInstance(true) - , dllBits(0) { } // Check whether a plugin can be hosted inside OpenMPT or requires bridging uint8 GetDllBits(bool fromCache = true) const; - bool IsNative(bool fromCache = true) const { return GetDllBits(fromCache) == sizeof(void *) * CHAR_BIT; } + bool IsNative(bool fromCache = true) const { return GetDllBits(fromCache) == mpt::arch_bits; } // Check if a plugin is native, and if it is currently unknown, assume that it is native. Use this function only for performance reasons // (e.g. if tons of unscanned plugins would slow down generation of the plugin selection dialog) - bool IsNativeFromCache() const { return dllBits == sizeof(void *) * CHAR_BIT || dllBits == 0; } + bool IsNativeFromCache() const { return dllBits == mpt::arch_bits || dllBits == 0; } void WriteToCache() const; @@ -132,7 +129,7 @@ class CVstPluginManager #ifndef NO_PLUGINS protected: #ifndef NO_DMO - bool MustUnInitilizeCOM; + bool MustUnInitilizeCOM = false; #endif std::vector pluginList; @@ -150,12 +147,11 @@ public: void reserve(size_t num) { pluginList.reserve(num); } bool IsValidPlugin(const VSTPluginLib *pLib) const; - VSTPluginLib *AddPlugin(const mpt::PathString &dllPath, const mpt::ustring &tags = mpt::ustring(), bool fromCache = true, const bool checkFileExistence = false, std::wstring* const errStr = nullptr); + VSTPluginLib *AddPlugin(const mpt::PathString &dllPath, const mpt::ustring &tags = mpt::ustring(), bool fromCache = true, bool *fileFound = nullptr); bool RemovePlugin(VSTPluginLib *); bool CreateMixPlugin(SNDMIXPLUGIN &, CSoundFile &); void OnIdle(); - static void ReportPlugException(const std::wstring &msg); - static void ReportPlugException(const std::string &msg); + static void ReportPlugException(const mpt::ustring &msg); protected: void EnumerateDirectXDMOs(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h index 07a81f124..e7a62dc74 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h @@ -10,8 +10,11 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN + // At least this part of the code is ready for double-precision rendering... :> // buffer_t: Sample buffer type (float, double, ...) // bufferSize: Buffer size in samples @@ -20,18 +23,17 @@ class PluginMixBuffer { protected: - std::vector mixBuffer; // Actual buffer, contains all input and output buffers - std::vector inputs; // Pointers to input buffers - std::vector outputs; // Pointers to output buffers - buffer_t *alignedBuffer; // Aligned pointer to the buffer - - // Align buffer to 16 bytes - static_assert(sizeof(buffer_t) < 16, "Check buffer alignment code"); - static const uintptr_t bufferAlignmentInBytes = (16 - 1); - static const uintptr_t additionalBuffer = bufferAlignmentInBytes / sizeof(buffer_t); + std::vector inputs; // Pointers to input buffers + std::vector outputs; // Pointers to output buffers + mpt::aligned_buffer alignedBuffer; // Aligned buffer pointed into // Return pointer to an aligned buffer - buffer_t *GetBuffer(size_t index) const + const buffer_t *GetBuffer(size_t index) const + { + MPT_ASSERT(index < inputs.size() + outputs.size()); + return &alignedBuffer[bufferSize * index]; + } + buffer_t *GetBuffer(size_t index) { MPT_ASSERT(index < inputs.size() + outputs.size()); return &alignedBuffer[bufferSize * index]; @@ -53,19 +55,17 @@ public: inputs.resize(numInputs); outputs.resize(numOutputs); - // Create inputs + outputs buffers with additional alignment. - const size_t totalBufferSize = bufferSize * (numInputs + numOutputs) + additionalBuffer; - mixBuffer.assign(totalBufferSize, 0); + // Create inputs + outputs buffers + alignedBuffer.destructive_resize(bufferSize * (numInputs + numOutputs)); - // Align buffer start. - alignedBuffer = reinterpret_cast((reinterpret_cast(mixBuffer.data()) + bufferAlignmentInBytes) & ~bufferAlignmentInBytes); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); inputs.clear(); + inputs.shrink_to_fit(); outputs.clear(); - mixBuffer.clear(); - alignedBuffer = nullptr; + outputs.shrink_to_fit(); + alignedBuffer.destructive_resize(0); return false; } @@ -88,7 +88,7 @@ public: MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < inputs.size(); i++) { - memset(inputs[i], 0, numSamples * sizeof(buffer_t)); + std::memset(inputs[i], 0, numSamples * sizeof(buffer_t)); } } @@ -98,7 +98,7 @@ public: MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < outputs.size(); i++) { - memset(outputs[i], 0, numSamples * sizeof(buffer_t)); + std::memset(outputs[i], 0, numSamples * sizeof(buffer_t)); } } @@ -108,14 +108,16 @@ public: } // Return pointer to a given input or output buffer - buffer_t *GetInputBuffer(uint32 index) const { return GetBuffer(index); } - buffer_t *GetOutputBuffer(uint32 index) const { return GetBuffer(inputs.size() + index); } + const buffer_t *GetInputBuffer(uint32 index) const { return GetBuffer(index); } + const buffer_t *GetOutputBuffer(uint32 index) const { return GetBuffer(inputs.size() + index); } + buffer_t *GetInputBuffer(uint32 index) { return GetBuffer(index); } + buffer_t *GetOutputBuffer(uint32 index) { return GetBuffer(inputs.size() + index); } // Return pointer array to all input or output buffers buffer_t **GetInputBufferArray() { return inputs.empty() ? nullptr : inputs.data(); } buffer_t **GetOutputBufferArray() { return outputs.empty() ? nullptr : outputs.data(); } - bool Ok() const { return alignedBuffer != nullptr; } + bool Ok() const { return alignedBuffer.size() > 0; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h index cbca759e4..03223fe6f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "../Snd_defs.h" #ifndef NO_PLUGINS #include "../../common/Endianness.h" @@ -84,7 +86,7 @@ struct SNDMIXPLUGIN { return Info.szLibraryName; } // Check if a plugin is loaded into this slot (also returns true if the plugin in this slot has not been found) - bool IsValidPlugin() const { return (Info.dwPluginId1 | Info.dwPluginId2) != 0; }; + bool IsValidPlugin() const { return (Info.dwPluginId1 | Info.dwPluginId2) != 0; } // Input routing getters uint8 GetGain() const diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp index 56d11f28c..f128551a7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp @@ -142,7 +142,7 @@ void Chorus::SetParameter(PlugParamIndex index, PlugParamValue value) if(index == kChorusWaveShape && value < 1.0f) value = 0.0f; else if(index == kChorusPhase) - value = Util::Round(value * 4.0f) / 4.0f; + value = mpt::round(value * 4.0f) / 4.0f; m_param[index] = value; RecalculateChorusParams(); } @@ -205,7 +205,7 @@ CString Chorus::GetParamLabel(PlugParamIndex param) case kChorusFrequency: return _T("Hz"); case kChorusPhase: - return _T("°"); + return mpt::ToCString(MPT_UTF8("\xC2\xB0")); // U+00B0 DEGREE SIGN case kChorusDelay: return _T("ms"); } @@ -258,7 +258,7 @@ void Chorus::RecalculateChorusParams() float delaySamples = Delay() * sampleRate / 1000.0f; m_depthDelay = Depth() * delaySamples * 2048.0f; - m_delayOffset = Util::Round(4096.0f * (delaySamples + 2.0f)); + m_delayOffset = mpt::saturate_round(4096.0f * (delaySamples + 2.0f)); m_frequency = FrequencyInHertz(); const float frequencySamples = m_frequency / sampleRate; if(IsTriangle()) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h index 8a8c207d3..ab488267d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #ifndef NO_PLUGINS #include "../PlugInterface.h" @@ -106,7 +108,7 @@ protected: virtual float Feedback() const { return -99.0f + m_param[kChorusFeedback] * 198.0f; } virtual float Delay() const { return m_param[kChorusDelay] * 20.0f; } virtual float FrequencyInHertz() const { return m_param[kChorusFrequency] * 10.0f; } - virtual int Phase() const { return Util::Round(m_param[kChorusPhase] * 4.0f); } + virtual int Phase() const { return mpt::saturate_round(m_param[kChorusPhase] * 4.0f); } void RecalculateChorusParams(); }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp index 2d0032b24..83e50641e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp @@ -33,7 +33,7 @@ OPENMPT_NAMESPACE_BEGIN IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { CLSID clsid; - if (Util::VerifyStringToCLSID(factory.dllPath.ToWide(), clsid)) + if(Util::VerifyStringToCLSID(factory.dllPath.AsNative(), clsid)) { IMediaObject *pMO = nullptr; IMediaObjectInPlace *pMOIP = nullptr; @@ -51,11 +51,11 @@ IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIX return p; } #ifdef DMO_LOG - Log(factory.libraryName.ToUnicode() + MPT_USTRING(": Unable to use this DMO")); + Log(factory.libraryName.ToUnicode() + U_(": Unable to use this DMO")); #endif } #ifdef DMO_LOG - else Log(factory.libraryName.ToUnicode() + MPT_USTRING(": Failed to get IMediaObject & IMediaObjectInPlace interfaces")); + else Log(factory.libraryName.ToUnicode() + U_(": Failed to get IMediaObject & IMediaObjectInPlace interfaces")); #endif if (pMO) pMO->Release(); if (pMOIP) pMOIP->Release(); @@ -127,7 +127,7 @@ static const float _si2f = 1.0f / 32768.0f; static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, float * MPT_RESTRICT output, uint32 numFrames) { -#if (defined(ENABLE_SSE) || defined(ENABLE_SSE2)) +#if defined(ENABLE_SSE) if(GetProcSupport() & PROCSUPPORT_SSE) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE @@ -161,7 +161,7 @@ static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MP static void DeinterleaveStereo(const float * MPT_RESTRICT input, float * MPT_RESTRICT outputL, float * MPT_RESTRICT outputR, uint32 numFrames) { -#if (defined(ENABLE_SSE) || defined(ENABLE_SSE2)) +#if defined(ENABLE_SSE) if(GetProcSupport() & PROCSUPPORT_SSE) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE @@ -196,10 +196,10 @@ static void DeinterleaveStereo(const float * MPT_RESTRICT input, float * MPT_RES // Interleave two float streams into one int16 stereo stream. static void InterleaveFloatToInt16(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, int16 * MPT_RESTRICT output, uint32 numFrames) { -#ifdef ENABLE_SSE +#if defined(ENABLE_MMX) && defined(ENABLE_SSE) // This uses __m64, so it's not available on the MSVC 64-bit compiler. // But if the user runs a 64-bit operating system, they will go the floating-point path anyway. - if(GetProcSupport() & PROCSUPPORT_SSE) + if((GetProcSupport() & (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) == (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); @@ -248,10 +248,10 @@ static void InterleaveFloatToInt16(const float * MPT_RESTRICT inputL, const floa // Deinterleave an int16 stereo stream into two float streams. static void DeinterleaveInt16ToFloat(const int16 * MPT_RESTRICT input, float * MPT_RESTRICT outputL, float * MPT_RESTRICT outputR, uint32 numFrames) { -#ifdef ENABLE_SSE +#if defined(ENABLE_MMX) && defined(ENABLE_SSE) // This uses __m64, so it's not available on the MSVC 64-bit compiler. // But if the user runs a 64-bit operating system, they will go the floating-point path anyway. - if(GetProcSupport() & PROCSUPPORT_SSE) + if((GetProcSupport() & (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) == (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); @@ -385,7 +385,7 @@ void DMOPlugin::SetParameter(PlugParamIndex index, PlugParamValue value) if (fMax > fMin) value *= (fMax - fMin); value += fMin; Limit(value, fMin, fMax); - if (mpi.mpType != MPT_FLOAT) value = Util::Round(value); + if (mpi.mpType != MPT_FLOAT) value = mpt::round(value); m_pMediaParams->SetParam(index, value); } } @@ -433,7 +433,7 @@ void DMOPlugin::Resume() || FAILED(m_pMediaObject->SetOutputType(0, &mt, 0))) { #ifdef DMO_LOG - Log(MPT_USTRING("DMO: Failed to set I/O media type")); + Log(U_("DMO: Failed to set I/O media type")); #endif } } @@ -525,7 +525,7 @@ CString DMOPlugin::GetParamDisplay(PlugParamIndex param) WCHAR *text = nullptr; m_pParamInfo->GetParamText(param, &text); - const int nValue = Util::Round(md * (mpi.mpdMaxValue - mpi.mpdMinValue)); + const int nValue = mpt::saturate_round(md * (mpi.mpdMaxValue - mpi.mpdMinValue)); // Always skip first two strings (param name, unit name) for(int i = 0; i < nValue + 2; i++) { @@ -539,7 +539,7 @@ CString DMOPlugin::GetParamDisplay(PlugParamIndex param) default: { CString s; - s.Format(_T("%d"), Util::Round(md)); + s.Format(_T("%d"), mpt::saturate_round(md)); return s; } break; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp index b367da5ad..f51756432 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp @@ -103,7 +103,7 @@ void Echo::SetParameter(PlugParamIndex index, PlugParamValue value) { Limit(value, 0.0f, 1.0f); if(index == kEchoPanDelay) - value = Util::Round(value); + value = mpt::round(value); m_param[index] = value; RecalculateEchoParams(); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp index 3eaf65736..7b6b781e5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.cpp @@ -53,7 +53,7 @@ void Flanger::SetParameter(PlugParamIndex index, PlugParamValue value) if(index == kFlangerWaveShape && value < 1.0f) value = 0.0f; else if(index == kFlangerPhase) - value = Util::Round(value * 4.0f) / 4.0f; + value = mpt::round(value * 4.0f) / 4.0f; m_param[index] = value; RecalculateChorusParams(); } @@ -89,7 +89,7 @@ CString Flanger::GetParamLabel(PlugParamIndex param) case kFlangerFrequency: return _T("Hz"); case kFlangerPhase: - return _T("°"); + return mpt::ToCString(MPT_UTF8("\xC2\xB0")); // U+00B0 DEGREE SIGN case kFlangerDelay: return _T("ms"); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h index d38f7b762..549921c8f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #ifndef NO_PLUGINS #include "Chorus.h" @@ -59,7 +61,7 @@ protected: float Feedback() const override { return -99.0f + m_param[kFlangerFeedback] * 198.0f; } float Delay() const override { return m_param[kFlangerDelay] * 4.0f; } float FrequencyInHertz() const override { return m_param[kFlangerFrequency] * 10.0f; } - int Phase() const override { return Util::Round(m_param[kFlangerPhase] * 4.0f); } + int Phase() const override { return mpt::saturate_round(m_param[kFlangerPhase] * 4.0f); } }; } // namespace DMO diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.cpp index b17dc029d..44ac08e54 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.cpp @@ -123,7 +123,7 @@ void Gargle::SetParameter(PlugParamIndex index, PlugParamValue value) { Limit(value, 0.0f, 1.0f); if(index == kGargleWaveShape) - value = Util::Round(value); + value = mpt::round(value); m_param[index] = value; RecalculateGargleParams(); } @@ -167,7 +167,7 @@ CString Gargle::GetParamDisplay(PlugParamIndex param) switch(param) { case kGargleRate: - s.Format(_T("%d"), RateInHertz()); + s.Format(_T("%u"), RateInHertz()); break; case kGargleWaveShape: return (m_param[param] < 0.5) ? _T("Triangle") : _T("Square"); @@ -180,7 +180,7 @@ CString Gargle::GetParamDisplay(PlugParamIndex param) uint32 Gargle::RateInHertz() const { - return Util::Round(m_param[kGargleRate] * 999.0f) + 1; + return mpt::saturate_round(m_param[kGargleRate] * 999.0f) + 1; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp index d1e24ffcd..e339fe38c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp @@ -308,7 +308,7 @@ void I3DL2Reverb::SetParameter(PlugParamIndex index, PlugParamValue value) { Limit(value, 0.0f, 1.0f); if(index == kI3DL2ReverbQuality) - value = Util::Round(value * 3.0f) / 3.0f; + value = mpt::round(value * 3.0f) / 3.0f; m_param[index] = value; m_recalcParams = true; } @@ -448,7 +448,7 @@ void I3DL2Reverb::RecalculateI3DL2ReverbParams() // Room Filter float roomHF = std::pow(10.0f, RoomHF() / 100.0f / 10.0f); - if(roomHF == 1.0) + if(roomHF == 1.0f) { m_roomFilter = 0.0f; } else @@ -569,7 +569,7 @@ float I3DL2Reverb::CalcDecayCoeffs(int32 index) float c22 = -2.0f * c21 - 2.0f; float c23 = std::sqrt(c22 * c22 - c21 * c21 * 4.0f); c2 = (c23 - c22) / (c21 + c21); - if(mpt::abs(c2) > 1.0) + if(mpt::abs(c2) > 1.0f) c2 = (-c22 - c23) / (c21 + c21); } m_delayCoeffs[index][0] = c1; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h index 7616e66eb..29e9dfdd4 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #ifndef NO_PLUGINS #include "../PlugInterface.h" @@ -142,14 +144,14 @@ protected: float RoomRolloffFactor() const { return m_param[kI3DL2ReverbRoomRolloffFactor] * 10.0f; } float DecayTime() const { return 0.1f + m_param[kI3DL2ReverbDecayTime] * 19.9f; } float DecayHFRatio() const { return 0.1f + m_param[kI3DL2ReverbDecayHFRatio] * 1.9f; } - float Reflections() const { return -10000.0f + m_param[kI3DL2ReverbReflections] * 11000.0f; }; + float Reflections() const { return -10000.0f + m_param[kI3DL2ReverbReflections] * 11000.0f; } float ReflectionsDelay() const { return m_param[kI3DL2ReverbReflectionsDelay] * 0.3f; } - float Reverb() const { return -10000.0f + m_param[kI3DL2ReverbReverb] * 12000.0f; }; + float Reverb() const { return -10000.0f + m_param[kI3DL2ReverbReverb] * 12000.0f; } float ReverbDelay() const { return m_param[kI3DL2ReverbReverbDelay] * 0.1f; } float Diffusion() const { return m_param[kI3DL2ReverbDiffusion] * 100.0f; } float Density() const { return m_param[kI3DL2ReverbDensity] * 100.0f; } float HFReference() const { return 20.0f + m_param[kI3DL2ReverbHFReference] * 19980.0f; } - uint32 Quality() const { return Util::Round(m_param[kI3DL2ReverbQuality] * 3.0f); } + uint32 Quality() const { return mpt::saturate_round(m_param[kI3DL2ReverbQuality] * 3.0f); } void RecalculateI3DL2ReverbParams(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.cpp index 8f4304400..f139b4c10 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.cpp @@ -145,12 +145,12 @@ void WavesReverb::Resume() { m_isResumed = true; // Recalculate delays - uint32 delay0 = Util::Round(m_SndFile.GetSampleRate() * 0.045f); - uint32 delay1 = Util::Round(delay0 * 1.18920707f); // 2^0.25 - uint32 delay2 = Util::Round(delay1 * 1.18920707f); - uint32 delay3 = Util::Round(delay2 * 1.18920707f); - uint32 delay4 = Util::Round((delay0 + delay2) * 0.11546667f); - uint32 delay5 = Util::Round((delay1 + delay3) * 0.11546667f); + uint32 delay0 = mpt::saturate_round(m_SndFile.GetSampleRate() * 0.045f); + uint32 delay1 = mpt::saturate_round(delay0 * 1.18920707f); // 2^0.25 + uint32 delay2 = mpt::saturate_round(delay1 * 1.18920707f); + uint32 delay3 = mpt::saturate_round(delay2 * 1.18920707f); + uint32 delay4 = mpt::saturate_round((delay0 + delay2) * 0.11546667f); + uint32 delay5 = mpt::saturate_round((delay1 + delay3) * 0.11546667f); // Comb delays m_delay[0] = delay0 - delay4; m_delay[1] = delay2 - delay4; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp index 25e8e5da6..84593272e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp @@ -39,7 +39,7 @@ namespace CTuningS11n void operator()(std::ostream& oStrm, const std::vector& v); uint16 m_nWriteCount; - static const uint16 s_nDefaultWriteCount = (uint16_max >> 2); + enum : uint16 { s_nDefaultWriteCount = (uint16_max >> 2) }; }; } @@ -54,6 +54,9 @@ Version changes: */ +MPT_STATIC_ASSERT(CTuningRTI::s_RatioTableFineSizeMaxDefault < static_cast(FINESTEPCOUNT_MAX)); + + CTuningRTI::CTuningRTI() : m_TuningType(TT_GENERAL) , m_FineStepCount(0) @@ -250,7 +253,7 @@ RATIOTYPE CTuningRTI::GetRatioFine(const NOTEINDEXTYPE& note, USTEPINDEXTYPE sd) else //Calculating ratio 'on the fly'. { //'Geometric finestepping'. - return pow(GetRatio(note+1) / GetRatio(note), static_cast(sd)/(GetFineStepCount()+1)); + return std::pow(GetRatio(note+1) / GetRatio(note), static_cast(sd)/(GetFineStepCount()+1)); } @@ -294,7 +297,7 @@ bool CTuningRTI::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) void CTuningRTI::SetFineStepCount(const USTEPINDEXTYPE& fs) { - m_FineStepCount = mpt::clamp(mpt::saturate_cast(fs), 0, FINESTEPCOUNT_MAX); + m_FineStepCount = mpt::clamp(mpt::saturate_cast(fs), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); } @@ -315,7 +318,7 @@ void CTuningRTI::UpdateFineStepTable() } m_RatioTableFine.resize(m_FineStepCount); const RATIOTYPE q = GetRatio(GetValidityRange().first + 1) / GetRatio(GetValidityRange().first); - const RATIOTYPE rFineStep = pow(q, static_cast(1)/(m_FineStepCount+1)); + const RATIOTYPE rFineStep = std::pow(q, static_cast(1)/(m_FineStepCount+1)); for(USTEPINDEXTYPE i = 1; i<=m_FineStepCount; i++) m_RatioTableFine[i-1] = std::pow(rFineStep, static_cast(i)); return; @@ -338,10 +341,10 @@ void CTuningRTI::UpdateFineStepTable() for(UNOTEINDEXTYPE i = 0; i(1)/(m_FineStepCount+1)); + const RATIOTYPE rFineStep = std::pow(GetRatio(refnote+1) / GetRatio(refnote), static_cast(1)/(m_FineStepCount+1)); for(UNOTEINDEXTYPE j = 1; j<=m_FineStepCount; j++) { - m_RatioTableFine[m_FineStepCount * refnote + (j-1)] = pow(rFineStep, static_cast(j)); + m_RatioTableFine[m_FineStepCount * refnote + (j-1)] = std::pow(rFineStep, static_cast(j)); } } return; @@ -409,6 +412,7 @@ SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) { return SerializationResult::Failure; } + m_FineStepCount = mpt::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); if(m_RatioTable.size() > static_cast(NOTEINDEXTYPE_MAX)) { return SerializationResult::Failure; @@ -667,6 +671,7 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) { m_FineStepCount -= 1; } + m_FineStepCount = mpt::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); if(m_TuningType == TT_GEOMETRIC) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h index 57467c477..050e6a1e2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include #include "tuningbase.h" @@ -36,9 +38,9 @@ public: }; static const RATIOTYPE s_DefaultFallbackRatio; - static const NOTEINDEXTYPE s_StepMinDefault = -64; - static const UNOTEINDEXTYPE s_RatioTableSizeDefault = 128; - static const USTEPINDEXTYPE s_RatioTableFineSizeMaxDefault = 1000; + enum : NOTEINDEXTYPE { s_StepMinDefault = -64 }; + enum : UNOTEINDEXTYPE { s_RatioTableSizeDefault = 128 }; + enum : USTEPINDEXTYPE { s_RatioTableFineSizeMaxDefault = 1000 }; public: diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp index 6ecaa6068..c8027059b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp @@ -15,6 +15,9 @@ #include #include "../common/mptFileIO.h" #include "Loaders.h" +#ifdef MODPLUG_TRACKER +#include "../mptrack/TrackerSettings.h" +#endif //MODPLUG_TRACKER OPENMPT_NAMESPACE_BEGIN @@ -141,9 +144,9 @@ Tuning::SerializationResult CTuningCollection::DeserializeOLD(std::istream& inSt { //1. begin marker: - int32 beginMarker = 0; - mpt::IO::ReadIntLE(inStrm, beginMarker); - if(beginMarker != MAGIC4BE('T','C','S','H')) + uint32 beginMarker = 0; + mpt::IO::ReadIntLE(inStrm, beginMarker); + if(beginMarker != MagicBE("TCSH")) // Magic is reversed in file, hence BE return Tuning::SerializationResult::NoMagic; //2. version @@ -184,9 +187,9 @@ Tuning::SerializationResult CTuningCollection::DeserializeOLD(std::istream& inSt } //6. End marker - int32 endMarker = 0; - mpt::IO::ReadIntLE(inStrm, endMarker); - if(endMarker != MAGIC4BE('T','C','S','F')) + uint32 endMarker = 0; + mpt::IO::ReadIntLE(inStrm, endMarker); + if(endMarker != MagicBE("TCSF")) // Magic is reversed in file, hence BE return Tuning::SerializationResult::Failure; return Tuning::SerializationResult::Success; @@ -271,22 +274,21 @@ bool UnpackTuningCollection(const CTuningCollection &tc, const mpt::PathString & mpt::ustring tuningName = mpt::ToUnicode(mpt::CharsetLocale, tuning.GetName()); if(tuningName.empty()) { - tuningName = MPT_USTRING("untitled"); + tuningName = U_("untitled"); } SanitizeFilename(tuningName); - fn += mpt::PathString::FromUnicode(mpt::format(MPT_USTRING("%1 - %2"))(numberFmt.ToWString(i + 1), tuningName)); + fn += mpt::PathString::FromUnicode(mpt::format(U_("%1 - %2"))(mpt::ufmt::fmt(i + 1, numberFmt), tuningName)); fn += mpt::PathString::FromUTF8(CTuning::s_FileExtension); if(fn.FileOrDirectoryExists()) { error = true; } else { - mpt::ofstream fout(fn, std::ios::binary); - if(tuning.Serialize(fout) != Tuning::SerializationResult::Success) + mpt::SafeOutputFile sfout(fn, std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave)); + if(tuning.Serialize(sfout) != Tuning::SerializationResult::Success) { error = true; } - fout.close(); } } return !error; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h index 7a94be1c7..14654566c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h @@ -10,9 +10,10 @@ #pragma once +#include "BuildSettings.h" + #include -#include "../common/typedefs.h" OPENMPT_NAMESPACE_BEGIN diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h index 7e4b4ff06..7e388f4fa 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h @@ -10,6 +10,8 @@ #pragma once +#include "BuildSettings.h" + #include "tuning.h" #include #include @@ -38,7 +40,7 @@ public: // user to additionally import both built-in tunings. // Older OpenMPT versions will silently skip loading tunings beyond index // 255. - static const size_t s_nMaxTuningCount = 255 + 255 + 2; + enum : size_t { s_nMaxTuningCount = 255 + 255 + 2 }; public: diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestTools.h b/Frameworks/OpenMPT/OpenMPT/test/TestTools.h index 5ef6f1215..4261a83b4 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestTools.h +++ b/Frameworks/OpenMPT/OpenMPT/test/TestTools.h @@ -9,6 +9,8 @@ #pragma once +#include "BuildSettings.h" + #include "TestToolsTracker.h" #include "TestToolsLib.h" diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp index 2ce811b11..9a3caac11 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp @@ -37,27 +37,11 @@ static std::string remove_newlines(std::string str) } -Context::Context(const char * file, int line) - : file(file) - , line(line) -{ - return; -} - - -Context::Context(const Context &c) - : file(c.file) - , line(c.line) -{ - return; -} - - -Testcase::Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const Context &context) +Testcase::Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const mpt::source_location &loc) : fatality(fatality) , verbosity(verbosity) , desc(desc) - , context(context) + , loc(loc) { return; } @@ -65,7 +49,7 @@ Testcase::Testcase(Fatality fatality, Verbosity verbosity, const char * const de std::string Testcase::AsString() const { - return mpt::format(std::string("%1(%2): %3"))(context.file, context.line, remove_newlines(desc)); + return mpt::format(std::string("%1(%2): %3"))(loc.file_name() ? loc.file_name() : "", loc.line(), remove_newlines(desc)); } @@ -76,10 +60,14 @@ void Testcase::ShowStart() const case VerbosityQuiet: break; case VerbosityNormal: +#if !MPT_OS_DJGPP std::cout << "TEST..: " << AsString() << ": " << std::endl; +#endif break; case VerbosityVerbose: +#if !MPT_OS_DJGPP std::cout << "TEST..: " << AsString() << ": " << std::endl; +#endif break; } } @@ -94,7 +82,11 @@ void Testcase::ShowProgress(const char * text) const case VerbosityNormal: break; case VerbosityVerbose: +#if !MPT_OS_DJGPP std::cout << "TEST..: " << AsString() << ": " << text << std::endl; +#else + MPT_UNUSED_VARIABLE(text); +#endif break; } } @@ -107,10 +99,14 @@ void Testcase::ShowPass() const case VerbosityQuiet: break; case VerbosityNormal: +#if !MPT_OS_DJGPP std::cout << "RESULT: PASS" << std::endl; +#endif break; case VerbosityVerbose: +#if !MPT_OS_DJGPP std::cout << "PASS..: " << AsString() << std::endl; +#endif break; } } @@ -195,18 +191,18 @@ void Testcase::ReportException() #if defined(MPT_ASSERT_HANDLER_NEEDED) -MPT_NOINLINE void AssertHandler(const char *file, int line, const char *function, const char *expr, const char *msg) +MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg) { Test::fail_count++; if(msg) { - mpt::log::Logger().SendLogMessage(mpt::log::Context(file, line, function), LogError, "ASSERT", - MPT_USTRING("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + MPT_USTRING(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + MPT_USTRING(")") + mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_(")") ); } else { - mpt::log::Logger().SendLogMessage(mpt::log::Context(file, line, function), LogError, "ASSERT", - MPT_USTRING("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) + mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h index bbac760a8..8202fceea 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + #ifdef ENABLE_TESTS #ifndef MODPLUG_TRACKER @@ -19,6 +21,8 @@ //#define MPT_TEST_CXX11 +#include + #include "../common/Endianness.h" #include "../common/FlagSet.h" #include "../soundlib/Snd_defs.h" @@ -47,19 +51,6 @@ enum Fatality }; -struct Context -{ -public: - const char * const file; - const int line; -public: - Context(const char * file, int line); - Context(const Context &c); -}; - -#define MPT_TEST_CONTEXT_CURRENT() (Test::Context( __FILE__ , __LINE__ )) - - struct TestFailed { std::string values; @@ -81,11 +72,13 @@ struct ToStringHelper #ifdef MPT_TEST_CXX11 template<> -struct ToStringHelper +struct ToStringHelper { - std::string operator () (const mpt::endian_type &x) + std::string operator () (const mpt::endian &x) { - return mpt::fmt::val(x.value); + if(x == mpt::endian::big) return "big"; + if(x == mpt::endian::little) return "little"; + return "unknown"; } }; @@ -139,18 +132,6 @@ struct ToStringHelper namespace Test { -// We do not generally have type_traits from C++03-TR1 -// and std::numeric_limits does not provide a is_integer which is useable as template argument. -template struct is_integer : public std::false_type { }; -template <> struct is_integer : public std::true_type { }; -template <> struct is_integer : public std::true_type { }; -template <> struct is_integer : public std::true_type { }; -template <> struct is_integer : public std::true_type { }; -template <> struct is_integer : public std::true_type { }; -template <> struct is_integer : public std::true_type { }; -template <> struct is_integer : public std::true_type { }; -template <> struct is_integer : public std::true_type { }; - class Testcase { @@ -159,11 +140,11 @@ private: Fatality const fatality; Verbosity const verbosity; const char * const desc; - Context const context; + mpt::source_location const loc; public: - Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const Context &context); + Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const mpt::source_location &loc); public: @@ -221,7 +202,7 @@ private: template MPT_NOINLINE void TypeCompareHelper(const Tx &x, const Ty &y) { - if(!IsEqual(x, y, is_integer(), is_integer())) + if(!IsEqual(x, y, std::is_integral(), std::is_integral())) { throw TestFailed(mpt::format(std::string("%1 != %2"))(ToStringHelper()(x), ToStringHelper()(y))); //throw TestFailed(); @@ -278,11 +259,11 @@ public: } } - #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) - #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) - #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) + #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) + #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) + #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} ) - #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;}, (eps) ) + #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;}, (eps) ) #else @@ -294,7 +275,7 @@ public: ShowStart(); try { - if(!IsEqual(x, y, is_integer(), is_integer())) + if(!IsEqual(x, y, std::is_integral(), std::is_integral())) { //throw TestFailed(mpt::format(std::string("%1 != %2"))(x, y)); throw TestFailed(); @@ -324,11 +305,11 @@ public: } } - #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( (x) , (y) ) - #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( (x) , (y) ) - #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( (x) , (y) ) + #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) + #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) + #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) ) - #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_TEST_CONTEXT_CURRENT() )( (x) , (y), (eps) ) + #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y), (eps) ) #endif @@ -337,7 +318,7 @@ public: #define DO_TEST(func) \ MPT_DO { \ - Test::Testcase test(Test::FatalityStop, Test::VerbosityVerbose, #func , MPT_TEST_CONTEXT_CURRENT() ); \ + Test::Testcase test(Test::FatalityStop, Test::VerbosityVerbose, #func , MPT_SOURCE_LOCATION_CURRENT() ); \ try { \ test.ShowStart(); \ fail_count = 0; \ diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h b/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h index 87da53485..978efa508 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + #ifdef ENABLE_TESTS #ifdef MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/test/test.cpp b/Frameworks/OpenMPT/OpenMPT/test/test.cpp index d999ae696..2863c5b9b 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/test.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/test.cpp @@ -18,7 +18,7 @@ #include "../common/version.h" #include "../common/misc_util.h" #include "../common/mptCRC.h" -#include "../common/StringFixer.h" +#include "../common/mptStringBuffer.h" #include "../common/serialization_utils.h" #include "../common/mptUUID.h" #include "../soundlib/Sndfile.h" @@ -33,10 +33,12 @@ #include "../soundlib/tuningcollection.h" #include "../soundlib/tuning.h" #ifdef MODPLUG_TRACKER -#include "../mptrack/mptrack.h" -#include "../mptrack/moddoc.h" -#include "../mptrack/MainFrm.h" +#include "../mptrack/Mptrack.h" +#include "../mptrack/Moddoc.h" +#include "../mptrack/ModDocTemplate.h" +#include "../mptrack/Mainfrm.h" #include "../mptrack/Settings.h" +#include "../mptrack/HTTP.h" #endif // MODPLUG_TRACKER #include "../common/mptFileIO.h" #ifdef LIBOPENMPT_BUILD @@ -47,6 +49,9 @@ #endif #include "../common/mptBufferIO.h" #include +#ifdef LIBOPENMPT_BUILD +#include +#endif // LIBOPENMPT_BUILD #include #include #include @@ -63,10 +68,14 @@ #include #endif -#ifdef _DEBUG -#if MPT_COMPILER_MSVC && defined(_MFC_VER) - #define new DEBUG_NEW +#define MPT_TEST_HAS_FILESYSTEM 1 +#if MPT_OS_DJGPP +#undef MPT_TEST_HAS_FILESYSTEM +#define MPT_TEST_HAS_FILESYSTEM 0 #endif + +#if MPT_COMPILER_MSVC && defined(_MFC_VER) && defined(_DEBUG) + #define new DEBUG_NEW #endif #include "TestTools.h" @@ -105,7 +114,7 @@ static MPT_NOINLINE void TestEditing(); static mpt::PathString *PathPrefix = nullptr; -static mpt::prng * s_PRNG = nullptr; +static mpt::default_prng * s_PRNG = nullptr; @@ -113,15 +122,35 @@ mpt::PathString GetPathPrefix() { if((*PathPrefix).empty()) { - return MPT_PATHSTRING(""); + return P_(""); } - return *PathPrefix + MPT_PATHSTRING("/"); + return *PathPrefix + P_("/"); } void DoTests() { + #ifdef LIBOPENMPT_BUILD + + std::cout << std::flush; + std::clog << std::flush; + std::cerr << std::flush; + + std::cout << "libopenmpt test suite" << std::endl; + + std::cout << "Version.: " << mpt::ToCharset(mpt::CharsetASCII, Build::GetVersionString(Build::StringVersion | Build::StringRevision | Build::StringBitness | Build::StringSourceInfo | Build::StringBuildFlags | Build::StringBuildFeatures)) << std::endl; + std::cout << "Compiler: " << mpt::ToCharset(mpt::CharsetASCII, Build::GetBuildCompilerString()) << std::endl; + #if MPT_OS_WINDOWS + std::cout << "Required Windows Kernel Level: " << mpt::ToCharset(mpt::CharsetASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumKernelLevel())) << std::endl; + std::cout << "Required Windows API Level...: " << mpt::ToCharset(mpt::CharsetASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumAPILevel())) << std::endl; + std::cout << "Windows.: " << mpt::ToCharset(mpt::CharsetASCII, mpt::Windows::Version::Current().GetName()) << std::endl; + #endif + + std::cout << std::flush; + + #endif + #if MPT_OS_WINDOWS // prefix for test suite @@ -145,7 +174,7 @@ void DoTests() pathprefix = L"../../"; } - PathPrefix = new mpt::PathString(mpt::PathString::FromNative(pathprefix)); + PathPrefix = new mpt::PathString(mpt::PathString::FromWide(pathprefix)); #else @@ -163,7 +192,7 @@ void DoTests() #endif mpt::random_device rd; - s_PRNG = new mpt::prng(mpt::make_prng(rd)); + s_PRNG = new mpt::default_prng(mpt::make_prng(rd)); DO_TEST(TestVersion); DO_TEST(TestTypes); @@ -200,7 +229,7 @@ static void RemoveFile(const mpt::PathString &filename) #if MPT_OS_WINDOWS for(int retry=0; retry<10; retry++) { - if(DeleteFileW(filename.AsNative().c_str()) != FALSE) + if(DeleteFile(filename.AsNative().c_str()) != FALSE) { break; } @@ -218,45 +247,43 @@ static MPT_NOINLINE void TestVersion() { //Verify that macros and functions work. { - VERIFY_EQUAL( MptVersion::ToNum(MptVersion::ToStr(MptVersion::num)), MptVersion::num ); - VERIFY_EQUAL( MptVersion::ToStr(MptVersion::ToNum(MptVersion::str)), MptVersion::str ); - VERIFY_EQUAL( MptVersion::ToStr(18285096), "1.17.02.28" ); - VERIFY_EQUAL( MptVersion::ToNum("1.17.02.28"), MptVersion::VersionNum(18285096) ); - VERIFY_EQUAL( MptVersion::ToNum("1.fe.02.28"), MptVersion::VersionNum(0x01fe0228) ); - VERIFY_EQUAL( MptVersion::ToNum("01.fe.02.28"), MptVersion::VersionNum(0x01fe0228) ); - VERIFY_EQUAL( MptVersion::ToNum("1.22"), MptVersion::VersionNum(0x01220000) ); - VERIFY_EQUAL( MptVersion::ToNum(MptVersion::str), MptVersion::num ); - VERIFY_EQUAL( MptVersion::ToStr(MptVersion::num), MptVersion::str ); - VERIFY_EQUAL( MptVersion::RemoveBuildNumber(MAKE_VERSION_NUMERIC(1,19,02,00)), MAKE_VERSION_NUMERIC(1,19,02,00)); - VERIFY_EQUAL( MptVersion::RemoveBuildNumber(MAKE_VERSION_NUMERIC(1,18,03,20)), MAKE_VERSION_NUMERIC(1,18,03,00)); - VERIFY_EQUAL( MptVersion::IsTestBuild(MAKE_VERSION_NUMERIC(1,18,01,13)), true); - VERIFY_EQUAL( MptVersion::IsTestBuild(MAKE_VERSION_NUMERIC(1,19,01,00)), false); - VERIFY_EQUAL( MptVersion::IsTestBuild(MAKE_VERSION_NUMERIC(1,17,02,54)), false); - VERIFY_EQUAL( MptVersion::IsTestBuild(MAKE_VERSION_NUMERIC(1,18,00,00)), false); - VERIFY_EQUAL( MptVersion::IsTestBuild(MAKE_VERSION_NUMERIC(1,18,02,00)), false); - VERIFY_EQUAL( MptVersion::IsTestBuild(MAKE_VERSION_NUMERIC(1,18,02,01)), true); + VERIFY_EQUAL( Version::Parse(Version::Current().ToUString()), Version::Current() ); + VERIFY_EQUAL( Version::Parse(Version::Current().ToUString()).ToUString(), Version::Current().ToUString() ); + VERIFY_EQUAL( Version(18285096).ToUString(), U_("1.17.02.28") ); + VERIFY_EQUAL( Version::Parse(U_("1.17.02.28")), Version(18285096) ); + VERIFY_EQUAL( Version::Parse(U_("1.fe.02.28")), Version(0x01fe0228) ); + VERIFY_EQUAL( Version::Parse(U_("01.fe.02.28")), Version(0x01fe0228) ); + VERIFY_EQUAL( Version::Parse(U_("1.22")), Version(0x01220000) ); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,19,02,00).WithoutTestNumber(), MAKE_VERSION_NUMERIC(1,19,02,00)); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,03,20).WithoutTestNumber(), MAKE_VERSION_NUMERIC(1,18,03,00)); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,01,13).IsTestVersion(), true); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,19,01,00).IsTestVersion(), false); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,17,02,54).IsTestVersion(), false); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,00,00).IsTestVersion(), false); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,02,00).IsTestVersion(), false); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,02,01).IsTestVersion(), true); // Ensure that versions ending in .00.00 (which are ambiguous to truncated version numbers in certain file formats (e.g. S3M and IT) do not get qualified as test builds. - VERIFY_EQUAL( MptVersion::IsTestBuild(MAKE_VERSION_NUMERIC(1,23,00,00)), false); + VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,23,00,00).IsTestVersion(), false); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(1,17,2,28) == 18285096 ); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(1,17,02,48) == 18285128 ); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,02,52) == 18285138 ); + STATIC_ASSERT( MAKE_VERSION_NUMERIC(1,17,2,28).GetRawVersion() == 18285096 ); + STATIC_ASSERT( MAKE_VERSION_NUMERIC(1,17,02,48).GetRawVersion() == 18285128 ); + STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,02,52).GetRawVersion() == 18285138 ); // Ensure that bit-shifting works (used in some mod loaders for example) - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,00,00) == 0x0117 << 16 ); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,03,00) >> 8 == 0x011703 ); + STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,00,00).GetRawVersion() == 0x0117 << 16 ); + STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,03,00).GetRawVersion() >> 8 == 0x011703 ); } #ifdef MODPLUG_TRACKER //Verify that the version obtained from the executable file is the same as - //defined in MptVersion. + //defined in Version. { WCHAR szFullPath[MAX_PATH]; DWORD dwVerHnd; DWORD dwVerInfoSize; // Get version information from the application - ::GetModuleFileNameW(NULL, szFullPath, mpt::size(szFullPath)); + ::GetModuleFileNameW(NULL, szFullPath, mpt::saturate_cast(mpt::size(szFullPath))); dwVerInfoSize = ::GetFileVersionInfoSizeW(szFullPath, &dwVerHnd); if (!dwVerInfoSize) throw std::runtime_error("!dwVerInfoSize is true"); @@ -275,18 +302,17 @@ static MPT_NOINLINE void TestVersion() throw std::runtime_error("VerQueryValue() returned false"); } - std::string version = mpt::ToCharset(mpt::CharsetASCII, szVer); + mpt::ustring version = mpt::ToUnicode(szVer); - //version string should be like: 1,17,2,38 Change ',' to '.' to get format 1.17.2.38 - version = mpt::String::Replace(version, ",", "."); - - VERIFY_EQUAL( version, MptVersion::str ); - VERIFY_EQUAL( MptVersion::ToNum(version), MptVersion::num ); + VERIFY_EQUAL( version, mpt::ufmt::val(Version::Current()) ); + VERIFY_EQUAL( Version::Parse(version), Version::Current() ); } #endif #ifdef LIBOPENMPT_BUILD - mpt::PathString version_mk = GetPathPrefix() + MPT_PATHSTRING("libopenmpt/libopenmpt_version.mk"); +#if MPT_TEST_HAS_FILESYSTEM +#if !MPT_OS_DJGPP + mpt::PathString version_mk = GetPathPrefix() + P_("libopenmpt/libopenmpt_version.mk"); mpt::ifstream f(version_mk, std::ios::in); VERIFY_EQUAL(f ? true : false, true); std::map fields; @@ -318,6 +344,8 @@ static MPT_NOINLINE void TestVersion() VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_CURRENT"].length() > 0, true); VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_REVISION"].length() > 0, true); VERIFY_EQUAL(fields["LIBOPENMPT_LTVER_AGE"].length() > 0, true); +#endif // !MPT_OS_DJGPP +#endif // MPT_TEST_HAS_FILESYSTEM #endif // LIBOPENMPT_BUILD } @@ -326,6 +354,13 @@ static MPT_NOINLINE void TestVersion() // Test if data types are interpreted correctly static MPT_NOINLINE void TestTypes() { + + MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); + #if defined(__SIZEOF_POINTER__) + MPT_STATIC_ASSERT(__SIZEOF_POINTER__ == mpt::pointer_size); + MPT_STATIC_ASSERT(__SIZEOF_POINTER__ == sizeof(void*)); + #endif + VERIFY_EQUAL(int8_min, (std::numeric_limits::min)()); VERIFY_EQUAL(int8_max, (std::numeric_limits::max)()); VERIFY_EQUAL(uint8_max, (std::numeric_limits::max)()); @@ -343,17 +378,54 @@ static MPT_NOINLINE void TestTypes() VERIFY_EQUAL(uint64_max, (std::numeric_limits::max)()); - STATIC_ASSERT(int8_max == MPT_MAX_VALUE_OF_TYPE(int8)); - STATIC_ASSERT(uint8_max == MPT_MAX_VALUE_OF_TYPE(uint8)); + STATIC_ASSERT(int8_max == (std::numeric_limits::max)()); + STATIC_ASSERT(uint8_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int16_max == MPT_MAX_VALUE_OF_TYPE(int16)); - STATIC_ASSERT(uint16_max == MPT_MAX_VALUE_OF_TYPE(uint16)); + STATIC_ASSERT(int16_max == (std::numeric_limits::max)()); + STATIC_ASSERT(uint16_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int32_max == MPT_MAX_VALUE_OF_TYPE(int32)); - STATIC_ASSERT(uint32_max == MPT_MAX_VALUE_OF_TYPE(uint32)); + STATIC_ASSERT(int32_max == (std::numeric_limits::max)()); + STATIC_ASSERT(uint32_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int64_max == MPT_MAX_VALUE_OF_TYPE(int64)); - STATIC_ASSERT(uint64_max == MPT_MAX_VALUE_OF_TYPE(uint64)); + STATIC_ASSERT(int64_max == (std::numeric_limits::max)()); + STATIC_ASSERT(uint64_max == (std::numeric_limits::max)()); + + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); } @@ -389,24 +461,27 @@ static std::string StringFormat(const char *format, T x) #endif -static void TestFloatFormat(double x, const char * format, mpt::FormatFlags f, std::size_t width = 0, int precision = -1) +template +static void TestFloatFormat(Tfloat x, const char * format, mpt::FormatFlags f, std::size_t width = 0, int precision = -1) { #ifdef MODPLUG_TRACKER std::string str_sprintf = StringFormat(format, x); #endif - std::string str_iostreams = mpt::FormatSpec().SetFlags(f).SetWidth(width).SetPrecision(precision).ToString(x); - std::string str_parsed = mpt::FormatSpec().ParsePrintf(format).ToString(x); + std::string str_iostreams = mpt::fmt::fmt(x, mpt::FormatSpec().SetFlags(f).SetWidth(width).SetPrecision(precision)); //Log("%s", str_sprintf.c_str()); //Log("%s", str_iostreams.c_str()); //Log("%s", str_iostreams.c_str()); #ifdef MODPLUG_TRACKER VERIFY_EQUAL(str_iostreams, str_sprintf); // this will fail with a set c locale (and there is nothing that can be done about that in libopenmpt) +#else + MPT_UNREFERENCED_PARAMETER(format); + MPT_UNUSED_VARIABLE(str_iostreams); #endif - VERIFY_EQUAL(str_iostreams, str_parsed); } -static void TestFloatFormats(double x) +template +static void TestFloatFormats(Tfloat x) { TestFloatFormat(x, "%g", mpt::fmt::NotaNrm | mpt::fmt::FillOff); @@ -418,10 +493,6 @@ static void TestFloatFormats(double x) TestFloatFormat(x, "%.1f", mpt::fmt::NotaFix | mpt::fmt::FillOff, 0, 1); TestFloatFormat(x, "%.2f", mpt::fmt::NotaFix | mpt::fmt::FillOff, 0, 2); TestFloatFormat(x, "%.3f", mpt::fmt::NotaFix | mpt::fmt::FillOff, 0, 3); - TestFloatFormat(x, "%1.1f", mpt::fmt::NotaFix | mpt::fmt::FillSpc, 1, 1); - TestFloatFormat(x, "%3.1f", mpt::fmt::NotaFix | mpt::fmt::FillSpc, 3, 1); - TestFloatFormat(x, "%4.1f", mpt::fmt::NotaFix | mpt::fmt::FillSpc, 4, 1); - TestFloatFormat(x, "%6.3f", mpt::fmt::NotaFix | mpt::fmt::FillSpc, 6, 3); TestFloatFormat(x, "%0.1f", mpt::fmt::NotaFix | mpt::fmt::FillNul, 0, 1); TestFloatFormat(x, "%02.0f", mpt::fmt::NotaFix | mpt::fmt::FillNul, 2, 0); } @@ -473,13 +544,34 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(mpt::fmt::val(-23), "-23"); VERIFY_EQUAL(mpt::fmt::val(42), "42"); - VERIFY_EQUAL(mpt::fmt::hex<3>((int32)-1), "ffffffff"); + VERIFY_EQUAL(mpt::fmt::hex0<3>((int32)-1), "-001"); + VERIFY_EQUAL(mpt::fmt::hex((int32)-1), "-1"); + VERIFY_EQUAL(mpt::fmt::hex(-0xabcde), "-abcde"); + VERIFY_EQUAL(mpt::fmt::hex(int32_min), "-80000000"); + VERIFY_EQUAL(mpt::fmt::hex(int32_min + 1), "-7fffffff"); VERIFY_EQUAL(mpt::fmt::hex(0x123e), "123e"); VERIFY_EQUAL(mpt::fmt::hex0<6>(0x123e), "00123e"); VERIFY_EQUAL(mpt::fmt::hex0<2>(0x123e), "123e"); + VERIFY_EQUAL(mpt::fmt::dec0<0>(1), "1"); + VERIFY_EQUAL(mpt::fmt::dec0<1>(1), "1"); + VERIFY_EQUAL(mpt::fmt::dec0<2>(1), "01"); + VERIFY_EQUAL(mpt::fmt::dec0<3>(1), "001"); + VERIFY_EQUAL(mpt::fmt::dec0<0>(11), "11"); + VERIFY_EQUAL(mpt::fmt::dec0<1>(11), "11"); + VERIFY_EQUAL(mpt::fmt::dec0<2>(11), "11"); + VERIFY_EQUAL(mpt::fmt::dec0<3>(11), "011"); + VERIFY_EQUAL(mpt::fmt::dec0<0>(-1), "-1"); + VERIFY_EQUAL(mpt::fmt::dec0<1>(-1), "-1"); + VERIFY_EQUAL(mpt::fmt::dec0<2>(-1), "-01"); + VERIFY_EQUAL(mpt::fmt::dec0<3>(-1), "-001"); + + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(0xa2345678), U_("A2345678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<8>(0xa2345678), U_("A2345678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<9>(0xa2345678), U_("0A2345678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<10>(0xa2345678), U_("00A2345678")); + #if MPT_WSTRING_FORMAT - VERIFY_EQUAL(mpt::wfmt::hex<3>((int32)-1), L"ffffffff"); VERIFY_EQUAL(mpt::wfmt::hex(0x123e), L"123e"); VERIFY_EQUAL(mpt::wfmt::hex0<6>(0x123e), L"00123e"); VERIFY_EQUAL(mpt::wfmt::hex0<2>(0x123e), L"123e"); @@ -494,8 +586,48 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(true, false); } VERIFY_EQUAL(mpt::fmt::val(58.65403492763), "58.654"); - VERIFY_EQUAL(mpt::FormatSpec("%3.1f").ToString(23.42), "23.4"); - VERIFY_EQUAL(mpt::fmt::f("%3.1f", 23.42), "23.4"); + VERIFY_EQUAL(mpt::fmt::fix(23.42, 1), "23.4"); + VERIFY_EQUAL(mpt::fmt::fix(234.2, 1), "234.2"); + VERIFY_EQUAL(mpt::fmt::fix(2342.0, 1), "2342.0"); + + VERIFY_EQUAL(mpt::fmt::dec(2, ';', 2345678), std::string("2;34;56;78")); + VERIFY_EQUAL(mpt::fmt::dec(2, ';', 12345678), std::string("12;34;56;78")); + VERIFY_EQUAL(mpt::fmt::hex(3, ':', 0xa2345678), std::string("a2:345:678")); + + VERIFY_EQUAL(mpt::ufmt::dec(2, ';', 12345678), U_("12;34;56;78")); + VERIFY_EQUAL(mpt::ufmt::hex(3, ':', 0xa2345678), U_("a2:345:678")); + + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', 0xa2345678), U_("A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<8>(3, ':', 0xa2345678), U_("A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<9>(3, ':', 0xa2345678), U_("0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<10>(3, ':', 0xa2345678), U_("0:0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<11>(3, ':', 0xa2345678), U_("00:0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<12>(3, ':', 0xa2345678), U_("000:0A2:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', -0x12345678), U_("-12:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<8>(3, ':', -0x12345678), U_("-12:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<9>(3, ':', -0x12345678), U_("-012:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<10>(3, ':', -0x12345678), U_("-0:012:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<11>(3, ':', -0x12345678), U_("-00:012:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<12>(3, ':', -0x12345678), U_("-000:012:345:678")); + + VERIFY_EQUAL(mpt::ufmt::HEX0<5>(3, ':', 0x345678), U_("345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<6>(3, ':', 0x345678), U_("345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', 0x345678), U_("0:345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<5>(3, ':', -0x345678), U_("-345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<6>(3, ':', -0x345678), U_("-345:678")); + VERIFY_EQUAL(mpt::ufmt::HEX0<7>(3, ':', -0x345678), U_("-0:345:678")); + + VERIFY_EQUAL(mpt::fmt::left(3, "a"), "a "); + VERIFY_EQUAL(mpt::fmt::right(3, "a"), " a"); + VERIFY_EQUAL(mpt::fmt::center(3, "a"), " a "); + VERIFY_EQUAL(mpt::fmt::center(4, "a"), " a "); + + #if defined(_MFC_VER) + VERIFY_EQUAL(mpt::cfmt::left(3, CString(_T("a"))), CString(_T("a "))); + VERIFY_EQUAL(mpt::cfmt::right(3, CString(_T("a"))), CString(_T(" a"))); + VERIFY_EQUAL(mpt::cfmt::center(3, CString(_T("a"))), CString(_T(" a "))); + VERIFY_EQUAL(mpt::cfmt::center(4, CString(_T("a"))), CString(_T(" a "))); + #endif VERIFY_EQUAL(ConvertStrTo("586"), 586u); VERIFY_EQUAL(ConvertStrTo("2147483647"), (uint32)int32_max); @@ -509,18 +641,26 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(ConvertStrTo("9223372036854775807"), (uint64)int64_max); VERIFY_EQUAL(ConvertStrTo("18446744073709551615"), uint64_max); - VERIFY_EQUAL(ConvertStrTo("-87.0"), -87.0); + VERIFY_EQUAL(ConvertStrTo("-87.0"), -87.0f); +#if !MPT_OS_DJGPP VERIFY_EQUAL(ConvertStrTo("-0.5e-6"), -0.5e-6); +#endif +#if !MPT_OS_DJGPP VERIFY_EQUAL(ConvertStrTo("58.65403492763"), 58.65403492763); +#else + VERIFY_EQUAL_EPS(ConvertStrTo("58.65403492763"), 58.65403492763, 0.0001); +#endif - VERIFY_EQUAL(ConvertStrTo(mpt::fmt::val(-87.0)), -87.0); + VERIFY_EQUAL(ConvertStrTo(mpt::fmt::val(-87.0)), -87.0f); +#if !MPT_OS_DJGPP VERIFY_EQUAL(ConvertStrTo(mpt::fmt::val(-0.5e-6)), -0.5e-6); +#endif VERIFY_EQUAL(mpt::String::Parse::Hex("fe"), 254); #if MPT_WSTRING_FORMAT VERIFY_EQUAL(mpt::String::Parse::Hex(L"fe"), 254); #endif - VERIFY_EQUAL(mpt::String::Parse::Hex(MPT_USTRING("ffff")), 65535); + VERIFY_EQUAL(mpt::String::Parse::Hex(U_("ffff")), 65535); TestFloatFormats(0.0f); TestFloatFormats(1.0f); @@ -533,27 +673,31 @@ static MPT_NOINLINE void TestStringFormatting() TestFloatFormats(-0.0000000001f); TestFloatFormats(6.12345f); + TestFloatFormats(0.0); + TestFloatFormats(1.0); + TestFloatFormats(-1.0); + TestFloatFormats(0.1); + TestFloatFormats(-0.1); + TestFloatFormats(1000000000.0); + TestFloatFormats(-1000000000.0); + TestFloatFormats(0.0000000001); + TestFloatFormats(-0.0000000001); + TestFloatFormats(6.12345); + TestFloatFormats(42.1234567890); TestFloatFormats(0.1234567890); TestFloatFormats(1234567890000000.0); TestFloatFormats(0.0000001234567890); - VERIFY_EQUAL(mpt::FormatSpec().ParsePrintf("%7.3f").ToString(6.12345), " 6.123"); - VERIFY_EQUAL(mpt::fmt::f("%7.3f", 6.12345), " 6.123"); - VERIFY_EQUAL(mpt::fmt::flt(6.12345, 7, 3), " 6.123"); - VERIFY_EQUAL(mpt::fmt::fix(6.12345, 7, 3), " 6.123"); - VERIFY_EQUAL(mpt::fmt::flt(6.12345, 0, 4), "6.123"); - #if !(MPT_OS_EMSCRIPTEN && MPT_OS_EMSCRIPTEN_ANCIENT) - VERIFY_EQUAL(mpt::fmt::fix(6.12345, 0, 4), "6.1235"); - #else - // emscripten(1.21)/nodejs(v0.10.25) print 6.1234 instead of 6.1235 for unknown reasons. - // As this test case is not fatal, ignore it for now in order to make the test cases pass. - #endif + VERIFY_EQUAL(mpt::fmt::flt(6.12345, 3), "6.12"); + VERIFY_EQUAL(mpt::fmt::fix(6.12345, 3), "6.123"); + VERIFY_EQUAL(mpt::fmt::flt(6.12345, 4), "6.123"); + VERIFY_EQUAL(mpt::fmt::fix(6.12345, 4), "6.1235"); #if MPT_WSTRING_FORMAT - VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 7, 3), L" 6.123"); - VERIFY_EQUAL(mpt::wfmt::fix(6.12345, 7, 3), L" 6.123"); - VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 0, 4), L"6.123"); + VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 3), L"6.12"); + VERIFY_EQUAL(mpt::wfmt::fix(6.12345, 3), L"6.123"); + VERIFY_EQUAL(mpt::wfmt::flt(6.12345, 4), L"6.123"); #endif // basic @@ -581,10 +725,10 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(mpt::format("%b")("a"), "%b"); #if defined(_MFC_VER) - VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), MPT_USTRING("foobar")); - VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), MPT_USTRING("foobar")); + VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); + VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); VERIFY_EQUAL(mpt::format(CString(_T("%1%2%3")))(1,2,3), _T("123")); - VERIFY_EQUAL(mpt::format(CString(_T("%1%2%3")))(1,mpt::tfmt::dec0<3>(2),3), _T("10023")); + VERIFY_EQUAL(mpt::format(CString(_T("%1%2%3")))(1,mpt::cfmt::dec0<3>(2),3), _T("10023")); #endif } @@ -631,25 +775,49 @@ Gregorian TestDate2(int s, int m, int h, int D, int M, int Y) { return Gregorian{Y,M,D,h,m,s}; } +#if MPT_ENDIAN_IS_CONSTEXPR +static constexpr int32le TestEndianConstexpr(uint32 x) +{ + int32le foo; + foo = x; + return foo; +} +#endif + static MPT_NOINLINE void TestMisc1() { - VERIFY_EQUAL(mpt::endian(), mpt::detail::endian_probe()); - VERIFY_EQUAL((mpt::endian() == mpt::endian_big) || (mpt::endian() == mpt::endian_little), true); + #if MPT_CXX_BEFORE(20) + VERIFY_EQUAL(mpt::get_endian(), mpt::detail::endian_probe()); + #endif + #if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) + VERIFY_EQUAL(mpt::get_endian(), mpt::endian::little); + VERIFY_EQUAL(mpt::endian::native, mpt::endian::little); + #if MPT_CXX_BEFORE(20) + VERIFY_EQUAL(mpt::detail::endian_probe(), mpt::endian::little); + #endif + #elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) + VERIFY_EQUAL(mpt::get_endian(), mpt::endian::big); + VERIFY_EQUAL(mpt::endian::native, mpt::endian::big); + #if MPT_CXX_BEFORE(20) + VERIFY_EQUAL(mpt::detail::endian_probe(), mpt::endian::big); + #endif + #endif -#define SwapBytesReturn(x) SwapBytesLE(SwapBytesBE(x)) +#if MPT_ENDIAN_IS_CONSTEXPR + constexpr int32le foo = TestEndianConstexpr(23); + (void)foo; +#endif - VERIFY_EQUAL(SwapBytesReturn(uint8(0x12)), 0x12); - VERIFY_EQUAL(SwapBytesReturn(uint16(0x1234)), 0x3412); - VERIFY_EQUAL(SwapBytesReturn(uint32(0x12345678u)), 0x78563412u); - VERIFY_EQUAL(SwapBytesReturn(uint64(0x123456789abcdef0ull)), 0xf0debc9a78563412ull); + VERIFY_EQUAL(mpt::detail::SwapBytes(uint8(0x12)), 0x12); + VERIFY_EQUAL(mpt::detail::SwapBytes(uint16(0x1234)), 0x3412); + VERIFY_EQUAL(mpt::detail::SwapBytes(uint32(0x12345678u)), 0x78563412u); + VERIFY_EQUAL(mpt::detail::SwapBytes(uint64(0x123456789abcdef0ull)), 0xf0debc9a78563412ull); - VERIFY_EQUAL(SwapBytesReturn(int8(int8_min)), int8_min); - VERIFY_EQUAL(SwapBytesReturn(int16(int16_min)), int16(0x80)); - VERIFY_EQUAL(SwapBytesReturn(int32(int32_min)), int32(0x80)); - VERIFY_EQUAL(SwapBytesReturn(int64(int64_min)), int64(0x80)); - -#undef SwapBytesReturn + VERIFY_EQUAL(mpt::detail::SwapBytes(int8(int8_min)), int8_min); + VERIFY_EQUAL(mpt::detail::SwapBytes(int16(int16_min)), int16(0x80)); + VERIFY_EQUAL(mpt::detail::SwapBytes(int32(int32_min)), int32(0x80)); + VERIFY_EQUAL(mpt::detail::SwapBytes(int64(int64_min)), int64(0x80)); VERIFY_EQUAL(EncodeIEEE754binary32(1.0f), 0x3f800000u); VERIFY_EQUAL(EncodeIEEE754binary32(-1.0f), 0xbf800000u); @@ -663,10 +831,10 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(DecodeIEEE754binary32(0x3f800000u), 1.0f); VERIFY_EQUAL(IEEE754binary32LE(1.0f).GetInt32(), 0x3f800000u); VERIFY_EQUAL(IEEE754binary32BE(1.0f).GetInt32(), 0x3f800000u); - VERIFY_EQUAL(IEEE754binary32LE(0x00,0x00,0x80,0x3f), 1.0f); - VERIFY_EQUAL(IEEE754binary32BE(0x3f,0x80,0x00,0x00), 1.0f); - VERIFY_EQUAL(IEEE754binary32LE(1.0f), IEEE754binary32LE(0x00,0x00,0x80,0x3f)); - VERIFY_EQUAL(IEEE754binary32BE(1.0f), IEEE754binary32BE(0x3f,0x80,0x00,0x00)); + VERIFY_EQUAL(IEEE754binary32LE(mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x80),mpt::as_byte(0x3f)), 1.0f); + VERIFY_EQUAL(IEEE754binary32BE(mpt::as_byte(0x3f),mpt::as_byte(0x80),mpt::as_byte(0x00),mpt::as_byte(0x00)), 1.0f); + VERIFY_EQUAL(IEEE754binary32LE(1.0f), IEEE754binary32LE(mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x80),mpt::as_byte(0x3f))); + VERIFY_EQUAL(IEEE754binary32BE(1.0f), IEEE754binary32BE(mpt::as_byte(0x3f),mpt::as_byte(0x80),mpt::as_byte(0x00),mpt::as_byte(0x00))); VERIFY_EQUAL(EncodeIEEE754binary64(1.0), 0x3ff0000000000000ull); VERIFY_EQUAL(EncodeIEEE754binary64(-1.0), 0xbff0000000000000ull); @@ -680,10 +848,10 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(DecodeIEEE754binary64(0x3ff0000000000000ull), 1.0); VERIFY_EQUAL(IEEE754binary64LE(1.0).GetInt64(), 0x3ff0000000000000ull); VERIFY_EQUAL(IEEE754binary64BE(1.0).GetInt64(), 0x3ff0000000000000ull); - VERIFY_EQUAL(IEEE754binary64LE(0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x3f), 1.0); - VERIFY_EQUAL(IEEE754binary64BE(0x3f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00), 1.0); - VERIFY_EQUAL(IEEE754binary64LE(1.0), IEEE754binary64LE(0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x3f)); - VERIFY_EQUAL(IEEE754binary64BE(1.0), IEEE754binary64BE(0x3f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00)); + VERIFY_EQUAL(IEEE754binary64LE(mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0xf0),mpt::as_byte(0x3f)), 1.0); + VERIFY_EQUAL(IEEE754binary64BE(mpt::as_byte(0x3f),mpt::as_byte(0xf0),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00)), 1.0); + VERIFY_EQUAL(IEEE754binary64LE(1.0), IEEE754binary64LE(mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0xf0),mpt::as_byte(0x3f))); + VERIFY_EQUAL(IEEE754binary64BE(1.0), IEEE754binary64BE(mpt::as_byte(0x3f),mpt::as_byte(0xf0),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00),mpt::as_byte(0x00))); // Packed integers with defined endianness { @@ -733,21 +901,91 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(CModSpecifications::ExtensionToType("s2m"), MOD_TYPE_NONE); VERIFY_EQUAL(CModSpecifications::ExtensionToType(""), MOD_TYPE_NONE); - VERIFY_EQUAL( Util::Round(1.99), 2.0 ); - VERIFY_EQUAL( Util::Round(1.5), 2.0 ); - VERIFY_EQUAL( Util::Round(1.1), 1.0 ); - VERIFY_EQUAL( Util::Round(-0.1), 0.0 ); - VERIFY_EQUAL( Util::Round(-0.5), -1.0 ); - VERIFY_EQUAL( Util::Round(-0.9), -1.0 ); - VERIFY_EQUAL( Util::Round(-1.4), -1.0 ); - VERIFY_EQUAL( Util::Round(-1.7), -2.0 ); - VERIFY_EQUAL( Util::Round(int32_max + 0.1), int32_max ); - VERIFY_EQUAL( Util::Round(int32_max - 0.4), int32_max ); - VERIFY_EQUAL( Util::Round(int32_min + 0.1), int32_min ); - VERIFY_EQUAL( Util::Round(int32_min - 0.1), int32_min ); - VERIFY_EQUAL( Util::Round(uint32_max + 0.499), uint32_max ); - VERIFY_EQUAL( Util::Round(110.1), 110 ); - VERIFY_EQUAL( Util::Round(-110.1), -110 ); + VERIFY_EQUAL( mpt::round(1.99), 2.0 ); + VERIFY_EQUAL( mpt::round(1.5), 2.0 ); + VERIFY_EQUAL( mpt::round(1.1), 1.0 ); + VERIFY_EQUAL( mpt::round(-0.1), 0.0 ); + VERIFY_EQUAL( mpt::round(-0.5), -1.0 ); + VERIFY_EQUAL( mpt::round(-0.9), -1.0 ); + VERIFY_EQUAL( mpt::round(-1.4), -1.0 ); + VERIFY_EQUAL( mpt::round(-1.7), -2.0 ); + VERIFY_EQUAL( mpt::saturate_round(int32_max + 0.1), int32_max ); + VERIFY_EQUAL( mpt::saturate_round(int32_max - 0.4), int32_max ); + VERIFY_EQUAL( mpt::saturate_round(int32_min + 0.1), int32_min ); + VERIFY_EQUAL( mpt::saturate_round(int32_min - 0.1), int32_min ); + VERIFY_EQUAL( mpt::saturate_round(uint32_max + 0.499), uint32_max ); + VERIFY_EQUAL( mpt::saturate_round(110.1), 110 ); + VERIFY_EQUAL( mpt::saturate_round(-110.1), -110 ); + + VERIFY_EQUAL(mpt::weight(int32(-1)), 32); + VERIFY_EQUAL(mpt::weight(0), 0); + VERIFY_EQUAL(mpt::weight(1), 1); + VERIFY_EQUAL(mpt::weight(2), 1); + VERIFY_EQUAL(mpt::weight(3), 2); + + VERIFY_EQUAL(mpt::ispow2(0u), false); + VERIFY_EQUAL(mpt::ispow2(1u), true); + VERIFY_EQUAL(mpt::ispow2(2u), true); + VERIFY_EQUAL(mpt::ispow2(3u), false); + VERIFY_EQUAL(mpt::ispow2(4u), true); + VERIFY_EQUAL(mpt::ispow2(5u), false); + VERIFY_EQUAL(mpt::ispow2(6u), false); + VERIFY_EQUAL(mpt::ispow2(7u), false); + VERIFY_EQUAL(mpt::ispow2(8u), true); + VERIFY_EQUAL(mpt::ispow2(9u), false); + VERIFY_EQUAL(mpt::ispow2(uint32(0x7fffffffu)), false); + VERIFY_EQUAL(mpt::ispow2(uint32(0x80000000u)), true); + VERIFY_EQUAL(mpt::ispow2(uint32(0x80000001u)), false); + VERIFY_EQUAL(mpt::ispow2(uint32(0xfffffffeu)), false); + VERIFY_EQUAL(mpt::ispow2(uint32(0xffffffffu)), false); + + VERIFY_EQUAL(mpt::ceil2(0u), 1u); + VERIFY_EQUAL(mpt::ceil2(1u), 1u); + VERIFY_EQUAL(mpt::ceil2(2u), 2u); + VERIFY_EQUAL(mpt::ceil2(3u), 4u); + VERIFY_EQUAL(mpt::ceil2(4u), 4u); + VERIFY_EQUAL(mpt::ceil2(5u), 8u); + VERIFY_EQUAL(mpt::ceil2(6u), 8u); + VERIFY_EQUAL(mpt::ceil2(7u), 8u); + VERIFY_EQUAL(mpt::ceil2(8u), 8u); + VERIFY_EQUAL(mpt::ceil2(9u), 16u); + VERIFY_EQUAL(mpt::ceil2(uint32(0x7fffffffu)), 0x80000000u); + VERIFY_EQUAL(mpt::ceil2(uint32(0x80000000u)), 0x80000000u); + //VERIFY_EQUAL(mpt::ceil2(uint32(0x80000001u)), 0u); + //VERIFY_EQUAL(mpt::ceil2(uint32(0xfffffffeu)), 0u); + //VERIFY_EQUAL(mpt::ceil2(uint32(0xffffffffu)), 0u); + + VERIFY_EQUAL(mpt::floor2(0u), 0u); + VERIFY_EQUAL(mpt::floor2(1u), 1u); + VERIFY_EQUAL(mpt::floor2(2u), 2u); + VERIFY_EQUAL(mpt::floor2(3u), 2u); + VERIFY_EQUAL(mpt::floor2(4u), 4u); + VERIFY_EQUAL(mpt::floor2(5u), 4u); + VERIFY_EQUAL(mpt::floor2(6u), 4u); + VERIFY_EQUAL(mpt::floor2(7u), 4u); + VERIFY_EQUAL(mpt::floor2(8u), 8u); + VERIFY_EQUAL(mpt::floor2(9u), 8u); + VERIFY_EQUAL(mpt::floor2(uint32(0x7fffffffu)), 0x40000000u); + VERIFY_EQUAL(mpt::floor2(uint32(0x80000000u)), 0x80000000u); + VERIFY_EQUAL(mpt::floor2(uint32(0x80000001u)), 0x80000000u); + VERIFY_EQUAL(mpt::floor2(uint32(0xfffffffeu)), 0x80000000u); + VERIFY_EQUAL(mpt::floor2(uint32(0xffffffffu)), 0x80000000u); + + VERIFY_EQUAL(mpt::log2p1(0u), 0u); + VERIFY_EQUAL(mpt::log2p1(1u), 1u); + VERIFY_EQUAL(mpt::log2p1(2u), 2u); + VERIFY_EQUAL(mpt::log2p1(3u), 2u); + VERIFY_EQUAL(mpt::log2p1(4u), 3u); + VERIFY_EQUAL(mpt::log2p1(5u), 3u); + VERIFY_EQUAL(mpt::log2p1(6u), 3u); + VERIFY_EQUAL(mpt::log2p1(7u), 3u); + VERIFY_EQUAL(mpt::log2p1(8u), 4u); + VERIFY_EQUAL(mpt::log2p1(9u), 4u); + VERIFY_EQUAL(mpt::log2p1(uint32(0x7fffffffu)), 31u); + VERIFY_EQUAL(mpt::log2p1(uint32(0x80000000u)), 32u); + VERIFY_EQUAL(mpt::log2p1(uint32(0x80000001u)), 32u); + VERIFY_EQUAL(mpt::log2p1(uint32(0xfffffffeu)), 32u); + VERIFY_EQUAL(mpt::log2p1(uint32(0xffffffffu)), 32u); // trivials VERIFY_EQUAL( mpt::saturate_cast(-1), -1 ); @@ -1238,12 +1476,12 @@ static MPT_NOINLINE void TestMisc2() VERIFY_EQUAL( mpt::String::Trim(std::string("\0\ta\0b\0",6),std::string("\0",1)), std::string("\ta\0b",4) ); // These should fail to compile - //Util::Round(1.0); - //Util::Round(1.0); - //Util::Round(1.0); + //mpt::saturate_round(1.0); + //mpt::saturate_round(1.0); + //mpt::saturate_round(1.0); // This should trigger assert in Round. - //VERIFY_EQUAL( Util::Round(-129), 0 ); + //VERIFY_EQUAL( mpt::saturate_round(-129), 0 ); // Check for completeness of supported effect list in mod specifications for(const auto &spec : ModSpecs::Collection) @@ -1254,9 +1492,9 @@ static MPT_NOINLINE void TestMisc2() // UUID { - VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull).ToUString(), MPT_USTRING("2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32")); + VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull).ToUString(), U_("2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32")); #if defined(MODPLUG_TRACKER) || !defined(NO_DMO) - VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), MPT_UUID(2ed6593a,dfe6,4cf8,b2e5,75ad7f600c32)); + VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid); VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(Util::StringToGUID(L"{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}"))); VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(Util::StringToCLSID(L"{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}"))); #endif @@ -1266,7 +1504,6 @@ static MPT_NOINLINE void TestMisc2() { mpt::UUID uuid = mpt::UUID::Generate(); VERIFY_EQUAL(uuid, mpt::UUID::FromString(mpt::UUID(uuid).ToUString())); - VERIFY_EQUAL(uuid, mpt::UUID(Util::StringToUUID(Util::UUIDToString(uuid)))); VERIFY_EQUAL(uuid, mpt::UUID(Util::StringToGUID(Util::GUIDToString(uuid)))); VERIFY_EQUAL(uuid, mpt::UUID(Util::StringToIID(Util::IIDToString(uuid)))); VERIFY_EQUAL(uuid, mpt::UUID(Util::StringToCLSID(Util::CLSIDToString(uuid)))); @@ -1274,7 +1511,6 @@ static MPT_NOINLINE void TestMisc2() { GUID guid = mpt::UUID::Generate(); VERIFY_EQUAL(IsEqualGUID(guid, static_cast(mpt::UUID::FromString(mpt::UUID(guid).ToUString()))), TRUE); - VERIFY_EQUAL(IsEqualGUID(guid, Util::StringToUUID(Util::UUIDToString(guid))), TRUE); VERIFY_EQUAL(IsEqualGUID(guid, Util::StringToGUID(Util::GUIDToString(guid))), TRUE); VERIFY_EQUAL(IsEqualGUID(guid, Util::StringToIID(Util::IIDToString(guid))), TRUE); VERIFY_EQUAL(IsEqualGUID(guid, Util::StringToCLSID(Util::CLSIDToString(guid))), TRUE); @@ -1285,17 +1521,20 @@ static MPT_NOINLINE void TestMisc2() VERIFY_EQUAL(mpt::UUID::Generate() != mpt::UUID::Generate(), true); mpt::UUID a = mpt::UUID::Generate(); VERIFY_EQUAL(a, mpt::UUID::FromString(a.ToUString())); - mpt::byte uuiddata[16]; + mpt::byte uuiddata[16]{}; for(std::size_t i = 0; i < 16; ++i) { - uuiddata[i] = static_cast(i); + uuiddata[i] = mpt::byte_cast(static_cast(i)); } STATIC_ASSERT(sizeof(mpt::UUID) == 16); - mpt::UUID uuid2; + UUIDbin uuid2; std::memcpy(&uuid2, uuiddata, 16); - VERIFY_EQUAL(uuid2.ToString(), std::string("00010203-0405-0607-0809-0a0b0c0d0e0f")); + VERIFY_EQUAL(mpt::UUID(uuid2).ToUString(), U_("00010203-0405-0607-0809-0a0b0c0d0e0f")); } + constexpr mpt::UUID uuid3 = "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid; + VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), uuid3); + // check that empty stringstream behaves correctly with our MSVC workarounds when using iostream interface directly { mpt::ostringstream ss; VERIFY_EQUAL(ss.tellp(), std::streampos(0)); } @@ -1522,16 +1761,204 @@ static MPT_NOINLINE void TestMisc2() VERIFY_EQUAL(mpt::IO::IsValid(s), true); VERIFY_EQUAL(std::string(1, a), std::string(1, 'a')); } + + { + auto TestAdaptive16 = [](uint16 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) + { + mpt::stringstream f; + VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt16LE(f, value, fixedSize), true); + VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); + if(bytes) + { + mpt::IO::SeekBegin(f); + for(mpt::IO::Offset i = 0; i < expected_size; ++i) + { + uint8 val = 0; + mpt::IO::ReadIntLE(f, val); + VERIFY_EQUAL_QUIET_NONCONT(val, static_cast(bytes[i])); + } + } + mpt::IO::SeekBegin(f); + uint16 result = 0; + VERIFY_EQUAL(mpt::IO::ReadAdaptiveInt16LE(f, result), true); + VERIFY_EQUAL(result, value); + }; + auto TestAdaptive32 = [](uint32 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) + { + mpt::stringstream f; + VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt32LE(f, value, fixedSize), true); + VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); + if(bytes) + { + mpt::IO::SeekBegin(f); + for(mpt::IO::Offset i = 0; i < expected_size; ++i) + { + uint8 val = 0; + mpt::IO::ReadIntLE(f, val); + VERIFY_EQUAL_QUIET_NONCONT(val, static_cast(bytes[i])); + } + } + mpt::IO::SeekBegin(f); + uint32 result = 0; + VERIFY_EQUAL(mpt::IO::ReadAdaptiveInt32LE(f, result), true); + VERIFY_EQUAL(result, value); + }; + auto TestAdaptive64 = [](uint64 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) + { + mpt::stringstream f; + VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt64LE(f, value, fixedSize), true); + VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); + if(bytes) + { + mpt::IO::SeekBegin(f); + for(mpt::IO::Offset i = 0; i < expected_size; ++i) + { + uint8 val = 0; + mpt::IO::ReadIntLE(f, val); + VERIFY_EQUAL_QUIET_NONCONT(val, static_cast(bytes[i])); + } + } + mpt::IO::SeekBegin(f); + uint64 result = 0; + VERIFY_EQUAL(mpt::IO::ReadAdaptiveInt64LE(f, result), true); + VERIFY_EQUAL(result, value); + }; + TestAdaptive16(0, 1, 0, "\x00"); + TestAdaptive16(1, 1, 0, "\x02"); + TestAdaptive16(2, 1, 0, nullptr); + TestAdaptive16(0x7f, 1, 0, nullptr); + TestAdaptive16(0x80, 2, 0, "\x01\x01"); + TestAdaptive16(0x81, 2, 0, "\x03\x01"); + TestAdaptive16(0x7fff, 2, 0, "\xff\xff"); + TestAdaptive16(0, 1, 1, nullptr); + TestAdaptive16(1, 1, 1, nullptr); + TestAdaptive16(2, 1, 1, nullptr); + TestAdaptive16(0x7f, 1, 1, nullptr); + TestAdaptive16(0x80, 2, 0, nullptr); + TestAdaptive16(0x81, 2, 0, nullptr); + TestAdaptive16(0x7fff, 2, 0, nullptr); + TestAdaptive16(0, 2, 2, "\x01\x00"); + TestAdaptive16(1, 2, 2, "\x03\x00"); + TestAdaptive16(2, 2, 2, nullptr); + TestAdaptive16(0x7f, 2, 2, nullptr); + TestAdaptive16(0x80, 2, 2, nullptr); + TestAdaptive16(0x81, 2, 2, nullptr); + TestAdaptive16(0x7fff, 2, 2, nullptr); + TestAdaptive32(0, 1, 0, "\x00"); + TestAdaptive32(1, 1, 0, nullptr); + TestAdaptive32(2, 1, 0, nullptr); + TestAdaptive32(0x3f, 1, 0, nullptr); + TestAdaptive32(0x40, 2, 0, "\x01\x01"); + TestAdaptive32(0x41, 2, 0, "\x05\x01"); + TestAdaptive32(0x7f, 2, 0, nullptr); + TestAdaptive32(0x80, 2, 0, nullptr); + TestAdaptive32(0x3fff, 2, 0, nullptr); + TestAdaptive32(0x4000, 3, 0, "\x02\x00\x01"); + TestAdaptive32(0x4001, 3, 0, nullptr); + TestAdaptive32(0x3fffff, 3, 0, nullptr); + TestAdaptive32(0x400000, 4, 0, "\x03\x00\x00\x01"); + TestAdaptive32(0x400001, 4, 0, nullptr); + TestAdaptive32(0x3fffffff, 4, 0, "\xff\xff\xff\xff"); + TestAdaptive32(0, 2, 2, nullptr); + TestAdaptive32(1, 2, 2, nullptr); + TestAdaptive32(2, 2, 2, nullptr); + TestAdaptive32(0x3f, 2, 2, nullptr); + TestAdaptive32(0x40, 2, 2, nullptr); + TestAdaptive32(0x41, 2, 2, nullptr); + TestAdaptive32(0x7f, 2, 2, nullptr); + TestAdaptive32(0x80, 2, 2, nullptr); + TestAdaptive32(0x3fff, 2, 2, nullptr); + TestAdaptive32(0, 3, 3, nullptr); + TestAdaptive32(1, 3, 3, nullptr); + TestAdaptive32(2, 3, 3, nullptr); + TestAdaptive32(0x3f, 3, 3, nullptr); + TestAdaptive32(0x40, 3, 3, nullptr); + TestAdaptive32(0x41, 3, 3, nullptr); + TestAdaptive32(0x7f, 3, 3, nullptr); + TestAdaptive32(0x80, 3, 3, nullptr); + TestAdaptive32(0x3fff, 3, 3, nullptr); + TestAdaptive32(0x4000, 3, 3, nullptr); + TestAdaptive32(0x4001, 3, 3, nullptr); + TestAdaptive32(0x3fffff, 3, 3, nullptr); + TestAdaptive32(0, 4, 4, nullptr); + TestAdaptive32(1, 4, 4, nullptr); + TestAdaptive32(2, 4, 4, nullptr); + TestAdaptive32(0x3f, 4, 4, nullptr); + TestAdaptive32(0x40, 4, 4, nullptr); + TestAdaptive32(0x41, 4, 4, nullptr); + TestAdaptive32(0x7f, 4, 4, nullptr); + TestAdaptive32(0x80, 4, 4, nullptr); + TestAdaptive32(0x3fff, 4, 4, nullptr); + TestAdaptive32(0x4000, 4, 4, nullptr); + TestAdaptive32(0x4001, 4, 4, nullptr); + TestAdaptive32(0x3fffff, 4, 4, nullptr); + TestAdaptive32(0x400000, 4, 4, nullptr); + TestAdaptive32(0x400001, 4, 4, nullptr); + TestAdaptive32(0x3fffffff, 4, 4, nullptr); + + TestAdaptive64(0, 1, 0, nullptr); + TestAdaptive64(1, 1, 0, nullptr); + TestAdaptive64(2, 1, 0, nullptr); + TestAdaptive64(0x3f, 1, 0, nullptr); + TestAdaptive64(0x40, 2, 0, nullptr); + TestAdaptive64(0x41, 2, 0, nullptr); + TestAdaptive64(0x7f, 2, 0, nullptr); + TestAdaptive64(0x80, 2, 0, nullptr); + TestAdaptive64(0x3fff, 2, 0, nullptr); + TestAdaptive64(0x4000, 4, 0, nullptr); + TestAdaptive64(0x4001, 4, 0, nullptr); + TestAdaptive64(0x3fffff, 4, 0, nullptr); + TestAdaptive64(0x400000, 4, 0, nullptr); + TestAdaptive64(0x400001, 4, 0, nullptr); + TestAdaptive64(0x3fffffff, 4, 0, nullptr); + TestAdaptive64(0x40000000, 8, 0, nullptr); + TestAdaptive64(0x40000001, 8, 0, nullptr); + TestAdaptive64(0x3fffffffffffffffull, 8, 0, nullptr); + TestAdaptive64(0, 2, 2, nullptr); + TestAdaptive64(1, 2, 2, nullptr); + TestAdaptive64(2, 2, 2, nullptr); + TestAdaptive64(0x3f, 2, 2, nullptr); + TestAdaptive64(0, 4, 4, nullptr); + TestAdaptive64(1, 4, 4, nullptr); + TestAdaptive64(2, 4, 4, nullptr); + TestAdaptive64(0x3f, 4, 4, nullptr); + TestAdaptive64(0x40, 4, 4, nullptr); + TestAdaptive64(0x41, 4, 4, nullptr); + TestAdaptive64(0x7f, 4, 4, nullptr); + TestAdaptive64(0x80, 4, 4, nullptr); + TestAdaptive64(0x3fff, 4, 4, nullptr); + TestAdaptive64(0, 8, 8, nullptr); + TestAdaptive64(1, 8, 8, nullptr); + TestAdaptive64(2, 8, 8, nullptr); + TestAdaptive64(0x3f, 8, 8, nullptr); + TestAdaptive64(0x40, 8, 8, nullptr); + TestAdaptive64(0x41, 8, 8, nullptr); + TestAdaptive64(0x7f, 8, 8, nullptr); + TestAdaptive64(0x80, 8, 8, nullptr); + TestAdaptive64(0x3fff, 8, 8, nullptr); + TestAdaptive64(0x4000, 8, 8, nullptr); + TestAdaptive64(0x4001, 8, 8, nullptr); + TestAdaptive64(0x3fffff, 8, 8, nullptr); + TestAdaptive64(0x400000, 8, 8, nullptr); + TestAdaptive64(0x400001, 8, 8, nullptr); + TestAdaptive64(0x3fffffff, 8, 8, nullptr); + TestAdaptive64(0x40000000, 8, 8, nullptr); + TestAdaptive64(0x40000001, 8, 8, nullptr); + TestAdaptive64(0x3fffffffffffffffull, 8, 8, nullptr); + + } + +#ifdef MODPLUG_TRACKER #ifdef MPT_ENABLE_FILEIO { std::vector data; - data.push_back(0); - data.push_back(255); - data.push_back(1); - data.push_back(2); - mpt::PathString fn = GetTempFilenameBase() + MPT_PATHSTRING("lazy"); + data.push_back(mpt::as_byte(0)); + data.push_back(mpt::as_byte(255)); + data.push_back(mpt::as_byte(1)); + data.push_back(mpt::as_byte(2)); + mpt::PathString fn = GetTempFilenameBase() + P_("lazy"); RemoveFile(fn); mpt::LazyFileRef f(fn); f = data; @@ -1546,6 +1973,7 @@ static MPT_NOINLINE void TestMisc2() } #endif +#endif // MODPLUG_TRACKER #ifdef MPT_WITH_ZLIB VERIFY_EQUAL(crc32(0, mpt::byte_cast(std::string("123456789").c_str()), 9), 0xCBF43926u); @@ -1594,7 +2022,7 @@ static MPT_NOINLINE void TestMisc2() VERIFY_EQUAL(SamplePosition(-1).IsNegative(), true); VERIFY_EQUAL(SamplePosition(int64_max).GetRaw(), int64_max); VERIFY_EQUAL(SamplePosition(2, SamplePosition::fractMax).GetInt(), 2); - VERIFY_EQUAL(SamplePosition(2, SamplePosition::fractMax).GetFract(), SamplePosition::GetFractMax()); + VERIFY_EQUAL(SamplePosition(2, SamplePosition::fractMax).GetFract(), SamplePosition::fractMax); VERIFY_EQUAL(SamplePosition(1, SamplePosition::fractMax).GetInvertedFract(), SamplePosition(0, 1)); VERIFY_EQUAL(SamplePosition(1, 0).GetInvertedFract(), SamplePosition(1, 0)); VERIFY_EQUAL(SamplePosition(2, 0).Negate(), SamplePosition(-2, 0)); @@ -1607,13 +2035,13 @@ static MPT_NOINLINE void TestMisc2() #if defined(MODPLUG_TRACKER) - VERIFY_EQUAL(mpt::Wine::Version(MPT_USTRING("1.1.44" )).AsString() , MPT_USTRING("1.1.44")); - VERIFY_EQUAL(mpt::Wine::Version(MPT_USTRING("1.6.2" )).AsString() , MPT_USTRING("1.6.2" )); - VERIFY_EQUAL(mpt::Wine::Version(MPT_USTRING("1.8" )).AsString() , MPT_USTRING("1.8.0" )); - VERIFY_EQUAL(mpt::Wine::Version(MPT_USTRING("2.0-rc" )).AsString() , MPT_USTRING("2.0.0" )); - VERIFY_EQUAL(mpt::Wine::Version(MPT_USTRING("2.0-rc4")).AsString() , MPT_USTRING("2.0.0" )); - VERIFY_EQUAL(mpt::Wine::Version(MPT_USTRING("2.0" )).AsString() , MPT_USTRING("2.0.0" )); - VERIFY_EQUAL(mpt::Wine::Version(MPT_USTRING("2.4" )).AsString() , MPT_USTRING("2.4.0" )); + VERIFY_EQUAL(mpt::Wine::Version(U_("1.1.44" )).AsString() , U_("1.1.44")); + VERIFY_EQUAL(mpt::Wine::Version(U_("1.6.2" )).AsString() , U_("1.6.2" )); + VERIFY_EQUAL(mpt::Wine::Version(U_("1.8" )).AsString() , U_("1.8.0" )); + VERIFY_EQUAL(mpt::Wine::Version(U_("2.0-rc" )).AsString() , U_("2.0.0" )); + VERIFY_EQUAL(mpt::Wine::Version(U_("2.0-rc4")).AsString() , U_("2.0.0" )); + VERIFY_EQUAL(mpt::Wine::Version(U_("2.0" )).AsString() , U_("2.0.0" )); + VERIFY_EQUAL(mpt::Wine::Version(U_("2.4" )).AsString() , U_("2.4.0" )); #endif // MODPLUG_TRACKER @@ -1669,6 +2097,146 @@ static MPT_NOINLINE void TestMisc2() VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064016).AsUTC()), TestDate2( 56, 46, 21, 11, 10, 2014 )); VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064100).AsUTC()), TestDate2( 20, 48, 21, 11, 10, 2014 )); + +#ifdef MODPLUG_TRACKER + + // URI & HTTP + + { + URI uri = ParseURI(U_("scheme://username:password@host:port/path?query#fragment")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("username")); + VERIFY_EQUAL(uri.password, U_("password")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("port")); + VERIFY_EQUAL(uri.path, U_("/path")); + VERIFY_EQUAL(uri.query, U_("query")); + VERIFY_EQUAL(uri.fragment, U_("fragment")); + } + { + URI uri = ParseURI(U_("scheme://host/path")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("host")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("/path")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("scheme://username:password@[2001:db8::1]:port/path?query#fragment")); + VERIFY_EQUAL(uri.scheme, U_("scheme")); + VERIFY_EQUAL(uri.username, U_("username")); + VERIFY_EQUAL(uri.password, U_("password")); + VERIFY_EQUAL(uri.host, U_("[2001:db8::1]")); + VERIFY_EQUAL(uri.port, U_("port")); + VERIFY_EQUAL(uri.path, U_("/path")); + VERIFY_EQUAL(uri.query, U_("query")); + VERIFY_EQUAL(uri.fragment, U_("fragment")); + } + + { + URI uri = ParseURI(U_("https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top")); + VERIFY_EQUAL(uri.scheme, U_("https")); + VERIFY_EQUAL(uri.username, U_("john.doe")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("www.example.com")); + VERIFY_EQUAL(uri.port, U_("123")); + VERIFY_EQUAL(uri.path, U_("/forum/questions/")); + VERIFY_EQUAL(uri.query, U_("tag=networking&order=newest")); + VERIFY_EQUAL(uri.fragment, U_("top")); + } + { + URI uri = ParseURI(U_("ldap://[2001:db8::7]/c=GB?objectClass?one")); + VERIFY_EQUAL(uri.scheme, U_("ldap")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("[2001:db8::7]")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("/c=GB")); + VERIFY_EQUAL(uri.query, U_("objectClass?one")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("mailto:John.Doe@example.com")); + VERIFY_EQUAL(uri.scheme, U_("mailto")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("John.Doe@example.com")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("news:comp.infosystems.www.servers.unix")); + VERIFY_EQUAL(uri.scheme, U_("news")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("comp.infosystems.www.servers.unix")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("tel:+1-816-555-1212")); + VERIFY_EQUAL(uri.scheme, U_("tel")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("+1-816-555-1212")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("telnet://192.0.2.16:80/")); + VERIFY_EQUAL(uri.scheme, U_("telnet")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("192.0.2.16")); + VERIFY_EQUAL(uri.port, U_("80")); + VERIFY_EQUAL(uri.path, U_("/")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + { + URI uri = ParseURI(U_("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")); + VERIFY_EQUAL(uri.scheme, U_("urn")); + VERIFY_EQUAL(uri.username, U_("")); + VERIFY_EQUAL(uri.password, U_("")); + VERIFY_EQUAL(uri.host, U_("")); + VERIFY_EQUAL(uri.port, U_("")); + VERIFY_EQUAL(uri.path, U_("oasis:names:specification:docbook:dtd:xml:4.1.2")); + VERIFY_EQUAL(uri.query, U_("")); + VERIFY_EQUAL(uri.fragment, U_("")); + } + + { + HTTP::Request req; + req.SetURI(ParseURI(U_("https://host/path?a1=a&a2=b"))); + VERIFY_EQUAL(req.protocol, HTTP::Protocol::HTTPS); + VERIFY_EQUAL(req.host, U_("host")); + VERIFY_EQUAL(req.path, U_("/path")); + VERIFY_EQUAL(req.query.size(), 2u); + if(req.query.size() == 2) + { + VERIFY_EQUAL(req.query[0], std::make_pair(U_("a1"), U_("a"))); + VERIFY_EQUAL(req.query[1], std::make_pair(U_("a2"), U_("b"))); + } + } + { + HTTP::Request req; + req.SetURI(ParseURI(U_("https://host/"))); + VERIFY_EQUAL(req.protocol, HTTP::Protocol::HTTPS); + VERIFY_EQUAL(req.host, U_("host")); + VERIFY_EQUAL(req.path, U_("/")); + } + +#endif // MODPLUG_TRACKER + // https://github.com/kripken/emscripten/issues/4251 #if MPT_OS_EMSCRIPTEN volatile int transpose = 32; @@ -1685,7 +2253,7 @@ static MPT_NOINLINE void TestMisc2() static MPT_NOINLINE void TestRandom() { - mpt::prng & prng = *s_PRNG; + mpt::default_prng & prng = *s_PRNG; for(std::size_t i = 0; i < 10000; ++i) { VERIFY_EQUAL_QUIET_NONCONT(IsInRange(mpt::random(prng), 0u, 127u), true); @@ -1766,15 +2334,15 @@ static MPT_NOINLINE void TestCharsets() // MPT_UTF8 version // Charset conversions (basic sanity checks) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8, MPT_USTRING("a")), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1, MPT_USTRING("a")), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetASCII, MPT_USTRING("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetUTF8, "a"), MPT_USTRING("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetISO8859_1, "a"), MPT_USTRING("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetASCII, "a"), MPT_USTRING("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetASCII, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetUTF8, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetISO8859_1, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetASCII, "a"), U_("a")); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, MPT_USTRING("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetLocale, "a"), MPT_USTRING("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetLocale, "a"), U_("a")); #endif VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8, MPT_UTF8("a")), "a"); VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1, MPT_UTF8("a")), "a"); @@ -1816,30 +2384,30 @@ static MPT_NOINLINE void TestCharsets() VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xC3\xA4xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xC3\xA4xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xC3\xA4xyz"),MPT_USTRING("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),MPT_USTRING("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xC3\xA4xyz"),MPT_USTRING("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),MPT_USTRING("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),U_("abc")),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xC3\xA4xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xC3\xA4xyz"),MPT_USTRING("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xC3\xA4xyz"),U_("abc")),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),MPT_USTRING("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); #endif // Check that characters are correctly converted @@ -1959,143 +2527,180 @@ static MPT_NOINLINE void TestCharsets() #endif - + { + char buf[4] = { 'x','x','x','x' }; + mpt::String::WriteAutoBuf(buf) = std::string("foobar"); + VERIFY_EQUAL(buf[0], 'f'); + VERIFY_EQUAL(buf[1], 'o'); + VERIFY_EQUAL(buf[2], 'o'); + VERIFY_EQUAL(buf[3], '\0'); + } + { + char buf[4] = { 'x','x','x','x' }; + char foobar[] = {'f','o','o','b','a','r','\0'}; + mpt::String::WriteTypedBuf(buf) = (char*)foobar; + VERIFY_EQUAL(buf[0], 'f'); + VERIFY_EQUAL(buf[1], 'o'); + VERIFY_EQUAL(buf[2], 'o'); + VERIFY_EQUAL(buf[3], '\0'); + } + { + char buf[4] = { 'x','x','x','x' }; + mpt::String::WriteTypedBuf(buf) = (const char*)"foobar"; + VERIFY_EQUAL(buf[0], 'f'); + VERIFY_EQUAL(buf[1], 'o'); + VERIFY_EQUAL(buf[2], 'o'); + VERIFY_EQUAL(buf[3], '\0'); + } + { + char buf[4] = { 'x','x','x','x' }; + mpt::String::WriteTypedBuf(buf) = "foobar"; + VERIFY_EQUAL(buf[0], 'f'); + VERIFY_EQUAL(buf[1], 'o'); + VERIFY_EQUAL(buf[2], 'o'); + VERIFY_EQUAL(buf[3], '\0'); + } + { + const char buf[4] = { 'f','o','o','b' }; + std::string foo = mpt::String::ReadAutoBuf(buf); + VERIFY_EQUAL(foo, std::string("foob")); + } // Path splitting #if MPT_OS_WINDOWS && defined(MPT_ENABLE_DYNBIND) - VERIFY_EQUAL(MPT_PATHSTRING("").GetDrive(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("").GetDir(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("").GetPath(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("").GetFileName(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("").GetFileExt(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("").GetFullFileName(), MPT_PATHSTRING("")); + VERIFY_EQUAL(P_("").GetDrive(), P_("")); + VERIFY_EQUAL(P_("").GetDir(), P_("")); + VERIFY_EQUAL(P_("").GetPath(), P_("")); + VERIFY_EQUAL(P_("").GetFileName(), P_("")); + VERIFY_EQUAL(P_("").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("").GetFullFileName(), P_("")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\").GetDrive(), MPT_PATHSTRING("C:")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\").GetDir(), MPT_PATHSTRING("\\")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\").GetPath(), MPT_PATHSTRING("C:\\")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\").GetFileName(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\").GetFileExt(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\").GetFullFileName(), MPT_PATHSTRING("")); + VERIFY_EQUAL(P_("C:\\").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:\\").GetDir(), P_("\\")); + VERIFY_EQUAL(P_("C:\\").GetPath(), P_("C:\\")); + VERIFY_EQUAL(P_("C:\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("C:\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("C:\\").GetFullFileName(), P_("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\").GetDrive(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\").GetDir(), MPT_PATHSTRING("\\directory\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\").GetPath(), MPT_PATHSTRING("\\directory\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\").GetFileName(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\").GetFileExt(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\").GetFullFileName(), MPT_PATHSTRING("")); + VERIFY_EQUAL(P_("\\directory\\").GetDrive(), P_("")); + VERIFY_EQUAL(P_("\\directory\\").GetDir(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\").GetPath(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\directory\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\directory\\").GetFullFileName(), P_("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\file.txt").GetDrive(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\file.txt").GetDir(), MPT_PATHSTRING("\\directory\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\file.txt").GetPath(), MPT_PATHSTRING("\\directory\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\file.txt").GetFileName(), MPT_PATHSTRING("file")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\file.txt").GetFileExt(), MPT_PATHSTRING(".txt")); - VERIFY_EQUAL(MPT_PATHSTRING("\\directory\\file.txt").GetFullFileName(), MPT_PATHSTRING("file.txt")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetDrive(), P_("")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetDir(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetPath(), P_("\\directory\\")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetFileName(), P_("file")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("\\directory\\file.txt").GetFullFileName(), P_("file.txt")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tmp.txt").GetDrive(), MPT_PATHSTRING("C:")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tmp.txt").GetDir(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tmp.txt").GetPath(), MPT_PATHSTRING("C:")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tmp.txt").GetFileName(), MPT_PATHSTRING("tmp")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tmp.txt").GetFileExt(), MPT_PATHSTRING(".txt")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tmp.txt").GetFullFileName(), MPT_PATHSTRING("tmp.txt")); + VERIFY_EQUAL(P_("C:tmp.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:tmp.txt").GetDir(), P_("")); + VERIFY_EQUAL(P_("C:tmp.txt").GetPath(), P_("C:")); + VERIFY_EQUAL(P_("C:tmp.txt").GetFileName(), P_("tmp")); + VERIFY_EQUAL(P_("C:tmp.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("C:tmp.txt").GetFullFileName(), P_("tmp.txt")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tempdir\\tmp.txt").GetDrive(), MPT_PATHSTRING("C:")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tempdir\\tmp.txt").GetDir(), MPT_PATHSTRING("tempdir\\")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tempdir\\tmp.txt").GetPath(), MPT_PATHSTRING("C:tempdir\\")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tempdir\\tmp.txt").GetFileName(), MPT_PATHSTRING("tmp")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tempdir\\tmp.txt").GetFileExt(), MPT_PATHSTRING(".txt")); - VERIFY_EQUAL(MPT_PATHSTRING("C:tempdir\\tmp.txt").GetFullFileName(), MPT_PATHSTRING("tmp.txt")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetDir(), P_("tempdir\\")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetPath(), P_("C:tempdir\\")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFileName(), P_("tmp")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("C:tempdir\\tmp.txt").GetFullFileName(), P_("tmp.txt")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.txt").GetDrive(), MPT_PATHSTRING("C:")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.txt").GetDir(), MPT_PATHSTRING("\\tempdir\\")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.txt").GetPath(), MPT_PATHSTRING("C:\\tempdir\\")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.txt").GetFileName(), MPT_PATHSTRING("tmp")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.txt").GetFileExt(), MPT_PATHSTRING(".txt")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.txt").GetFullFileName(), MPT_PATHSTRING("tmp.txt")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetDir(), P_("\\tempdir\\")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetPath(), P_("C:\\tempdir\\")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFileName(), P_("tmp")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.txt").GetFullFileName(), P_("tmp.txt")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.foo.txt").GetFileName(), MPT_PATHSTRING("tmp.foo")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\tempdir\\tmp.foo.txt").GetFileExt(), MPT_PATHSTRING(".txt")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.foo.txt").GetFileName(), P_("tmp.foo")); + VERIFY_EQUAL(P_("C:\\tempdir\\tmp.foo.txt").GetFileExt(), P_(".txt")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server").GetDrive(), MPT_PATHSTRING("\\\\server")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server").GetDir(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server").GetPath(), MPT_PATHSTRING("\\\\server")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server").GetFileName(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server").GetFileExt(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server").GetFullFileName(), MPT_PATHSTRING("")); + VERIFY_EQUAL(P_("\\\\server").GetDrive(), P_("\\\\server")); + VERIFY_EQUAL(P_("\\\\server").GetDir(), P_("")); + VERIFY_EQUAL(P_("\\\\server").GetPath(), P_("\\\\server")); + VERIFY_EQUAL(P_("\\\\server").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server").GetFullFileName(), P_("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\").GetDrive(), MPT_PATHSTRING("\\\\server\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\").GetDir(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\").GetPath(), MPT_PATHSTRING("\\\\server\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\").GetFileName(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\").GetFileExt(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\").GetFullFileName(), MPT_PATHSTRING("")); + VERIFY_EQUAL(P_("\\\\server\\").GetDrive(), P_("\\\\server\\")); + VERIFY_EQUAL(P_("\\\\server\\").GetDir(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\").GetPath(), P_("\\\\server\\")); + VERIFY_EQUAL(P_("\\\\server\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\").GetFullFileName(), P_("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share").GetDrive(), MPT_PATHSTRING("\\\\server\\share")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share").GetDir(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share").GetPath(), MPT_PATHSTRING("\\\\server\\share")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share").GetFileName(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share").GetFileExt(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share").GetFullFileName(), MPT_PATHSTRING("")); + VERIFY_EQUAL(P_("\\\\server\\share").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share").GetDir(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share").GetPath(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share").GetFullFileName(), P_("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\").GetDrive(), MPT_PATHSTRING("\\\\server\\share")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\").GetDir(), MPT_PATHSTRING("\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\").GetPath(), MPT_PATHSTRING("\\\\server\\share\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\").GetFileName(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\").GetFileExt(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\").GetFullFileName(), MPT_PATHSTRING("")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetDir(), P_("\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetPath(), P_("\\\\server\\share\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetFileName(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetFileExt(), P_("")); + VERIFY_EQUAL(P_("\\\\server\\share\\").GetFullFileName(), P_("")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), MPT_PATHSTRING("\\\\server\\share")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), MPT_PATHSTRING("\\dir1\\dir2\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), MPT_PATHSTRING("name.foo")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), MPT_PATHSTRING(".ext")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), MPT_PATHSTRING("name.foo.ext")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), P_("\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), P_("\\\\server\\share\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), P_("name.foo")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), P_(".ext")); + VERIFY_EQUAL(P_("\\\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), P_("name.foo.ext")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDrive(), MPT_PATHSTRING("C:")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDir(), MPT_PATHSTRING("\\tempdir\\dir.2\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetPath(), MPT_PATHSTRING("C:\\tempdir\\dir.2\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileName(), MPT_PATHSTRING("tmp.foo")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileExt(), MPT_PATHSTRING(".txt")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFullFileName(), MPT_PATHSTRING("tmp.foo.txt")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDrive(), P_("C:")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetDir(), P_("\\tempdir\\dir.2\\")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetPath(), P_("C:\\tempdir\\dir.2\\")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileName(), P_("tmp.foo")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFileExt(), P_(".txt")); + VERIFY_EQUAL(P_("\\\\?\\C:\\tempdir\\dir.2\\tmp.foo.txt").GetFullFileName(), P_("tmp.foo.txt")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), MPT_PATHSTRING("\\\\server\\share")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), MPT_PATHSTRING("\\dir1\\dir2\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), MPT_PATHSTRING("\\\\server\\share\\dir1\\dir2\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), MPT_PATHSTRING("name.foo")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), MPT_PATHSTRING(".ext")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), MPT_PATHSTRING("name.foo.ext")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDrive(), P_("\\\\server\\share")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetDir(), P_("\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetPath(), P_("\\\\server\\share\\dir1\\dir2\\")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileName(), P_("name.foo")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFileExt(), P_(".ext")); + VERIFY_EQUAL(P_("\\\\?\\UNC\\server\\share\\dir1\\dir2\\name.foo.ext").GetFullFileName(), P_("name.foo.ext")); #endif // Path conversions #ifdef MODPLUG_TRACKER - const mpt::PathString exePath = MPT_PATHSTRING("C:\\OpenMPT\\"); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\OpenMPT\\").AbsolutePathToRelative(exePath), MPT_PATHSTRING(".\\")); - VERIFY_EQUAL(MPT_PATHSTRING("c:\\OpenMPT\\foo").AbsolutePathToRelative(exePath), MPT_PATHSTRING(".\\foo")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\foo").AbsolutePathToRelative(exePath), MPT_PATHSTRING("\\foo")); - VERIFY_EQUAL(MPT_PATHSTRING(".\\").RelativePathToAbsolute(exePath), MPT_PATHSTRING("C:\\OpenMPT\\")); - VERIFY_EQUAL(MPT_PATHSTRING(".\\foo").RelativePathToAbsolute(exePath), MPT_PATHSTRING("C:\\OpenMPT\\foo")); - VERIFY_EQUAL(MPT_PATHSTRING("\\foo").RelativePathToAbsolute(exePath), MPT_PATHSTRING("C:\\foo")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\path\\file").AbsolutePathToRelative(exePath), MPT_PATHSTRING("\\\\server\\path\\file")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\server\\path\\file").RelativePathToAbsolute(exePath), MPT_PATHSTRING("\\\\server\\path\\file")); + const mpt::PathString exePath = P_("C:\\OpenMPT\\"); + VERIFY_EQUAL(P_("C:\\OpenMPT\\").AbsolutePathToRelative(exePath), P_(".\\")); + VERIFY_EQUAL(P_("c:\\OpenMPT\\foo").AbsolutePathToRelative(exePath), P_(".\\foo")); + VERIFY_EQUAL(P_("C:\\foo").AbsolutePathToRelative(exePath), P_("\\foo")); + VERIFY_EQUAL(P_(".\\").RelativePathToAbsolute(exePath), P_("C:\\OpenMPT\\")); + VERIFY_EQUAL(P_(".\\foo").RelativePathToAbsolute(exePath), P_("C:\\OpenMPT\\foo")); + VERIFY_EQUAL(P_("\\foo").RelativePathToAbsolute(exePath), P_("C:\\foo")); + VERIFY_EQUAL(P_("\\\\server\\path\\file").AbsolutePathToRelative(exePath), P_("\\\\server\\path\\file")); + VERIFY_EQUAL(P_("\\\\server\\path\\file").RelativePathToAbsolute(exePath), P_("\\\\server\\path\\file")); - VERIFY_EQUAL(MPT_PATHSTRING("").Simplify(), MPT_PATHSTRING("")); - VERIFY_EQUAL(MPT_PATHSTRING(" ").Simplify(), MPT_PATHSTRING(" ")); - VERIFY_EQUAL(MPT_PATHSTRING("foo\\bar").Simplify(), MPT_PATHSTRING("foo\\bar")); - VERIFY_EQUAL(MPT_PATHSTRING(".\\foo\\bar").Simplify(), MPT_PATHSTRING(".\\foo\\bar")); - VERIFY_EQUAL(MPT_PATHSTRING(".\\\\foo\\bar").Simplify(), MPT_PATHSTRING(".\\foo\\bar")); - VERIFY_EQUAL(MPT_PATHSTRING("./\\foo\\bar").Simplify(), MPT_PATHSTRING(".\\foo\\bar")); - VERIFY_EQUAL(MPT_PATHSTRING("\\foo\\bar").Simplify(), MPT_PATHSTRING("\\foo\\bar")); - VERIFY_EQUAL(MPT_PATHSTRING("A:\\name_1\\.\\name_2\\..\\name_3\\").Simplify(), MPT_PATHSTRING("A:\\name_1\\name_3")); - VERIFY_EQUAL(MPT_PATHSTRING("A:\\name_1\\..\\name_2\\./name_3").Simplify(), MPT_PATHSTRING("A:\\name_2\\name_3")); - VERIFY_EQUAL(MPT_PATHSTRING("A:\\name_1\\.\\name_2\\.\\name_3\\..\\name_4\\..").Simplify(), MPT_PATHSTRING("A:\\name_1\\name_2")); - VERIFY_EQUAL(MPT_PATHSTRING("A:foo\\\\bar").Simplify(), MPT_PATHSTRING("A:\\foo\\bar")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\..").Simplify(), MPT_PATHSTRING("C:\\")); - VERIFY_EQUAL(MPT_PATHSTRING("C:\\.").Simplify(), MPT_PATHSTRING("C:\\")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\foo\\..\\.bar").Simplify(), MPT_PATHSTRING("\\\\.bar")); - VERIFY_EQUAL(MPT_PATHSTRING("\\\\foo\\..\\..\\bar").Simplify(), MPT_PATHSTRING("\\\\bar")); + VERIFY_EQUAL(P_("").Simplify(), P_("")); + VERIFY_EQUAL(P_(" ").Simplify(), P_(" ")); + VERIFY_EQUAL(P_("foo\\bar").Simplify(), P_("foo\\bar")); + VERIFY_EQUAL(P_(".\\foo\\bar").Simplify(), P_(".\\foo\\bar")); + VERIFY_EQUAL(P_(".\\\\foo\\bar").Simplify(), P_(".\\foo\\bar")); + VERIFY_EQUAL(P_("./\\foo\\bar").Simplify(), P_(".\\foo\\bar")); + VERIFY_EQUAL(P_("\\foo\\bar").Simplify(), P_("\\foo\\bar")); + VERIFY_EQUAL(P_("A:\\name_1\\.\\name_2\\..\\name_3\\").Simplify(), P_("A:\\name_1\\name_3")); + VERIFY_EQUAL(P_("A:\\name_1\\..\\name_2\\./name_3").Simplify(), P_("A:\\name_2\\name_3")); + VERIFY_EQUAL(P_("A:\\name_1\\.\\name_2\\.\\name_3\\..\\name_4\\..").Simplify(), P_("A:\\name_1\\name_2")); + VERIFY_EQUAL(P_("A:foo\\\\bar").Simplify(), P_("A:\\foo\\bar")); + VERIFY_EQUAL(P_("C:\\..").Simplify(), P_("C:\\")); + VERIFY_EQUAL(P_("C:\\.").Simplify(), P_("C:\\")); + VERIFY_EQUAL(P_("\\\\foo\\..\\.bar").Simplify(), P_("\\\\.bar")); + VERIFY_EQUAL(P_("\\\\foo\\..\\..\\bar").Simplify(), P_("\\\\bar")); #endif @@ -2114,26 +2719,26 @@ static MPT_NOINLINE void TestCharsets() // here. char src_char[256]; - wchar_t src_wchar[256]; - TCHAR src_tchar[256]; + wchar_t src_wchar_t[256]; + TCHAR src_TCHAR[256]; char dst_char[256]; - wchar_t dst_wchar[256]; - TCHAR dst_tchar[256]; + wchar_t dst_wchar_t[256]; + TCHAR dst_TCHAR[256]; MemsetZero(src_char); - MemsetZero(src_wchar); - MemsetZero(src_tchar); + MemsetZero(src_wchar_t); + MemsetZero(src_TCHAR); strcpy(src_char, "ab"); - wcscpy(src_wchar, L"ab"); - _tcscpy(src_tchar, _T("ab")); + wcscpy(src_wchar_t, L"ab"); + _tcscpy(src_TCHAR, _T("ab")); #define MPT_TEST_PRINTF(dst_type, function, format, src_type) \ MPT_DO { \ MemsetZero(dst_ ## dst_type); \ function(dst_ ## dst_type, format, src_ ## src_type); \ - VERIFY_EQUAL(std::memcmp(dst_ ## dst_type, src_ ## dst_type, 256), 0); \ + VERIFY_EQUAL(std::char_traits< dst_type >::compare(dst_ ## dst_type, src_ ## dst_type, std::char_traits< dst_type >::length( src_ ## dst_type ) + 1), 0); \ } MPT_WHILE_0 \ /**/ @@ -2141,52 +2746,52 @@ static MPT_NOINLINE void TestCharsets() MPT_DO { \ MemsetZero(dst_ ## dst_type); \ function(dst_ ## dst_type, 255, format, src_ ## src_type); \ - VERIFY_EQUAL(std::memcmp(dst_ ## dst_type, src_ ## dst_type, 256), 0); \ + VERIFY_EQUAL(std::char_traits< dst_type >::compare(dst_ ## dst_type, src_ ## dst_type, std::char_traits< dst_type >::length( src_ ## dst_type ) + 1), 0); \ } MPT_WHILE_0 \ /**/ // CRT narrow MPT_TEST_PRINTF(char, sprintf, "%s", char); - MPT_TEST_PRINTF(char, sprintf, "%S", wchar); + MPT_TEST_PRINTF(char, sprintf, "%S", wchar_t); MPT_TEST_PRINTF(char, sprintf, "%hs", char); MPT_TEST_PRINTF(char, sprintf, "%hS", char); - MPT_TEST_PRINTF(char, sprintf, "%ls", wchar); - MPT_TEST_PRINTF(char, sprintf, "%lS", wchar); - MPT_TEST_PRINTF(char, sprintf, "%ws", wchar); - MPT_TEST_PRINTF(char, sprintf, "%wS", wchar); + MPT_TEST_PRINTF(char, sprintf, "%ls", wchar_t); + MPT_TEST_PRINTF(char, sprintf, "%lS", wchar_t); + MPT_TEST_PRINTF(char, sprintf, "%ws", wchar_t); + MPT_TEST_PRINTF(char, sprintf, "%wS", wchar_t); // CRT wide - MPT_TEST_PRINTF_N(wchar, swprintf, L"%s", wchar); - MPT_TEST_PRINTF_N(wchar, swprintf, L"%S", char); - MPT_TEST_PRINTF_N(wchar, swprintf, L"%hs", char); - MPT_TEST_PRINTF_N(wchar, swprintf, L"%hS", char); - MPT_TEST_PRINTF_N(wchar, swprintf, L"%ls", wchar); - MPT_TEST_PRINTF_N(wchar, swprintf, L"%lS", wchar); - MPT_TEST_PRINTF_N(wchar, swprintf, L"%ws", wchar); - MPT_TEST_PRINTF_N(wchar, swprintf, L"%wS", wchar); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%s", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%S", char); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%hs", char); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%hS", char); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%ls", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%lS", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%ws", wchar_t); + MPT_TEST_PRINTF_N(wchar_t, swprintf, L"%wS", wchar_t); // WinAPI TCHAR - MPT_TEST_PRINTF(tchar, wsprintf, _T("%s"), tchar); - MPT_TEST_PRINTF(tchar, wsprintf, _T("%hs"), char); - MPT_TEST_PRINTF(tchar, wsprintf, _T("%hS"), char); - MPT_TEST_PRINTF(tchar, wsprintf, _T("%ls"), wchar); - MPT_TEST_PRINTF(tchar, wsprintf, _T("%lS"), wchar); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%s"), TCHAR); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%hs"), char); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%hS"), char); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%ls"), wchar_t); + MPT_TEST_PRINTF(TCHAR, wsprintf, _T("%lS"), wchar_t); // WinAPI CHAR MPT_TEST_PRINTF(char, wsprintfA, "%s", char); - MPT_TEST_PRINTF(char, wsprintfA, "%S", wchar); + MPT_TEST_PRINTF(char, wsprintfA, "%S", wchar_t); MPT_TEST_PRINTF(char, wsprintfA, "%hs", char); MPT_TEST_PRINTF(char, wsprintfA, "%hS", char); - MPT_TEST_PRINTF(char, wsprintfA, "%ls", wchar); - MPT_TEST_PRINTF(char, wsprintfA, "%lS", wchar); + MPT_TEST_PRINTF(char, wsprintfA, "%ls", wchar_t); + MPT_TEST_PRINTF(char, wsprintfA, "%lS", wchar_t); // WinAPI WCHAR - MPT_TEST_PRINTF(wchar, wsprintfW, L"%s", wchar); - MPT_TEST_PRINTF(wchar, wsprintfW, L"%S", char); - MPT_TEST_PRINTF(wchar, wsprintfW, L"%hs", char); - MPT_TEST_PRINTF(wchar, wsprintfW, L"%hS", char); - MPT_TEST_PRINTF(wchar, wsprintfW, L"%ls", wchar); - MPT_TEST_PRINTF(wchar, wsprintfW, L"%lS", wchar); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%s", wchar_t); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%S", char); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%hs", char); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%hS", char); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%ls", wchar_t); + MPT_TEST_PRINTF(wchar_t, wsprintfW, L"%lS", wchar_t); #undef MPT_TEST_PRINTF #undef MPT_TEST_PRINTF_n @@ -2235,21 +2840,21 @@ template <> inline Test::CustomSettingsTestType FromSettingValue(const SettingValue &val) { MPT_ASSERT(val.GetTypeTag() == "myType"); - std::string xy = val.as(); + mpt::ustring xy = val.as(); if(xy.empty()) { return Test::CustomSettingsTestType(0.0f, 0.0f); } - std::size_t pos = xy.find("|"); - std::string x = xy.substr(0, pos); - std::string y = xy.substr(pos + 1); - return Test::CustomSettingsTestType(ConvertStrTo(x.c_str()), ConvertStrTo(y.c_str())); + std::size_t pos = xy.find(U_("|")); + mpt::ustring x = xy.substr(0, pos); + mpt::ustring y = xy.substr(pos + 1); + return Test::CustomSettingsTestType(ConvertStrTo(x), ConvertStrTo(y)); } template <> inline SettingValue ToSettingValue(const Test::CustomSettingsTestType &val) { - return SettingValue(mpt::fmt::val(val.x) + "|" + mpt::fmt::val(val.y), "myType"); + return SettingValue(mpt::ufmt::val(val.x) + U_("|") + mpt::ufmt::val(val.y), "myType"); } namespace Test { @@ -2261,39 +2866,39 @@ static MPT_NOINLINE void TestSettings() #ifdef MODPLUG_TRACKER - VERIFY_EQUAL(SettingPath("a","b") < SettingPath("a","c"), true); - VERIFY_EQUAL(!(SettingPath("c","b") < SettingPath("a","c")), true); + VERIFY_EQUAL(SettingPath(U_("a"),U_("b")) < SettingPath(U_("a"),U_("c")), true); + VERIFY_EQUAL(!(SettingPath(U_("c"),U_("b")) < SettingPath(U_("a"),U_("c"))), true); { DefaultSettingsContainer conf; - int32 foobar = conf.Read("Test", "bar", 23); - conf.Write("Test", "bar", 64); - conf.Write("Test", "bar", 42); - conf.Read("Test", "baz", 4711); - foobar = conf.Read("Test", "bar", 28); + int32 foobar = conf.Read(U_("Test"), U_("bar"), 23); + conf.Write(U_("Test"), U_("bar"), 64); + conf.Write(U_("Test"), U_("bar"), 42); + conf.Read(U_("Test"), U_("baz"), 4711); + foobar = conf.Read(U_("Test"), U_("bar"), 28); } { DefaultSettingsContainer conf; - int32 foobar = conf.Read("Test", "bar", 28); + int32 foobar = conf.Read(U_("Test"), U_("bar"), 28); VERIFY_EQUAL(foobar, 42); - conf.Write("Test", "bar", 43); + conf.Write(U_("Test"), U_("bar"), 43); } { DefaultSettingsContainer conf; - int32 foobar = conf.Read("Test", "bar", 123); + int32 foobar = conf.Read(U_("Test"), U_("bar"), 123); VERIFY_EQUAL(foobar, 43); - conf.Write("Test", "bar", 88); + conf.Write(U_("Test"), U_("bar"), 88); } { DefaultSettingsContainer conf; - Setting foo(conf, "Test", "bar", 99); + Setting foo(conf, U_("Test"), U_("bar"), 99); VERIFY_EQUAL(foo, 88); @@ -2303,27 +2908,27 @@ static MPT_NOINLINE void TestSettings() { DefaultSettingsContainer conf; - Setting foo(conf, "Test", "bar", 99); + Setting foo(conf, U_("Test"), U_("bar"), 99); VERIFY_EQUAL(foo, 7); } { DefaultSettingsContainer conf; - conf.Read("Test", "struct", std::string("")); - conf.Write("Test", "struct", std::string("")); + conf.Read(U_("Test"), U_("struct"), std::string("")); + conf.Write(U_("Test"), U_("struct"), std::string("")); } { DefaultSettingsContainer conf; - CustomSettingsTestType dummy = conf.Read("Test", "struct", CustomSettingsTestType(1.0f, 1.0f)); + CustomSettingsTestType dummy = conf.Read(U_("Test"), U_("struct"), CustomSettingsTestType(1.0f, 1.0f)); dummy = CustomSettingsTestType(0.125f, 32.0f); - conf.Write("Test", "struct", dummy); + conf.Write(U_("Test"), U_("struct"), dummy); } { DefaultSettingsContainer conf; - Setting dummyVar(conf, "Test", "struct", CustomSettingsTestType(1.0f, 1.0f)); + Setting dummyVar(conf, U_("Test"), U_("struct"), CustomSettingsTestType(1.0f, 1.0f)); CustomSettingsTestType dummy = dummyVar; VERIFY_EQUAL(dummy.x, 0.125f); VERIFY_EQUAL(dummy.y, 32.0f); @@ -2407,9 +3012,9 @@ static void TestLoadXMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 1); // Macros - VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), sfx_reso); - VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), sfx_drywet); - VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), zxx_resomode); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), kSFxReso); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), kSFxDryWet); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), kZxxResoFltMode); // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 2); @@ -2456,11 +3061,11 @@ static void TestLoadXMFile(const CSoundFile &sndFile) // Sample Data for(size_t i = 0; i < 6; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample8[i], 18); + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 18); } for(size_t i = 6; i < 16; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample8[i], 0); + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 0); } // Instruments @@ -2474,7 +3079,7 @@ static void TestLoadXMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(pIns->nPPC, NOTE_MIDDLEC - 1); VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); - VERIFY_EQUAL_NONCONT(pIns->nResampling, (unsigned)SRCMODE_POLYPHASE); + VERIFY_EQUAL_NONCONT(pIns->nResampling, (unsigned)SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), false); VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0); @@ -2604,8 +3209,8 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerBeat, 6); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerMeasure, 12); VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MAKE_VERSION_NUMERIC(1, 19, 02, 05)); - VERIFY_EQUAL_NONCONT(sndFile.m_nResampling, SRCMODE_POLYPHASE); - VERIFY_EQUAL_NONCONT(sndFile.m_songArtist, MPT_USTRING("Tester")); + VERIFY_EQUAL_NONCONT(sndFile.m_nResampling, SRCMODE_SINC8LP); + VERIFY_EQUAL_NONCONT(sndFile.m_songArtist, U_("Tester")); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing.size(), 6); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[0], 29360125); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing[1], 4194305); @@ -2626,10 +3231,10 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT((uint32)((double)fh.openTime / HISTORY_TIMER_PRECISION), 31); // Macros - VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), sfx_reso); - VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), sfx_drywet); - VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(2), sfx_polyAT); - VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), zxx_resomode); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(0), kSFxReso); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(1), kSFxDryWet); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetParameteredMacroType(2), kSFxPolyAT); + VERIFY_EQUAL_NONCONT(sndFile.m_MidiCfg.GetFixedMacroType(), kZxxResoFltMode); // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 70); @@ -2683,11 +3288,11 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // Sample Data for(size_t i = 0; i < 6; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample8[i], 18); + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 18); } for(size_t i = 6; i < 16; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample8[i], 0); + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 0); } VERIFY_EQUAL_NONCONT(sample.cues[0], 2); @@ -2707,7 +3312,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // Sample Data (Stereo Interleaved) for(size_t i = 0; i < 7; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample16[4 + i], int16(-32768)); + VERIFY_EQUAL_NONCONT(sample.sample16()[4 + i], int16(-32768)); } VERIFY_EQUAL_NONCONT(sample.cues[0], 3); @@ -2745,7 +3350,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) #ifdef MPT_EXTERNAL_SAMPLES for(size_t i = 0; i < 16; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample8[i], int8(45)); + VERIFY_EQUAL_NONCONT(sample.sample8()[i], int8(45)); } #endif // MPT_EXTERNAL_SAMPLES @@ -2767,7 +3372,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(pIns->nPPC, (NOTE_MIDDLEC - NOTE_MIN) + 6); // F#5 VERIFY_EQUAL_NONCONT(pIns->nVolRampUp, 1200); - VERIFY_EQUAL_NONCONT(pIns->nResampling, (unsigned)SRCMODE_POLYPHASE); + VERIFY_EQUAL_NONCONT(pIns->nResampling, (unsigned)SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(pIns->IsCutoffEnabled(), true); VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0x32); @@ -2929,12 +3534,12 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(33, 0)); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 254); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 32 * 4); - VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 48); + VERIFY_EQUAL_NONCONT(sndFile.m_nVSTiVolume, 36); VERIFY_EQUAL_NONCONT(sndFile.m_nSamplePreAmp, 16); VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_FASTVOLSLIDES); VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), mixLevelsCompatible); VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeClassic); - VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? (MptVersion::num & 0xFFFF0000) : MAKE_VERSION_NUMERIC(1, 27, 00, 00)); + VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version(Version::Current().GetRawVersion() & 0xFFFF0000u) : MAKE_VERSION_NUMERIC(1, 27, 00, 00)); VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); // Channels @@ -2952,7 +3557,7 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[3].dwFlags, CHN_MUTE); // Samples - VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 3); + VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 4); { const ModSample &sample = sndFile.GetSample(1); VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[1], "Sample_1__________________X"), 0); @@ -2972,11 +3577,11 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) // Sample Data for(size_t i = 0; i < 30; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample8[i], 127); + VERIFY_EQUAL_NONCONT(sample.sample8()[i], 127); } for(size_t i = 31; i < 60; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample8[i], -128); + VERIFY_EQUAL_NONCONT(sample.sample8()[i], -128); } } @@ -3005,10 +3610,20 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) // Sample Data (Stereo Interleaved) for(size_t i = 0; i < 7; i++) { - VERIFY_EQUAL_NONCONT(sample.pSample16[4 + i], int16(-32768)); + VERIFY_EQUAL_NONCONT(sample.sample16()[4 + i], int16(-32768)); } } + { + const ModSample &sample = sndFile.GetSample(4); + VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[4], "adlib"), 0); + VERIFY_EQUAL_NONCONT(strcmp(sample.filename, ""), 0); + VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 8363); + VERIFY_EQUAL_NONCONT(sample.nVolume, 58 * 4); + VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_ADLIB); + VERIFY_EQUAL_NONCONT(sample.adlib, (OPLPatch{ { 0x00, 0x00, 0xC0, 0x00, 0xF0, 0xD2, 0x05, 0xB3, 0x01, 0x00, 0x00, 0x00 } })); + } + // Orders VERIFY_EQUAL_NONCONT(sndFile.Order().GetLengthTailTrimmed(), 5); VERIFY_EQUAL_NONCONT(sndFile.Order()[0], 0); @@ -3049,17 +3664,17 @@ static bool ShouldRunTests() { mpt::PathString theFile = theApp.GetAppDirPath(); // Only run the tests when we're in the project directory structure. - std::size_t pathComponents = mpt::String::Split(theFile.ToUnicode(), MPT_USTRING("\\")).size() - 1; + std::size_t pathComponents = mpt::String::Split(theFile.ToUnicode(), U_("\\")).size() - 1; for(std::size_t i = 0; i < pathComponents; ++i) { - if(theFile.IsDirectory() && (theFile + MPT_PATHSTRING("test")).IsDirectory()) + if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) { - if((theFile + MPT_PATHSTRING("test\\test.mptm")).IsFile()) + if((theFile + P_("test\\test.mptm")).IsFile()) { return true; } } - theFile += MPT_PATHSTRING("..\\"); + theFile += P_("..\\"); } return false; } @@ -3067,19 +3682,19 @@ static bool ShouldRunTests() static mpt::PathString GetTestFilenameBase() { mpt::PathString theFile = theApp.GetAppDirPath(); - std::size_t pathComponents = mpt::String::Split(theFile.ToUnicode(), MPT_USTRING("\\")).size() - 1; + std::size_t pathComponents = mpt::String::Split(theFile.ToUnicode(), U_("\\")).size() - 1; for(std::size_t i = 0; i < pathComponents; ++i) { - if(theFile.IsDirectory() && (theFile + MPT_PATHSTRING("test")).IsDirectory()) + if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) { - if((theFile + MPT_PATHSTRING("test\\test.mptm")).IsFile()) + if((theFile + P_("test\\test.mptm")).IsFile()) { break; } } - theFile += MPT_PATHSTRING("..\\"); + theFile += P_("..\\"); } - theFile += MPT_PATHSTRING("test/test."); + theFile += P_("test/test."); return theFile; } @@ -3090,15 +3705,15 @@ static mpt::PathString GetTempFilenameBase() typedef CModDoc *TSoundFileContainer; -static CSoundFile &GetrSoundFile(TSoundFileContainer &sndFile) +static CSoundFile &GetSoundFile(TSoundFileContainer &sndFile) { - return sndFile->GetrSoundFile(); + return sndFile->GetSoundFile(); } static TSoundFileContainer CreateSoundFileContainer(const mpt::PathString &filename) { - CModDoc *pModDoc = (CModDoc *)theApp.OpenDocumentFile(filename, FALSE); + CModDoc *pModDoc = static_cast(theApp.OpenDocumentFile(filename.ToCString(), FALSE)); return pModDoc; } @@ -3128,26 +3743,30 @@ static void SaveS3M(const TSoundFileContainer &sndFile, const mpt::PathString &f theApp.RemoveMruItem(0); } -#else +#else // !MODPLUG_TRACKER static bool ShouldRunTests() { - return true; + #if MPT_TEST_HAS_FILESYSTEM + return true; + #else + return false; + #endif } static mpt::PathString GetTestFilenameBase() { - return Test::GetPathPrefix() + MPT_PATHSTRING("./test/test."); + return Test::GetPathPrefix() + P_("./test/test."); } static mpt::PathString GetTempFilenameBase() { - return MPT_PATHSTRING("./test."); + return P_("./test."); } typedef std::shared_ptr TSoundFileContainer; -static CSoundFile &GetrSoundFile(TSoundFileContainer &sndFile) +static CSoundFile &GetSoundFile(TSoundFileContainer &sndFile) { return *sndFile.get(); } @@ -3170,22 +3789,25 @@ static void DestroySoundFileContainer(TSoundFileContainer & /* sndFile */ ) static void SaveIT(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { - sndFile->SaveIT(filename, false); + mpt::ofstream f(filename, std::ios::binary); + sndFile->SaveIT(f, filename, false); } static void SaveXM(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { - sndFile->SaveXM(filename, false); + mpt::ofstream f(filename, std::ios::binary); + sndFile->SaveXM(f, false); } static void SaveS3M(const TSoundFileContainer &sndFile, const mpt::PathString &filename) { - sndFile->SaveS3M(filename); + mpt::ofstream f(filename, std::ios::binary); + sndFile->SaveS3M(f); } -#endif +#endif // !MODPLUG_NO_FILESAVE -#endif +#endif // MODPLUG_TRACKER @@ -3207,14 +3829,14 @@ static MPT_NOINLINE void TestLoadSaveFile() // Test MPTM file loading { - TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + MPT_PATHSTRING("mptm")); + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("mptm")); - TestLoadMPTMFile(GetrSoundFile(sndFileContainer)); + TestLoadMPTMFile(GetSoundFile(sndFileContainer)); #ifndef MODPLUG_NO_FILESAVE // Test file saving - GetrSoundFile(sndFileContainer).m_dwLastSavedWithVersion = MptVersion::num; - SaveIT(sndFileContainer, filenameBase + MPT_PATHSTRING("saved.mptm")); + GetSoundFile(sndFileContainer).m_dwLastSavedWithVersion = Version::Current(); + SaveIT(sndFileContainer, filenameBase + P_("saved.mptm")); #endif DestroySoundFileContainer(sndFileContainer); @@ -3223,34 +3845,34 @@ static MPT_NOINLINE void TestLoadSaveFile() // Reload the saved file and test if everything is still working correctly. #ifndef MODPLUG_NO_FILESAVE { - TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + MPT_PATHSTRING("saved.mptm")); + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.mptm")); - TestLoadMPTMFile(GetrSoundFile(sndFileContainer)); + TestLoadMPTMFile(GetSoundFile(sndFileContainer)); DestroySoundFileContainer(sndFileContainer); - RemoveFile(filenameBase + MPT_PATHSTRING("saved.mptm")); + RemoveFile(filenameBase + P_("saved.mptm")); } #endif // Test XM file loading { - TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + MPT_PATHSTRING("xm")); + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("xm")); - TestLoadXMFile(GetrSoundFile(sndFileContainer)); + TestLoadXMFile(GetSoundFile(sndFileContainer)); // In OpenMPT 1.20 (up to revision 1459), there was a bug in the XM saver // that would create broken XMs if the sample map contained samples that // were only referenced below C-1 or above B-8 (such samples should not // be written). Let's insert a sample there and check if re-loading the // file still works. - GetrSoundFile(sndFileContainer).m_nSamples++; - GetrSoundFile(sndFileContainer).Instruments[1]->Keyboard[110] = GetrSoundFile(sndFileContainer).GetNumSamples(); + GetSoundFile(sndFileContainer).m_nSamples++; + GetSoundFile(sndFileContainer).Instruments[1]->Keyboard[110] = GetSoundFile(sndFileContainer).GetNumSamples(); #ifndef MODPLUG_NO_FILESAVE // Test file saving - GetrSoundFile(sndFileContainer).m_dwLastSavedWithVersion = MptVersion::num; - SaveXM(sndFileContainer, filenameBase + MPT_PATHSTRING("saved.xm")); + GetSoundFile(sndFileContainer).m_dwLastSavedWithVersion = Version::Current(); + SaveXM(sndFileContainer, filenameBase + P_("saved.xm")); #endif DestroySoundFileContainer(sndFileContainer); @@ -3259,39 +3881,43 @@ static MPT_NOINLINE void TestLoadSaveFile() // Reload the saved file and test if everything is still working correctly. #ifndef MODPLUG_NO_FILESAVE { - TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + MPT_PATHSTRING("saved.xm")); + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.xm")); - TestLoadXMFile(GetrSoundFile(sndFileContainer)); + TestLoadXMFile(GetSoundFile(sndFileContainer)); DestroySoundFileContainer(sndFileContainer); - RemoveFile(filenameBase + MPT_PATHSTRING("saved.xm")); + RemoveFile(filenameBase + P_("saved.xm")); } #endif // Test S3M file loading { - TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + MPT_PATHSTRING("s3m")); + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBaseSrc + P_("s3m")); + auto &sndFile = GetSoundFile(sndFileContainer); - TestLoadS3MFile(GetrSoundFile(sndFileContainer), false); + TestLoadS3MFile(sndFile, false); // Test GetLength code, in particular with subsongs - VERIFY_EQUAL_EPS(GetrSoundFile(sndFileContainer).GetLength(eAdjustSamplePositions, GetLengthTarget(3, 1)).back().duration, 19.237, 0.01); - VERIFY_EQUAL_NONCONT(GetrSoundFile(sndFileContainer).GetLength(eAdjustSamplePositions, GetLengthTarget(2, 0).StartPos(0, 1, 0)).back().targetReached, false); + sndFile.ChnSettings[1].dwFlags.reset(CHN_MUTE); + + VERIFY_EQUAL_EPS(sndFile.GetLength(eAdjustSamplePositions, GetLengthTarget(3, 1)).back().duration, 19.237, 0.01); + VERIFY_EQUAL_NONCONT(sndFile.GetLength(eAdjustSamplePositions, GetLengthTarget(2, 0).StartPos(0, 1, 0)).back().targetReached, false); - auto allSubSongs = GetrSoundFile(sndFileContainer).GetLength(eNoAdjust, GetLengthTarget(true)); + auto allSubSongs = sndFile.GetLength(eNoAdjust, GetLengthTarget(true)); VERIFY_EQUAL_NONCONT(allSubSongs.size(), 3); double totalDuration = 0.0; for(const auto &subSong : allSubSongs) { totalDuration += subSong.duration; } - VERIFY_EQUAL_EPS(totalDuration, 2505.53, 0.01); + VERIFY_EQUAL_EPS(totalDuration, 3674.38, 1.0); #ifndef MODPLUG_NO_FILESAVE // Test file saving - GetrSoundFile(sndFileContainer).m_dwLastSavedWithVersion = MptVersion::num; - SaveS3M(sndFileContainer, filenameBase + MPT_PATHSTRING("saved.s3m")); + sndFile.ChnSettings[1].dwFlags.set(CHN_MUTE); + sndFile.m_dwLastSavedWithVersion = Version::Current(); + SaveS3M(sndFileContainer, filenameBase + P_("saved.s3m")); #endif DestroySoundFileContainer(sndFileContainer); @@ -3300,13 +3926,13 @@ static MPT_NOINLINE void TestLoadSaveFile() // Reload the saved file and test if everything is still working correctly. #ifndef MODPLUG_NO_FILESAVE { - TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + MPT_PATHSTRING("saved.s3m")); + TSoundFileContainer sndFileContainer = CreateSoundFileContainer(filenameBase + P_("saved.s3m")); - TestLoadS3MFile(GetrSoundFile(sndFileContainer), true); + TestLoadS3MFile(GetSoundFile(sndFileContainer), true); DestroySoundFileContainer(sndFileContainer); - RemoveFile(filenameBase + MPT_PATHSTRING("saved.s3m")); + RemoveFile(filenameBase + P_("saved.s3m")); } #endif @@ -3344,6 +3970,25 @@ static MPT_NOINLINE void TestLoadSaveFile() mpt::IO::Write(f, data); VERIFY_EQUAL(f.str(), std::string("\x12\x34\x56\x78")); } + { + mpt::ostringstream f; + std::vector data; + data.resize(3); + data[0] = 0x1234; + data[1] = 0x5678; + data[2] = 0x1234; + mpt::IO::Write(f, data); + VERIFY_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34")); + } + { + mpt::ostringstream f; + int16be data[3]; + data[0] = 0x1234; + data[1] = 0x5678; + data[2] = 0x1234; + mpt::IO::Write(f, data); + VERIFY_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34")); + } #ifdef MODPLUG_TRACKER TrackerSettings::Instance().MiscSaveChannelMuteStatus = saveMutedChannels; @@ -3356,7 +4001,7 @@ static MPT_NOINLINE void TestEditing() { #ifdef MODPLUG_TRACKER auto modDoc = static_cast(theApp.GetModDocTemplate()->CreateNewDocument()); - auto &sndFile = modDoc->GetrSoundFile(); + auto &sndFile = modDoc->GetSoundFile(); sndFile.Create(FileReader(), CSoundFile::loadCompleteModule, modDoc); sndFile.m_nChannels = 4; sndFile.ChangeModTypeTo(MOD_TYPE_MPT); @@ -3395,7 +4040,7 @@ static MPT_NOINLINE void TestEditing() sndFile.GetSample(2).nLength = 16; sndFile.GetSample(2).AllocateSample(); modDoc->ReArrangeSamples({ 2, SAMPLEINDEX_INVALID, 1 }); - VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).pSample != nullptr, true); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).HasSampleData(), true); VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).filename, std::string("2")); VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], std::string("2")); VERIFY_EQUAL_NONCONT(sndFile.GetSample(2).filename, std::string()); @@ -3431,7 +4076,7 @@ static void RunITCompressionTest(const std::vector &sampleData, FlagSet(sampleData.data()); + smp.pData.pSample = const_cast(sampleData.data()); smp.nLength = mpt::saturate_cast(sampleData.size() / smp.GetBytesPerSample()); std::string data; @@ -3446,7 +4091,7 @@ static void RunITCompressionTest(const std::vector &sampleData, FlagSet(mpt::as_span(data))); std::vector sampleDataNew(sampleData.size(), 0); - smp.pSample = sampleDataNew.data(); + smp.pData.pSample = sampleDataNew.data(); ITDecompression decompression(file, smp, it215); VERIFY_EQUAL_NONCONT(memcmp(sampleData.data(), sampleDataNew.data(), sampleData.size()), 0); @@ -3533,7 +4178,7 @@ static double Rand01() template T Rand(const T min, const T max) { - return Util::Round(min + Rand01() * (max - min)); + return mpt::saturate_round(min + Rand01() * (max - min)); } static void GenerateCommands(CPattern& pat, const double dProbPcs, const double dProbPc) @@ -3614,16 +4259,30 @@ static MPT_NOINLINE void TestStringIO() char dst2[3]; // Destination buffer, smaller than source buffer #define ReadTest(mode, dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::Read(dst, src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, CountOf(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = strlen(dst); i < CountOf(dst); i++) \ - VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < mpt::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadBuf(mpt::String:: mode , src); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < mpt::size(dst); i++) \ + /* VERIFY_EQUAL_NONCONT(dst[i], '\0'); */ /* Ensure that rest of the buffer is completely nulled */ \ + /**/ #define WriteTest(mode, dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::Write(dst, src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, CountOf(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = mpt::strnlen(dst, CountOf(dst)); i < CountOf(dst); i++) \ - VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteBuf(mpt::String:: mode , dst) = mpt::String::ReadAutoBuf(src); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ // Check reading of null-terminated string into larger buffer ReadTest(nullTerminated, dst1, src0, ""); @@ -3678,7 +4337,7 @@ static MPT_NOINLINE void TestStringIO() // Check reading of space-padded strings ReadTest(spacePadded, dst2, src0, " X"); - ReadTest(spacePadded, dst2, src1, "X"); + ReadTest(spacePadded, dst2, src1, "X "); ReadTest(spacePadded, dst2, src2, "XY"); ReadTest(spacePadded, dst2, src3, "XY"); ReadTest(spacePadded, dst2, src4, "xy"); @@ -3741,20 +4400,30 @@ static MPT_NOINLINE void TestStringIO() { std::string dststring; - std::string src0string = std::string(src0, CountOf(src0)); - std::string src1string = std::string(src1, CountOf(src1)); - std::string src2string = std::string(src2, CountOf(src2)); - std::string src3string = std::string(src3, CountOf(src3)); + std::string src0string = std::string(src0, mpt::size(src0)); + std::string src1string = std::string(src1, mpt::size(src1)); + std::string src2string = std::string(src2, mpt::size(src2)); + std::string src3string = std::string(src3, mpt::size(src3)); #define ReadTest(mode, dst, src, expectedResult) \ mpt::String::Read(dst, src); \ VERIFY_EQUAL_NONCONT(dst, expectedResult); /* Ensure that the strings are identical */ \ + dst = mpt::String::ReadBuf(mpt::String:: mode , src); \ + VERIFY_EQUAL_NONCONT(dst, expectedResult); /* Ensure that the strings are identical */ \ + /**/ #define WriteTest(mode, dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::Write(dst, src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, CountOf(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = mpt::strnlen(dst, CountOf(dst)); i < CountOf(dst); i++) \ - VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteBuf(mpt::String:: mode , dst) = src; \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ // Check reading of null-terminated string into std::string ReadTest(nullTerminated, dststring, src0, ""); @@ -3847,9 +4516,9 @@ static MPT_NOINLINE void TestStringIO() mpt::String::FixNullString(src1); mpt::String::FixNullString(src2); mpt::String::FixNullString(src3); - VERIFY_EQUAL_NONCONT(strncmp(src1, "X ", CountOf(src1)), 0); - VERIFY_EQUAL_NONCONT(strncmp(src2, "XYZ", CountOf(src2)), 0); - VERIFY_EQUAL_NONCONT(strncmp(src3, "XYZ", CountOf(src3)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src1, "X ", mpt::size(src1)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src2, "XYZ", mpt::size(src2)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src3, "XYZ", mpt::size(src3)), 0); } @@ -3956,13 +4625,13 @@ static MPT_NOINLINE void TestSampleConversion() sample.Initialize(); sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); - sample.pSample = (static_cast(targetBuf) + 65536); + sample.pData.pSample = (static_cast(targetBuf) + 65536); CopyAndNormalizeSample, SC::DecodeInt24<0, littleEndian24> > >(sample, mpt::byte_cast(source24), 3*65536); CopySample, SC::DecodeInt24<0, littleEndian24> > >(truncated16, 65536, 1, mpt::byte_cast(source24), 65536 * 3, 1); for(size_t i = 0; i < 65536; i++) { - VERIFY_EQUAL_QUIET_NONCONT(sample.pSample16[i], static_cast(i)); + VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast(i)); VERIFY_EQUAL_QUIET_NONCONT(truncated16[i], static_cast(i)); } } @@ -3973,10 +4642,10 @@ static MPT_NOINLINE void TestSampleConversion() for(size_t i = 0; i < 65536; i++) { IEEE754binary32BE floatbits = IEEE754binary32BE((static_cast(i) / 65536.0f) - 0.5f); - source32[i * 4 + 0] = floatbits.GetByte(0); - source32[i * 4 + 1] = floatbits.GetByte(1); - source32[i * 4 + 2] = floatbits.GetByte(2); - source32[i * 4 + 3] = floatbits.GetByte(3); + source32[i * 4 + 0] = mpt::byte_cast(floatbits.GetByte(0)); + source32[i * 4 + 1] = mpt::byte_cast(floatbits.GetByte(1)); + source32[i * 4 + 2] = mpt::byte_cast(floatbits.GetByte(2)); + source32[i * 4 + 3] = mpt::byte_cast(floatbits.GetByte(3)); } int16 *truncated16 = static_cast(targetBuf); @@ -3984,13 +4653,13 @@ static MPT_NOINLINE void TestSampleConversion() sample.Initialize(); sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); - sample.pSample = static_cast(targetBuf) + 65536; + sample.pData.pSample = static_cast(targetBuf) + 65536; CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, mpt::byte_cast(source32), 4*65536); CopySample, SC::DecodeFloat32 > >(truncated16, 65536, 1, mpt::byte_cast(source32), 65536 * 4, 1); for(size_t i = 0; i < 65536; i++) { - VERIFY_EQUAL_QUIET_NONCONT(sample.pSample16[i], static_cast(i - 0x8000u)); + VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast(i - 0x8000u)); if(mpt::abs(truncated16[i] - static_cast((i - 0x8000u) / 2)) > 1) { VERIFY_EQUAL_QUIET_NONCONT(true, false); diff --git a/Frameworks/OpenMPT/OpenMPT/test/test.h b/Frameworks/OpenMPT/OpenMPT/test/test.h index 30e37cd28..24625bb94 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/test.h +++ b/Frameworks/OpenMPT/OpenMPT/test/test.h @@ -11,6 +11,8 @@ #pragma once +#include "BuildSettings.h" + OPENMPT_NAMESPACE_BEGIN namespace Test { diff --git a/Frameworks/OpenMPT/OpenMPT/test/test.s3m b/Frameworks/OpenMPT/OpenMPT/test/test.s3m index c13c3c81f..5013ee837 100644 Binary files a/Frameworks/OpenMPT/OpenMPT/test/test.s3m and b/Frameworks/OpenMPT/OpenMPT/test/test.s3m differ diff --git a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj index da211f1ed..2f4921442 100644 --- a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj @@ -7,13 +7,28 @@ objects = { /* Begin PBXBuildFile section */ + 831132D521F955B2001F678F /* mptMemory.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132C921F955B0001F678F /* mptMemory.h */; }; + 831132D621F955B2001F678F /* mptAlloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831132CA21F955B0001F678F /* mptAlloc.cpp */; }; + 831132D721F955B2001F678F /* mptBaseMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132CB21F955B0001F678F /* mptBaseMacros.h */; }; + 831132D821F955B2001F678F /* mptStringBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831132CC21F955B1001F678F /* mptStringBuffer.cpp */; }; + 831132D921F955B2001F678F /* mptAlloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132CD21F955B1001F678F /* mptAlloc.h */; }; + 831132DA21F955B2001F678F /* mptBaseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132CE21F955B1001F678F /* mptBaseTypes.h */; }; + 831132DB21F955B2001F678F /* mptSpan.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132CF21F955B1001F678F /* mptSpan.h */; }; + 831132DC21F955B2001F678F /* mptStringBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132D021F955B1001F678F /* mptStringBuffer.h */; }; + 831132DD21F955B2001F678F /* mptBaseUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132D121F955B1001F678F /* mptBaseUtils.h */; }; + 831132DE21F955B2001F678F /* mptException.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132D221F955B2001F678F /* mptException.h */; }; + 831132DF21F955B2001F678F /* mptExceptionText.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132D321F955B2001F678F /* mptExceptionText.h */; }; + 831132E021F955B2001F678F /* mptAssert.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132D421F955B2001F678F /* mptAssert.h */; }; + 831132E221F955F4001F678F /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 831132E121F955F3001F678F /* LICENSE */; }; + 831132E821F9565F001F678F /* BitReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132E321F9565E001F678F /* BitReader.h */; }; + 831132E921F9565F001F678F /* opal.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132E421F9565F001F678F /* opal.h */; }; + 831132EA21F9565F001F678F /* OPL.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132E521F9565F001F678F /* OPL.h */; }; + 831132EB21F9565F001F678F /* Load_c67.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831132E621F9565F001F678F /* Load_c67.cpp */; }; + 831132EC21F9565F001F678F /* OPL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831132E721F9565F001F678F /* OPL.cpp */; }; 83E5EFD01FFEF9D200659F0F /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5EFCE1FFEF9D200659F0F /* config.h */; }; 83E5F8801FFEF9E400659F0F /* minimp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E5F2461FFEF9E100659F0F /* minimp3.c */; }; 83E5F8811FFEF9E400659F0F /* minimp3.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5F2471FFEF9E100659F0F /* minimp3.h */; }; - 83E5F8831FFEF9E400659F0F /* LGPL.txt in Resources */ = {isa = PBXBuildFile; fileRef = 83E5F2491FFEF9E100659F0F /* LGPL.txt */; }; - 83E5F8841FFEF9E400659F0F /* libc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5F24A1FFEF9E100659F0F /* libc.h */; }; 83E5F9831FFEF9E400659F0F /* stb_vorbis.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E5F3691FFEF9E100659F0F /* stb_vorbis.c */; }; - 83E5FC621FFEFA0D00659F0F /* typedefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC291FFEFA0D00659F0F /* typedefs.cpp */; }; 83E5FC631FFEFA0D00659F0F /* mptCRC.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2A1FFEFA0D00659F0F /* mptCRC.h */; }; 83E5FC641FFEFA0D00659F0F /* mptLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2B1FFEFA0D00659F0F /* mptLibrary.h */; }; 83E5FC651FFEFA0D00659F0F /* mptIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC2C1FFEFA0D00659F0F /* mptIO.h */; }; @@ -25,14 +40,11 @@ 83E5FC6B1FFEFA0D00659F0F /* mptLibrary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC321FFEFA0D00659F0F /* mptLibrary.cpp */; }; 83E5FC6C1FFEFA0D00659F0F /* mptStringFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC331FFEFA0D00659F0F /* mptStringFormat.h */; }; 83E5FC6D1FFEFA0D00659F0F /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC341FFEFA0D00659F0F /* Logging.cpp */; }; - 83E5FC6E1FFEFA0D00659F0F /* typedefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC351FFEFA0D00659F0F /* typedefs.h */; }; 83E5FC6F1FFEFA0D00659F0F /* Profiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC361FFEFA0D00659F0F /* Profiler.cpp */; }; 83E5FC701FFEFA0D00659F0F /* mptMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC371FFEFA0D00659F0F /* mptMutex.h */; }; 83E5FC711FFEFA0D00659F0F /* version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC381FFEFA0D00659F0F /* version.cpp */; }; - 83E5FC721FFEFA0D00659F0F /* StringFixer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC391FFEFA0D00659F0F /* StringFixer.h */; }; 83E5FC731FFEFA0D00659F0F /* mptUUID.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3A1FFEFA0D00659F0F /* mptUUID.h */; }; 83E5FC741FFEFA0D00659F0F /* BuildSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */; }; - 83E5FC751FFEFA0D00659F0F /* WriteMemoryDump.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3C1FFEFA0D00659F0F /* WriteMemoryDump.h */; }; 83E5FC761FFEFA0D00659F0F /* mptCPU.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC3D1FFEFA0D00659F0F /* mptCPU.cpp */; }; 83E5FC771FFEFA0D00659F0F /* mptTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3E1FFEFA0D00659F0F /* mptTime.h */; }; 83E5FC781FFEFA0D00659F0F /* FileReaderFwd.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC3F1FFEFA0D00659F0F /* FileReaderFwd.h */; }; @@ -41,7 +53,6 @@ 83E5FC7B1FFEFA0D00659F0F /* mptWine.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC421FFEFA0D00659F0F /* mptWine.h */; }; 83E5FC7C1FFEFA0D00659F0F /* misc_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC431FFEFA0D00659F0F /* misc_util.h */; }; 83E5FC7D1FFEFA0D00659F0F /* mptOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */; }; - 83E5FC7E1FFEFA0D00659F0F /* mptTypeTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC451FFEFA0D00659F0F /* mptTypeTraits.h */; }; 83E5FC7F1FFEFA0D00659F0F /* serialization_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC461FFEFA0D00659F0F /* serialization_utils.cpp */; }; 83E5FC801FFEFA0D00659F0F /* mptFileIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC471FFEFA0D00659F0F /* mptFileIO.h */; }; 83E5FC811FFEFA0D00659F0F /* mptStringFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC481FFEFA0D00659F0F /* mptStringFormat.cpp */; }; @@ -59,7 +70,6 @@ 83E5FC8D1FFEFA0D00659F0F /* mptStringParse.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC541FFEFA0D00659F0F /* mptStringParse.h */; }; 83E5FC8E1FFEFA0D00659F0F /* mptRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC551FFEFA0D00659F0F /* mptRandom.h */; }; 83E5FC8F1FFEFA0D00659F0F /* CompilerDetect.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC561FFEFA0D00659F0F /* CompilerDetect.h */; }; - 83E5FC901FFEFA0D00659F0F /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FC571FFEFA0D00659F0F /* stdafx.cpp */; }; 83E5FC911FFEFA0D00659F0F /* FileReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC581FFEFA0D00659F0F /* FileReader.h */; }; 83E5FC921FFEFA0D00659F0F /* mptPathString.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC591FFEFA0D00659F0F /* mptPathString.h */; }; 83E5FC931FFEFA0D00659F0F /* Profiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC5A1FFEFA0D00659F0F /* Profiler.h */; }; @@ -145,7 +155,6 @@ 83E5FDE11FFEFA8500659F0F /* PlugInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3A1FFEFA8400659F0F /* PlugInterface.cpp */; }; 83E5FDE21FFEFA8500659F0F /* LFOPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3B1FFEFA8400659F0F /* LFOPlugin.cpp */; }; 83E5FDE31FFEFA8500659F0F /* OpCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD3C1FFEFA8400659F0F /* OpCodes.h */; }; - 83E5FDE41FFEFA8500659F0F /* PluginEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD3D1FFEFA8400659F0F /* PluginEventQueue.h */; }; 83E5FDE51FFEFA8500659F0F /* PluginManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3E1FFEFA8400659F0F /* PluginManager.cpp */; }; 83E5FDE61FFEFA8500659F0F /* DigiBoosterEcho.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD3F1FFEFA8400659F0F /* DigiBoosterEcho.cpp */; }; 83E5FDE71FFEFA8500659F0F /* DMOPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD411FFEFA8400659F0F /* DMOPlugin.cpp */; }; @@ -272,17 +281,32 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 831132C921F955B0001F678F /* mptMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptMemory.h; sourceTree = ""; }; + 831132CA21F955B0001F678F /* mptAlloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptAlloc.cpp; sourceTree = ""; }; + 831132CB21F955B0001F678F /* mptBaseMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptBaseMacros.h; sourceTree = ""; }; + 831132CC21F955B1001F678F /* mptStringBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptStringBuffer.cpp; sourceTree = ""; }; + 831132CD21F955B1001F678F /* mptAlloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptAlloc.h; sourceTree = ""; }; + 831132CE21F955B1001F678F /* mptBaseTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptBaseTypes.h; sourceTree = ""; }; + 831132CF21F955B1001F678F /* mptSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptSpan.h; sourceTree = ""; }; + 831132D021F955B1001F678F /* mptStringBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptStringBuffer.h; sourceTree = ""; }; + 831132D121F955B1001F678F /* mptBaseUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptBaseUtils.h; sourceTree = ""; }; + 831132D221F955B2001F678F /* mptException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptException.h; sourceTree = ""; }; + 831132D321F955B2001F678F /* mptExceptionText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptExceptionText.h; sourceTree = ""; }; + 831132D421F955B2001F678F /* mptAssert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptAssert.h; sourceTree = ""; }; + 831132E121F955F3001F678F /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + 831132E321F9565E001F678F /* BitReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BitReader.h; sourceTree = ""; }; + 831132E421F9565F001F678F /* opal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opal.h; sourceTree = ""; }; + 831132E521F9565F001F678F /* OPL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OPL.h; sourceTree = ""; }; + 831132E621F9565F001F678F /* Load_c67.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_c67.cpp; sourceTree = ""; }; + 831132E721F9565F001F678F /* OPL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OPL.cpp; sourceTree = ""; }; 83E5EFBD1FFEF7CC00659F0F /* libOpenMPT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libOpenMPT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 83E5EFCE1FFEF9D200659F0F /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = SOURCE_ROOT; }; 83E5EFCF1FFEF9D200659F0F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 83E5F2461FFEF9E100659F0F /* minimp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = minimp3.c; sourceTree = ""; }; 83E5F2471FFEF9E100659F0F /* minimp3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minimp3.h; sourceTree = ""; }; 83E5F2481FFEF9E100659F0F /* OpenMPT.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OpenMPT.txt; sourceTree = ""; }; - 83E5F2491FFEF9E100659F0F /* LGPL.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LGPL.txt; sourceTree = ""; }; - 83E5F24A1FFEF9E100659F0F /* libc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libc.h; sourceTree = ""; }; 83E5F3691FFEF9E100659F0F /* stb_vorbis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stb_vorbis.c; sourceTree = ""; }; 83E5F36A1FFEF9E100659F0F /* OpenMPT.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OpenMPT.txt; sourceTree = ""; }; - 83E5FC291FFEFA0D00659F0F /* typedefs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = typedefs.cpp; sourceTree = ""; }; 83E5FC2A1FFEFA0D00659F0F /* mptCRC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptCRC.h; sourceTree = ""; }; 83E5FC2B1FFEFA0D00659F0F /* mptLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptLibrary.h; sourceTree = ""; }; 83E5FC2C1FFEFA0D00659F0F /* mptIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptIO.h; sourceTree = ""; }; @@ -294,14 +318,11 @@ 83E5FC321FFEFA0D00659F0F /* mptLibrary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptLibrary.cpp; sourceTree = ""; }; 83E5FC331FFEFA0D00659F0F /* mptStringFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptStringFormat.h; sourceTree = ""; }; 83E5FC341FFEFA0D00659F0F /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = ""; }; - 83E5FC351FFEFA0D00659F0F /* typedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = typedefs.h; sourceTree = ""; }; 83E5FC361FFEFA0D00659F0F /* Profiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Profiler.cpp; sourceTree = ""; }; 83E5FC371FFEFA0D00659F0F /* mptMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptMutex.h; sourceTree = ""; }; 83E5FC381FFEFA0D00659F0F /* version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version.cpp; sourceTree = ""; }; - 83E5FC391FFEFA0D00659F0F /* StringFixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringFixer.h; sourceTree = ""; }; 83E5FC3A1FFEFA0D00659F0F /* mptUUID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptUUID.h; sourceTree = ""; }; 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BuildSettings.h; sourceTree = ""; }; - 83E5FC3C1FFEFA0D00659F0F /* WriteMemoryDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WriteMemoryDump.h; sourceTree = ""; }; 83E5FC3D1FFEFA0D00659F0F /* mptCPU.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptCPU.cpp; sourceTree = ""; }; 83E5FC3E1FFEFA0D00659F0F /* mptTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptTime.h; sourceTree = ""; }; 83E5FC3F1FFEFA0D00659F0F /* FileReaderFwd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileReaderFwd.h; sourceTree = ""; }; @@ -310,7 +331,6 @@ 83E5FC421FFEFA0D00659F0F /* mptWine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptWine.h; sourceTree = ""; }; 83E5FC431FFEFA0D00659F0F /* misc_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = misc_util.h; sourceTree = ""; }; 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptOS.cpp; sourceTree = ""; }; - 83E5FC451FFEFA0D00659F0F /* mptTypeTraits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptTypeTraits.h; sourceTree = ""; }; 83E5FC461FFEFA0D00659F0F /* serialization_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization_utils.cpp; sourceTree = ""; }; 83E5FC471FFEFA0D00659F0F /* mptFileIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptFileIO.h; sourceTree = ""; }; 83E5FC481FFEFA0D00659F0F /* mptStringFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mptStringFormat.cpp; sourceTree = ""; }; @@ -328,7 +348,6 @@ 83E5FC541FFEFA0D00659F0F /* mptStringParse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptStringParse.h; sourceTree = ""; }; 83E5FC551FFEFA0D00659F0F /* mptRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptRandom.h; sourceTree = ""; }; 83E5FC561FFEFA0D00659F0F /* CompilerDetect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompilerDetect.h; sourceTree = ""; }; - 83E5FC571FFEFA0D00659F0F /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = ""; }; 83E5FC581FFEFA0D00659F0F /* FileReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileReader.h; sourceTree = ""; }; 83E5FC591FFEFA0D00659F0F /* mptPathString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptPathString.h; sourceTree = ""; }; 83E5FC5A1FFEFA0D00659F0F /* Profiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Profiler.h; sourceTree = ""; }; @@ -414,7 +433,6 @@ 83E5FD3A1FFEFA8400659F0F /* PlugInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlugInterface.cpp; sourceTree = ""; }; 83E5FD3B1FFEFA8400659F0F /* LFOPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LFOPlugin.cpp; sourceTree = ""; }; 83E5FD3C1FFEFA8400659F0F /* OpCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpCodes.h; sourceTree = ""; }; - 83E5FD3D1FFEFA8400659F0F /* PluginEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginEventQueue.h; sourceTree = ""; }; 83E5FD3E1FFEFA8400659F0F /* PluginManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginManager.cpp; sourceTree = ""; }; 83E5FD3F1FFEFA8400659F0F /* DigiBoosterEcho.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DigiBoosterEcho.cpp; sourceTree = ""; }; 83E5FD411FFEFA8400659F0F /* DMOPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DMOPlugin.cpp; sourceTree = ""; }; @@ -598,11 +616,10 @@ 83E5F2451FFEF9E100659F0F /* minimp3 */ = { isa = PBXGroup; children = ( + 831132E121F955F3001F678F /* LICENSE */, 83E5F2461FFEF9E100659F0F /* minimp3.c */, 83E5F2471FFEF9E100659F0F /* minimp3.h */, 83E5F2481FFEF9E100659F0F /* OpenMPT.txt */, - 83E5F2491FFEF9E100659F0F /* LGPL.txt */, - 83E5F24A1FFEF9E100659F0F /* libc.h */, ); path = minimp3; sourceTree = ""; @@ -619,63 +636,69 @@ 83E5FC281FFEFA0D00659F0F /* common */ = { isa = PBXGroup; children = ( - 83E5FC291FFEFA0D00659F0F /* typedefs.cpp */, - 83E5FC2A1FFEFA0D00659F0F /* mptCRC.h */, - 83E5FC2B1FFEFA0D00659F0F /* mptLibrary.h */, - 83E5FC2C1FFEFA0D00659F0F /* mptIO.h */, - 83E5FC2D1FFEFA0D00659F0F /* version.h */, - 83E5FC2E1FFEFA0D00659F0F /* mptStringParse.cpp */, - 83E5FC2F1FFEFA0D00659F0F /* stdafx.h */, + 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */, + 83E5FC561FFEFA0D00659F0F /* CompilerDetect.h */, + 83E5FC411FFEFA0D00659F0F /* ComponentManager.cpp */, 83E5FC301FFEFA0D00659F0F /* ComponentManager.h */, 83E5FC311FFEFA0D00659F0F /* Endianness.h */, - 83E5FC321FFEFA0D00659F0F /* mptLibrary.cpp */, - 83E5FC331FFEFA0D00659F0F /* mptStringFormat.h */, - 83E5FC341FFEFA0D00659F0F /* Logging.cpp */, - 83E5FC351FFEFA0D00659F0F /* typedefs.h */, - 83E5FC361FFEFA0D00659F0F /* Profiler.cpp */, - 83E5FC371FFEFA0D00659F0F /* mptMutex.h */, - 83E5FC381FFEFA0D00659F0F /* version.cpp */, - 83E5FC391FFEFA0D00659F0F /* StringFixer.h */, - 83E5FC3A1FFEFA0D00659F0F /* mptUUID.h */, - 83E5FC3B1FFEFA0D00659F0F /* BuildSettings.h */, - 83E5FC3C1FFEFA0D00659F0F /* WriteMemoryDump.h */, - 83E5FC3D1FFEFA0D00659F0F /* mptCPU.cpp */, - 83E5FC3E1FFEFA0D00659F0F /* mptTime.h */, - 83E5FC3F1FFEFA0D00659F0F /* FileReaderFwd.h */, - 83E5FC401FFEFA0D00659F0F /* Logging.h */, - 83E5FC411FFEFA0D00659F0F /* ComponentManager.cpp */, - 83E5FC421FFEFA0D00659F0F /* mptWine.h */, - 83E5FC431FFEFA0D00659F0F /* misc_util.h */, - 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */, - 83E5FC451FFEFA0D00659F0F /* mptTypeTraits.h */, - 83E5FC461FFEFA0D00659F0F /* serialization_utils.cpp */, - 83E5FC471FFEFA0D00659F0F /* mptFileIO.h */, - 83E5FC481FFEFA0D00659F0F /* mptStringFormat.cpp */, 83E5FC491FFEFA0D00659F0F /* FileReader.cpp */, - 83E5FC4A1FFEFA0D00659F0F /* serialization_utils.h */, - 83E5FC4B1FFEFA0D00659F0F /* mptWine.cpp */, - 83E5FC4C1FFEFA0D00659F0F /* mptThread.h */, - 83E5FC4D1FFEFA0D00659F0F /* mptPathString.cpp */, - 83E5FC4E1FFEFA0D00659F0F /* mptUUID.cpp */, - 83E5FC4F1FFEFA0D00659F0F /* mptTime.cpp */, - 83E5FC501FFEFA0D00659F0F /* mptString.cpp */, - 83E5FC511FFEFA0D00659F0F /* FlagSet.h */, - 83E5FC521FFEFA0D00659F0F /* mptFileIO.cpp */, - 83E5FC531FFEFA0D00659F0F /* mptString.h */, - 83E5FC541FFEFA0D00659F0F /* mptStringParse.h */, - 83E5FC551FFEFA0D00659F0F /* mptRandom.h */, - 83E5FC561FFEFA0D00659F0F /* CompilerDetect.h */, - 83E5FC571FFEFA0D00659F0F /* stdafx.cpp */, 83E5FC581FFEFA0D00659F0F /* FileReader.h */, - 83E5FC591FFEFA0D00659F0F /* mptPathString.h */, - 83E5FC5A1FFEFA0D00659F0F /* Profiler.h */, - 83E5FC5B1FFEFA0D00659F0F /* mptRandom.cpp */, - 83E5FC5C1FFEFA0D00659F0F /* mptOS.h */, - 83E5FC5D1FFEFA0D00659F0F /* mptIO.cpp */, - 83E5FC5E1FFEFA0D00659F0F /* mptCPU.h */, - 83E5FC5F1FFEFA0D00659F0F /* mptBufferIO.h */, - 83E5FC601FFEFA0D00659F0F /* versionNumber.h */, + 83E5FC3F1FFEFA0D00659F0F /* FileReaderFwd.h */, + 83E5FC511FFEFA0D00659F0F /* FlagSet.h */, + 83E5FC341FFEFA0D00659F0F /* Logging.cpp */, + 83E5FC401FFEFA0D00659F0F /* Logging.h */, 83E5FC611FFEFA0D00659F0F /* misc_util.cpp */, + 83E5FC431FFEFA0D00659F0F /* misc_util.h */, + 831132CA21F955B0001F678F /* mptAlloc.cpp */, + 831132CD21F955B1001F678F /* mptAlloc.h */, + 831132D421F955B2001F678F /* mptAssert.h */, + 831132CB21F955B0001F678F /* mptBaseMacros.h */, + 831132CE21F955B1001F678F /* mptBaseTypes.h */, + 831132D121F955B1001F678F /* mptBaseUtils.h */, + 83E5FC5F1FFEFA0D00659F0F /* mptBufferIO.h */, + 83E5FC3D1FFEFA0D00659F0F /* mptCPU.cpp */, + 83E5FC5E1FFEFA0D00659F0F /* mptCPU.h */, + 83E5FC2A1FFEFA0D00659F0F /* mptCRC.h */, + 831132D221F955B2001F678F /* mptException.h */, + 831132D321F955B2001F678F /* mptExceptionText.h */, + 83E5FC521FFEFA0D00659F0F /* mptFileIO.cpp */, + 83E5FC471FFEFA0D00659F0F /* mptFileIO.h */, + 83E5FC5D1FFEFA0D00659F0F /* mptIO.cpp */, + 83E5FC2C1FFEFA0D00659F0F /* mptIO.h */, + 83E5FC321FFEFA0D00659F0F /* mptLibrary.cpp */, + 83E5FC2B1FFEFA0D00659F0F /* mptLibrary.h */, + 831132C921F955B0001F678F /* mptMemory.h */, + 83E5FC371FFEFA0D00659F0F /* mptMutex.h */, + 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */, + 83E5FC5C1FFEFA0D00659F0F /* mptOS.h */, + 83E5FC4D1FFEFA0D00659F0F /* mptPathString.cpp */, + 83E5FC591FFEFA0D00659F0F /* mptPathString.h */, + 83E5FC5B1FFEFA0D00659F0F /* mptRandom.cpp */, + 83E5FC551FFEFA0D00659F0F /* mptRandom.h */, + 831132CF21F955B1001F678F /* mptSpan.h */, + 83E5FC501FFEFA0D00659F0F /* mptString.cpp */, + 83E5FC531FFEFA0D00659F0F /* mptString.h */, + 831132CC21F955B1001F678F /* mptStringBuffer.cpp */, + 831132D021F955B1001F678F /* mptStringBuffer.h */, + 83E5FC481FFEFA0D00659F0F /* mptStringFormat.cpp */, + 83E5FC331FFEFA0D00659F0F /* mptStringFormat.h */, + 83E5FC2E1FFEFA0D00659F0F /* mptStringParse.cpp */, + 83E5FC541FFEFA0D00659F0F /* mptStringParse.h */, + 83E5FC4C1FFEFA0D00659F0F /* mptThread.h */, + 83E5FC4F1FFEFA0D00659F0F /* mptTime.cpp */, + 83E5FC3E1FFEFA0D00659F0F /* mptTime.h */, + 83E5FC4E1FFEFA0D00659F0F /* mptUUID.cpp */, + 83E5FC3A1FFEFA0D00659F0F /* mptUUID.h */, + 83E5FC4B1FFEFA0D00659F0F /* mptWine.cpp */, + 83E5FC421FFEFA0D00659F0F /* mptWine.h */, + 83E5FC361FFEFA0D00659F0F /* Profiler.cpp */, + 83E5FC5A1FFEFA0D00659F0F /* Profiler.h */, + 83E5FC461FFEFA0D00659F0F /* serialization_utils.cpp */, + 83E5FC4A1FFEFA0D00659F0F /* serialization_utils.h */, + 83E5FC2F1FFEFA0D00659F0F /* stdafx.h */, + 83E5FC381FFEFA0D00659F0F /* version.cpp */, + 83E5FC2D1FFEFA0D00659F0F /* version.h */, + 83E5FC601FFEFA0D00659F0F /* versionNumber.h */, ); path = common; sourceTree = ""; @@ -734,141 +757,146 @@ 83E5FD0E1FFEFA8400659F0F /* soundlib */ = { isa = PBXGroup; children = ( - 83E5FD0F1FFEFA8400659F0F /* WAVTools.cpp */, - 83E5FD101FFEFA8400659F0F /* ITTools.cpp */, 83E5FD111FFEFA8400659F0F /* AudioCriticalSection.cpp */, - 83E5FD121FFEFA8400659F0F /* MixerInterface.h */, - 83E5FD131FFEFA8400659F0F /* Load_stm.cpp */, - 83E5FD141FFEFA8400659F0F /* MixerLoops.cpp */, - 83E5FD151FFEFA8400659F0F /* Load_dbm.cpp */, - 83E5FD161FFEFA8400659F0F /* ModChannel.cpp */, - 83E5FD171FFEFA8400659F0F /* Load_gdm.cpp */, - 83E5FD181FFEFA8400659F0F /* SoundFilePlayConfig.h */, - 83E5FD191FFEFA8400659F0F /* Snd_fx.cpp */, - 83E5FD1A1FFEFA8400659F0F /* ModSample.h */, - 83E5FD1B1FFEFA8400659F0F /* MIDIEvents.h */, - 83E5FD1C1FFEFA8400659F0F /* Load_mid.cpp */, - 83E5FD1D1FFEFA8400659F0F /* ModSampleCopy.h */, - 83E5FD1E1FFEFA8400659F0F /* mod_specifications.cpp */, - 83E5FD1F1FFEFA8400659F0F /* patternContainer.h */, - 83E5FD201FFEFA8400659F0F /* Snd_flt.cpp */, - 83E5FD211FFEFA8400659F0F /* ChunkReader.h */, - 83E5FD221FFEFA8400659F0F /* ITCompression.h */, - 83E5FD231FFEFA8400659F0F /* Load_psm.cpp */, - 83E5FD241FFEFA8400659F0F /* Dither.h */, - 83E5FD251FFEFA8400659F0F /* S3MTools.h */, - 83E5FD261FFEFA8400659F0F /* Load_far.cpp */, - 83E5FD271FFEFA8400659F0F /* patternContainer.cpp */, - 83E5FD281FFEFA8400659F0F /* Load_med.cpp */, - 83E5FD291FFEFA8400659F0F /* Load_dmf.cpp */, - 83E5FD2A1FFEFA8400659F0F /* MPEGFrame.h */, - 83E5FD2B1FFEFA8400659F0F /* Paula.cpp */, - 83E5FD2C1FFEFA8400659F0F /* WAVTools.h */, - 83E5FD2D1FFEFA8400659F0F /* mod_specifications.h */, - 83E5FD2E1FFEFA8400659F0F /* modcommand.cpp */, - 83E5FD2F1FFEFA8400659F0F /* Message.cpp */, - 83E5FD301FFEFA8400659F0F /* ITTools.h */, - 83E5FD311FFEFA8400659F0F /* SoundFilePlayConfig.cpp */, - 83E5FD321FFEFA8400659F0F /* RowVisitor.h */, - 83E5FD331FFEFA8400659F0F /* Load_uax.cpp */, - 83E5FD341FFEFA8400659F0F /* plugins */, - 83E5FD561FFEFA8400659F0F /* Load_ams.cpp */, - 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */, - 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */, - 83E5FD591FFEFA8400659F0F /* Load_ptm.cpp */, - 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */, - 83E5FD5B1FFEFA8400659F0F /* Container.h */, - 83E5FD5C1FFEFA8400659F0F /* ModSequence.h */, - 83E5FD5D1FFEFA8400659F0F /* ContainerXPK.cpp */, - 83E5FD5E1FFEFA8400659F0F /* SampleFormatMP3.cpp */, - 83E5FD5F1FFEFA8400659F0F /* tuning.cpp */, - 83E5FD601FFEFA8400659F0F /* Sndfile.cpp */, - 83E5FD611FFEFA8400659F0F /* ContainerMMCMP.cpp */, - 83E5FD621FFEFA8400659F0F /* Load_amf.cpp */, - 83E5FD631FFEFA8400659F0F /* Load_669.cpp */, - 83E5FD641FFEFA8400659F0F /* UMXTools.h */, - 83E5FD651FFEFA8400659F0F /* modsmp_ctrl.cpp */, - 83E5FD661FFEFA8400659F0F /* Load_mtm.cpp */, - 83E5FD671FFEFA8400659F0F /* Message.h */, - 83E5FD681FFEFA8400659F0F /* OggStream.cpp */, - 83E5FD691FFEFA8400659F0F /* Load_plm.cpp */, - 83E5FD6A1FFEFA8400659F0F /* modcommand.h */, - 83E5FD6B1FFEFA8400659F0F /* Tables.cpp */, - 83E5FD6C1FFEFA8400659F0F /* XMTools.h */, - 83E5FD6D1FFEFA8400659F0F /* Load_mod.cpp */, - 83E5FD6E1FFEFA8400659F0F /* Load_sfx.cpp */, - 83E5FD6F1FFEFA8400659F0F /* Sndmix.cpp */, - 83E5FD701FFEFA8400659F0F /* load_j2b.cpp */, - 83E5FD711FFEFA8400659F0F /* ModSequence.cpp */, - 83E5FD721FFEFA8400659F0F /* Snd_defs.h */, - 83E5FD731FFEFA8400659F0F /* MixFuncTable.h */, - 83E5FD741FFEFA8400659F0F /* pattern.h */, - 83E5FD751FFEFA8400659F0F /* SampleFormatFLAC.cpp */, - 83E5FD761FFEFA8400659F0F /* modsmp_ctrl.h */, - 83E5FD771FFEFA8400659F0F /* ModInstrument.cpp */, - 83E5FD781FFEFA8400659F0F /* Load_mo3.cpp */, - 83E5FD791FFEFA8400659F0F /* ModSample.cpp */, - 83E5FD7A1FFEFA8400659F0F /* Dlsbank.cpp */, - 83E5FD7B1FFEFA8400659F0F /* Tagging.h */, - 83E5FD7C1FFEFA8400659F0F /* tuningcollection.h */, - 83E5FD7D1FFEFA8400659F0F /* Mixer.h */, - 83E5FD7E1FFEFA8400659F0F /* FloatMixer.h */, - 83E5FD7F1FFEFA8400659F0F /* Load_itp.cpp */, 83E5FD801FFEFA8400659F0F /* AudioCriticalSection.h */, - 83E5FD811FFEFA8400659F0F /* Tables.h */, - 83E5FD821FFEFA8400659F0F /* UpgradeModule.cpp */, - 83E5FD831FFEFA8400659F0F /* tuningbase.h */, - 83E5FD841FFEFA8400659F0F /* MIDIMacros.cpp */, - 83E5FD851FFEFA8400659F0F /* WindowedFIR.h */, - 83E5FD861FFEFA8400659F0F /* Sndfile.h */, - 83E5FD871FFEFA8400659F0F /* Paula.h */, - 83E5FD881FFEFA8400659F0F /* ContainerPP20.cpp */, - 83E5FD891FFEFA8400659F0F /* RowVisitor.cpp */, - 83E5FD8A1FFEFA8400659F0F /* Load_imf.cpp */, - 83E5FD8B1FFEFA8400659F0F /* SampleFormatVorbis.cpp */, - 83E5FD8C1FFEFA8400659F0F /* Load_dsm.cpp */, - 83E5FD8D1FFEFA8400659F0F /* ModInstrument.h */, - 83E5FD8E1FFEFA8400659F0F /* Load_mt2.cpp */, - 83E5FD8F1FFEFA8400659F0F /* MixerSettings.cpp */, - 83E5FD901FFEFA8400659F0F /* S3MTools.cpp */, - 83E5FD911FFEFA8400659F0F /* Dlsbank.h */, - 83E5FD921FFEFA8400659F0F /* Load_xm.cpp */, - 83E5FD931FFEFA8400659F0F /* IntMixer.h */, - 83E5FD941FFEFA8400659F0F /* MIDIEvents.cpp */, - 83E5FD951FFEFA8400659F0F /* pattern.cpp */, - 83E5FD961FFEFA8400659F0F /* Load_digi.cpp */, - 83E5FD971FFEFA8400659F0F /* Load_s3m.cpp */, - 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */, - 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */, - 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */, - 83E5FD9B1FFEFA8400659F0F /* Resampler.h */, - 83E5FD9C1FFEFA8400659F0F /* ModChannel.h */, - 83E5FD9D1FFEFA8400659F0F /* MixerSettings.h */, 83E5FD9E1FFEFA8400659F0F /* AudioReadTarget.h */, - 83E5FD9F1FFEFA8400659F0F /* Load_mdl.cpp */, - 83E5FDA01FFEFA8400659F0F /* WindowedFIR.cpp */, - 83E5FDA11FFEFA8400659F0F /* SampleFormats.cpp */, - 83E5FDA21FFEFA8400659F0F /* Load_wav.cpp */, - 83E5FDA31FFEFA8400659F0F /* MixerLoops.h */, - 83E5FDA41FFEFA8400659F0F /* Load_it.cpp */, - 83E5FDA51FFEFA8400659F0F /* tuning.h */, - 83E5FDA61FFEFA8400659F0F /* UMXTools.cpp */, - 83E5FDA71FFEFA8400659F0F /* Load_stp.cpp */, - 83E5FDA81FFEFA8400659F0F /* Load_okt.cpp */, - 83E5FDA91FFEFA8400659F0F /* Load_ult.cpp */, - 83E5FDAA1FFEFA8400659F0F /* MIDIMacros.h */, - 83E5FDAB1FFEFA8400659F0F /* MixFuncTable.cpp */, - 83E5FDAC1FFEFA8400659F0F /* SampleFormatOpus.cpp */, - 83E5FDAD1FFEFA8400659F0F /* OggStream.h */, + 831132E321F9565E001F678F /* BitReader.h */, + 83E5FD211FFEFA8400659F0F /* ChunkReader.h */, + 83E5FD5B1FFEFA8400659F0F /* Container.h */, + 83E5FD611FFEFA8400659F0F /* ContainerMMCMP.cpp */, + 83E5FD881FFEFA8400659F0F /* ContainerPP20.cpp */, + 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */, + 83E5FD5D1FFEFA8400659F0F /* ContainerXPK.cpp */, + 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */, + 83E5FD241FFEFA8400659F0F /* Dither.h */, + 83E5FD7A1FFEFA8400659F0F /* Dlsbank.cpp */, + 83E5FD911FFEFA8400659F0F /* Dlsbank.h */, 83E5FDAE1FFEFA8400659F0F /* Fastmix.cpp */, - 83E5FDAF1FFEFA8400659F0F /* Loaders.h */, - 83E5FDB01FFEFA8400659F0F /* Tagging.cpp */, - 83E5FDB11FFEFA8400659F0F /* ITCompression.cpp */, - 83E5FDB21FFEFA8400659F0F /* Load_dtm.cpp */, - 83E5FDB31FFEFA8400659F0F /* MPEGFrame.cpp */, - 83E5FDB41FFEFA8400659F0F /* XMTools.cpp */, - 83E5FDB51FFEFA8400659F0F /* SampleFormatMediaFoundation.cpp */, + 83E5FD7E1FFEFA8400659F0F /* FloatMixer.h */, 83E5FDB61FFEFA8400659F0F /* InstrumentExtensions.cpp */, + 83E5FD931FFEFA8400659F0F /* IntMixer.h */, + 83E5FDB11FFEFA8400659F0F /* ITCompression.cpp */, + 83E5FD221FFEFA8400659F0F /* ITCompression.h */, + 83E5FD101FFEFA8400659F0F /* ITTools.cpp */, + 83E5FD301FFEFA8400659F0F /* ITTools.h */, + 83E5FD631FFEFA8400659F0F /* Load_669.cpp */, + 83E5FD621FFEFA8400659F0F /* Load_amf.cpp */, + 83E5FD561FFEFA8400659F0F /* Load_ams.cpp */, + 831132E621F9565F001F678F /* Load_c67.cpp */, + 83E5FD151FFEFA8400659F0F /* Load_dbm.cpp */, + 83E5FD961FFEFA8400659F0F /* Load_digi.cpp */, + 83E5FD291FFEFA8400659F0F /* Load_dmf.cpp */, + 83E5FD8C1FFEFA8400659F0F /* Load_dsm.cpp */, + 83E5FDB21FFEFA8400659F0F /* Load_dtm.cpp */, + 83E5FD261FFEFA8400659F0F /* Load_far.cpp */, + 83E5FD171FFEFA8400659F0F /* Load_gdm.cpp */, + 83E5FD8A1FFEFA8400659F0F /* Load_imf.cpp */, + 83E5FDA41FFEFA8400659F0F /* Load_it.cpp */, + 83E5FD7F1FFEFA8400659F0F /* Load_itp.cpp */, + 83E5FD701FFEFA8400659F0F /* load_j2b.cpp */, + 83E5FD9F1FFEFA8400659F0F /* Load_mdl.cpp */, + 83E5FD281FFEFA8400659F0F /* Load_med.cpp */, + 83E5FD1C1FFEFA8400659F0F /* Load_mid.cpp */, + 83E5FD781FFEFA8400659F0F /* Load_mo3.cpp */, + 83E5FD6D1FFEFA8400659F0F /* Load_mod.cpp */, + 83E5FD8E1FFEFA8400659F0F /* Load_mt2.cpp */, + 83E5FD661FFEFA8400659F0F /* Load_mtm.cpp */, + 83E5FDA81FFEFA8400659F0F /* Load_okt.cpp */, + 83E5FD691FFEFA8400659F0F /* Load_plm.cpp */, + 83E5FD231FFEFA8400659F0F /* Load_psm.cpp */, + 83E5FD591FFEFA8400659F0F /* Load_ptm.cpp */, + 83E5FD971FFEFA8400659F0F /* Load_s3m.cpp */, + 83E5FD6E1FFEFA8400659F0F /* Load_sfx.cpp */, + 83E5FD131FFEFA8400659F0F /* Load_stm.cpp */, + 83E5FDA71FFEFA8400659F0F /* Load_stp.cpp */, + 83E5FD331FFEFA8400659F0F /* Load_uax.cpp */, + 83E5FDA91FFEFA8400659F0F /* Load_ult.cpp */, + 83E5FDA21FFEFA8400659F0F /* Load_wav.cpp */, + 83E5FD921FFEFA8400659F0F /* Load_xm.cpp */, + 83E5FDAF1FFEFA8400659F0F /* Loaders.h */, + 83E5FD2F1FFEFA8400659F0F /* Message.cpp */, + 83E5FD671FFEFA8400659F0F /* Message.h */, + 83E5FD941FFEFA8400659F0F /* MIDIEvents.cpp */, + 83E5FD1B1FFEFA8400659F0F /* MIDIEvents.h */, + 83E5FD841FFEFA8400659F0F /* MIDIMacros.cpp */, + 83E5FDAA1FFEFA8400659F0F /* MIDIMacros.h */, + 83E5FD7D1FFEFA8400659F0F /* Mixer.h */, + 83E5FD121FFEFA8400659F0F /* MixerInterface.h */, + 83E5FD141FFEFA8400659F0F /* MixerLoops.cpp */, + 83E5FDA31FFEFA8400659F0F /* MixerLoops.h */, + 83E5FD8F1FFEFA8400659F0F /* MixerSettings.cpp */, + 83E5FD9D1FFEFA8400659F0F /* MixerSettings.h */, + 83E5FDAB1FFEFA8400659F0F /* MixFuncTable.cpp */, + 83E5FD731FFEFA8400659F0F /* MixFuncTable.h */, + 83E5FD1E1FFEFA8400659F0F /* mod_specifications.cpp */, + 83E5FD2D1FFEFA8400659F0F /* mod_specifications.h */, + 83E5FD161FFEFA8400659F0F /* ModChannel.cpp */, + 83E5FD9C1FFEFA8400659F0F /* ModChannel.h */, + 83E5FD2E1FFEFA8400659F0F /* modcommand.cpp */, + 83E5FD6A1FFEFA8400659F0F /* modcommand.h */, + 83E5FD771FFEFA8400659F0F /* ModInstrument.cpp */, + 83E5FD8D1FFEFA8400659F0F /* ModInstrument.h */, + 83E5FD791FFEFA8400659F0F /* ModSample.cpp */, + 83E5FD1A1FFEFA8400659F0F /* ModSample.h */, + 83E5FD1D1FFEFA8400659F0F /* ModSampleCopy.h */, + 83E5FD711FFEFA8400659F0F /* ModSequence.cpp */, + 83E5FD5C1FFEFA8400659F0F /* ModSequence.h */, + 83E5FD651FFEFA8400659F0F /* modsmp_ctrl.cpp */, + 83E5FD761FFEFA8400659F0F /* modsmp_ctrl.h */, + 83E5FDB31FFEFA8400659F0F /* MPEGFrame.cpp */, + 83E5FD2A1FFEFA8400659F0F /* MPEGFrame.h */, + 83E5FD681FFEFA8400659F0F /* OggStream.cpp */, + 83E5FDAD1FFEFA8400659F0F /* OggStream.h */, + 831132E421F9565F001F678F /* opal.h */, + 831132E721F9565F001F678F /* OPL.cpp */, + 831132E521F9565F001F678F /* OPL.h */, + 83E5FD951FFEFA8400659F0F /* pattern.cpp */, + 83E5FD741FFEFA8400659F0F /* pattern.h */, + 83E5FD271FFEFA8400659F0F /* patternContainer.cpp */, + 83E5FD1F1FFEFA8400659F0F /* patternContainer.h */, + 83E5FD2B1FFEFA8400659F0F /* Paula.cpp */, + 83E5FD871FFEFA8400659F0F /* Paula.h */, + 83E5FD341FFEFA8400659F0F /* plugins */, + 83E5FD9B1FFEFA8400659F0F /* Resampler.h */, + 83E5FD891FFEFA8400659F0F /* RowVisitor.cpp */, + 83E5FD321FFEFA8400659F0F /* RowVisitor.h */, + 83E5FD901FFEFA8400659F0F /* S3MTools.cpp */, + 83E5FD251FFEFA8400659F0F /* S3MTools.h */, + 83E5FD751FFEFA8400659F0F /* SampleFormatFLAC.cpp */, + 83E5FDB51FFEFA8400659F0F /* SampleFormatMediaFoundation.cpp */, + 83E5FD5E1FFEFA8400659F0F /* SampleFormatMP3.cpp */, + 83E5FDAC1FFEFA8400659F0F /* SampleFormatOpus.cpp */, + 83E5FDA11FFEFA8400659F0F /* SampleFormats.cpp */, + 83E5FD8B1FFEFA8400659F0F /* SampleFormatVorbis.cpp */, + 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */, + 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */, + 83E5FD721FFEFA8400659F0F /* Snd_defs.h */, + 83E5FD201FFEFA8400659F0F /* Snd_flt.cpp */, + 83E5FD191FFEFA8400659F0F /* Snd_fx.cpp */, + 83E5FD601FFEFA8400659F0F /* Sndfile.cpp */, + 83E5FD861FFEFA8400659F0F /* Sndfile.h */, + 83E5FD6F1FFEFA8400659F0F /* Sndmix.cpp */, + 83E5FD311FFEFA8400659F0F /* SoundFilePlayConfig.cpp */, + 83E5FD181FFEFA8400659F0F /* SoundFilePlayConfig.h */, + 83E5FD6B1FFEFA8400659F0F /* Tables.cpp */, + 83E5FD811FFEFA8400659F0F /* Tables.h */, + 83E5FDB01FFEFA8400659F0F /* Tagging.cpp */, + 83E5FD7B1FFEFA8400659F0F /* Tagging.h */, + 83E5FD5F1FFEFA8400659F0F /* tuning.cpp */, + 83E5FDA51FFEFA8400659F0F /* tuning.h */, + 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */, + 83E5FD831FFEFA8400659F0F /* tuningbase.h */, + 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */, + 83E5FD7C1FFEFA8400659F0F /* tuningcollection.h */, + 83E5FDA61FFEFA8400659F0F /* UMXTools.cpp */, + 83E5FD641FFEFA8400659F0F /* UMXTools.h */, + 83E5FD821FFEFA8400659F0F /* UpgradeModule.cpp */, + 83E5FD0F1FFEFA8400659F0F /* WAVTools.cpp */, + 83E5FD2C1FFEFA8400659F0F /* WAVTools.h */, + 83E5FDA01FFEFA8400659F0F /* WindowedFIR.cpp */, + 83E5FD851FFEFA8400659F0F /* WindowedFIR.h */, + 83E5FDB41FFEFA8400659F0F /* XMTools.cpp */, + 83E5FD6C1FFEFA8400659F0F /* XMTools.h */, ); path = soundlib; sourceTree = ""; @@ -884,7 +912,6 @@ 83E5FD3A1FFEFA8400659F0F /* PlugInterface.cpp */, 83E5FD3B1FFEFA8400659F0F /* LFOPlugin.cpp */, 83E5FD3C1FFEFA8400659F0F /* OpCodes.h */, - 83E5FD3D1FFEFA8400659F0F /* PluginEventQueue.h */, 83E5FD3E1FFEFA8400659F0F /* PluginManager.cpp */, 83E5FD3F1FFEFA8400659F0F /* DigiBoosterEcho.cpp */, 83E5FD401FFEFA8400659F0F /* dmo */, @@ -950,9 +977,8 @@ 83E5FCCD1FFEFA1A00659F0F /* libopenmpt.hpp in Headers */, 83E5FCD81FFEFA1A00659F0F /* libopenmpt_config.h in Headers */, 83E5FCF01FFEFA1A00659F0F /* libopenmpt_version.h in Headers */, - 83E5FC751FFEFA0D00659F0F /* WriteMemoryDump.h in Headers */, 83E5FDC51FFEFA8500659F0F /* ModSampleCopy.h in Headers */, - 83E5FC7E1FFEFA0D00659F0F /* mptTypeTraits.h in Headers */, + 831132DF21F955B2001F678F /* mptExceptionText.h in Headers */, 83E5FE611FFEFEA600659F0F /* svn_version.template.subwcrev.h in Headers */, 83E5FE2C1FFEFA8500659F0F /* Sndfile.h in Headers */, 83E5FE191FFEFA8500659F0F /* MixFuncTable.h in Headers */, @@ -983,6 +1009,7 @@ 83E5FC651FFEFA0D00659F0F /* mptIO.h in Headers */, 83E5FE1A1FFEFA8500659F0F /* pattern.h in Headers */, 83E5FC8F1FFEFA0D00659F0F /* CompilerDetect.h in Headers */, + 831132E021F955B2001F678F /* mptAssert.h in Headers */, 83E5FE211FFEFA8500659F0F /* Tagging.h in Headers */, 83E5FE101FFEFA8500659F0F /* modcommand.h in Headers */, 83E5FE331FFEFA8500659F0F /* ModInstrument.h in Headers */, @@ -991,8 +1018,8 @@ 83E5FE2B1FFEFA8500659F0F /* WindowedFIR.h in Headers */, 83E5FCFB1FFEFA7400659F0F /* SampleFormatCopy.h in Headers */, 83E5FE001FFEFA8500659F0F /* SampleIO.h in Headers */, - 83E5FC721FFEFA0D00659F0F /* StringFixer.h in Headers */, 83E5FE441FFEFA8500659F0F /* AudioReadTarget.h in Headers */, + 831132EA21F9565F001F678F /* OPL.h in Headers */, 83E5FC8E1FFEFA0D00659F0F /* mptRandom.h in Headers */, 83E5FE491FFEFA8500659F0F /* MixerLoops.h in Headers */, 83E5FE411FFEFA8500659F0F /* Resampler.h in Headers */, @@ -1006,21 +1033,25 @@ 83E5FE221FFEFA8500659F0F /* tuningcollection.h in Headers */, 83E5FE121FFEFA8500659F0F /* XMTools.h in Headers */, 83E5FCCB1FFEFA1A00659F0F /* libopenmpt_impl.hpp in Headers */, + 831132E821F9565F001F678F /* BitReader.h in Headers */, 83E5FC981FFEFA0D00659F0F /* mptBufferIO.h in Headers */, 83E5FC741FFEFA0D00659F0F /* BuildSettings.h in Headers */, + 831132D521F955B2001F678F /* mptMemory.h in Headers */, + 831132DE21F955B2001F678F /* mptException.h in Headers */, 83E5FDC01FFEFA8500659F0F /* SoundFilePlayConfig.h in Headers */, 83E5FCCF1FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp in Headers */, 83E5FE4B1FFEFA8500659F0F /* tuning.h in Headers */, 83E5FDFA1FFEFA8500659F0F /* Flanger.h in Headers */, 83E5FC921FFEFA0D00659F0F /* mptPathString.h in Headers */, - 83E5FC6E1FFEFA0D00659F0F /* typedefs.h in Headers */, 83E5FC931FFEFA0D00659F0F /* Profiler.h in Headers */, 83E5FDE31FFEFA8500659F0F /* OpCodes.h in Headers */, 83E5FCFA1FFEFA7400659F0F /* SampleFormat.h in Headers */, + 831132DB21F955B2001F678F /* mptSpan.h in Headers */, 83E5FE2D1FFEFA8500659F0F /* Paula.h in Headers */, 83E5FDDD1FFEFA8500659F0F /* PluginStructs.h in Headers */, 83E5FDDE1FFEFA8500659F0F /* LFOPlugin.h in Headers */, 83E5FDC71FFEFA8500659F0F /* patternContainer.h in Headers */, + 831132DC21F955B2001F678F /* mptStringBuffer.h in Headers */, 83E5FC791FFEFA0D00659F0F /* Logging.h in Headers */, 83E5FC971FFEFA0D00659F0F /* mptCPU.h in Headers */, 83E5FC951FFEFA0D00659F0F /* mptOS.h in Headers */, @@ -1032,7 +1063,6 @@ 83E5FC641FFEFA0D00659F0F /* mptLibrary.h in Headers */, 83E5FCDD1FFEFA1A00659F0F /* libopenmpt_ext.h in Headers */, 83E5FDC91FFEFA8500659F0F /* ChunkReader.h in Headers */, - 83E5FDE41FFEFA8500659F0F /* PluginEventQueue.h in Headers */, 83E5FDF91FFEFA8500659F0F /* Distortion.h in Headers */, 83E5FE261FFEFA8500659F0F /* AudioCriticalSection.h in Headers */, 83E5FCD01FFEFA1A00659F0F /* libopenmpt_internal.h in Headers */, @@ -1047,27 +1077,31 @@ 83E5FC6A1FFEFA0D00659F0F /* Endianness.h in Headers */, 83E5FCED1FFEFA1A00659F0F /* libopenmpt_ext.hpp in Headers */, 83E5FE0D1FFEFA8500659F0F /* Message.h in Headers */, + 831132D721F955B2001F678F /* mptBaseMacros.h in Headers */, 83E5FCFC1FFEFA7400659F0F /* SampleFormatConverters.h in Headers */, 83E5FE421FFEFA8500659F0F /* ModChannel.h in Headers */, 83E5FC851FFEFA0D00659F0F /* mptThread.h in Headers */, 83E5FE021FFEFA8500659F0F /* ModSequence.h in Headers */, 83E5FC7C1FFEFA0D00659F0F /* misc_util.h in Headers */, 83E5FE431FFEFA8500659F0F /* MixerSettings.h in Headers */, + 831132D921F955B2001F678F /* mptAlloc.h in Headers */, 83E5FE271FFEFA8500659F0F /* Tables.h in Headers */, 83E5FE551FFEFA8500659F0F /* Loaders.h in Headers */, 83E5FE631FFEFEA600659F0F /* svn_version.h in Headers */, 83E5FC801FFEFA0D00659F0F /* mptFileIO.h in Headers */, + 831132E921F9565F001F678F /* opal.h in Headers */, 83E5EFD01FFEF9D200659F0F /* config.h in Headers */, 83E5FE011FFEFA8500659F0F /* Container.h in Headers */, - 83E5F8841FFEF9E400659F0F /* libc.h in Headers */, 83E5FE391FFEFA8500659F0F /* IntMixer.h in Headers */, 83E5FDF01FFEFA8500659F0F /* WavesReverb.h in Headers */, 83E5FDED1FFEFA8500659F0F /* I3DL2Reverb.h in Headers */, 83E5FC701FFEFA0D00659F0F /* mptMutex.h in Headers */, 83E5FD0C1FFEFA7D00659F0F /* DSP.h in Headers */, 83E5FC631FFEFA0D00659F0F /* mptCRC.h in Headers */, + 831132DD21F955B2001F678F /* mptBaseUtils.h in Headers */, 83E5FDC21FFEFA8500659F0F /* ModSample.h in Headers */, 83E5FC7B1FFEFA0D00659F0F /* mptWine.h in Headers */, + 831132DA21F955B2001F678F /* mptBaseTypes.h in Headers */, 83E5FE231FFEFA8500659F0F /* Mixer.h in Headers */, 83E5FDDA1FFEFA8500659F0F /* RowVisitor.h in Headers */, 83E5FCF51FFEFA1A00659F0F /* libopenmpt_stream_callbacks_buffer.h in Headers */, @@ -1133,7 +1167,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 83E5F8831FFEF9E400659F0F /* LGPL.txt in Resources */, + 831132E221F955F4001F678F /* LICENSE in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1160,6 +1194,7 @@ 83E5FDBC1FFEFA8500659F0F /* MixerLoops.cpp in Sources */, 83E5FC7D1FFEFA0D00659F0F /* mptOS.cpp in Sources */, 83E5FDE11FFEFA8500659F0F /* PlugInterface.cpp in Sources */, + 831132EB21F9565F001F678F /* Load_c67.cpp in Sources */, 83E5FC871FFEFA0D00659F0F /* mptUUID.cpp in Sources */, 83E5FE4A1FFEFA8500659F0F /* Load_it.cpp in Sources */, 83E5FE3D1FFEFA8500659F0F /* Load_s3m.cpp in Sources */, @@ -1202,6 +1237,7 @@ 83E5FE4D1FFEFA8500659F0F /* Load_stp.cpp in Sources */, 83E5FE0E1FFEFA8500659F0F /* OggStream.cpp in Sources */, 83E5FE451FFEFA8500659F0F /* Load_mdl.cpp in Sources */, + 831132EC21F9565F001F678F /* OPL.cpp in Sources */, 83E5FDBB1FFEFA8500659F0F /* Load_stm.cpp in Sources */, 83E5FCD21FFEFA1A00659F0F /* libopenmpt_modplug.c in Sources */, 83E5FC841FFEFA0D00659F0F /* mptWine.cpp in Sources */, @@ -1211,6 +1247,8 @@ 83E5FE351FFEFA8500659F0F /* MixerSettings.cpp in Sources */, 83E5FC891FFEFA0D00659F0F /* mptString.cpp in Sources */, 83E5FDC61FFEFA8500659F0F /* mod_specifications.cpp in Sources */, + 831132D621F955B2001F678F /* mptAlloc.cpp in Sources */, + 831132D821F955B2001F678F /* mptStringBuffer.cpp in Sources */, 83E5FDD61FFEFA8500659F0F /* modcommand.cpp in Sources */, 83E5FD091FFEFA7D00659F0F /* DSP.cpp in Sources */, 83E5FE081FFEFA8500659F0F /* Load_amf.cpp in Sources */, @@ -1239,7 +1277,6 @@ 83E5FE591FFEFA8500659F0F /* MPEGFrame.cpp in Sources */, 83E5FE341FFEFA8500659F0F /* Load_mt2.cpp in Sources */, 83E5FC941FFEFA0D00659F0F /* mptRandom.cpp in Sources */, - 83E5FC901FFEFA0D00659F0F /* stdafx.cpp in Sources */, 83E5FDD31FFEFA8500659F0F /* Paula.cpp in Sources */, 83E5FE251FFEFA8500659F0F /* Load_itp.cpp in Sources */, 83E5FE401FFEFA8500659F0F /* Dither.cpp in Sources */, @@ -1266,7 +1303,6 @@ 83E5FDFE1FFEFA8500659F0F /* ContainerUMX.cpp in Sources */, 83E5FC671FFEFA0D00659F0F /* mptStringParse.cpp in Sources */, 83E5FDE81FFEFA8500659F0F /* Flanger.cpp in Sources */, - 83E5FC621FFEFA0D00659F0F /* typedefs.cpp in Sources */, 83E5FE4E1FFEFA8500659F0F /* Load_okt.cpp in Sources */, 83E5FE521FFEFA8500659F0F /* SampleFormatOpus.cpp in Sources */, 83E5FC7F1FFEFA0D00659F0F /* serialization_utils.cpp in Sources */, diff --git a/Plugins/OpenMPT/OpenMPT/OMPTContainer.mm b/Plugins/OpenMPT/OpenMPT/OMPTContainer.mm index 3e53b9b3d..b2c0c3f17 100755 --- a/Plugins/OpenMPT/OpenMPT/OMPTContainer.mm +++ b/Plugins/OpenMPT/OpenMPT/OMPTContainer.mm @@ -63,14 +63,12 @@ int i; int subsongs = mod->get_num_subsongs(); - for (i = 0; i < subsongs; ++i) { - mod->select_subsong(i); - if (mod->get_duration_seconds() < 10.0) continue; + delete mod; + + for (i = 0; i < subsongs; ++i) { [tracks addObject:[NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"#%i", i]]]; } - - delete mod; - + return tracks; } catch ( std::exception & /*e*/ ) { return 0;