From dab78ed74fb2568957725e41c913e80a93700adb Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Mon, 21 Sep 2020 21:54:24 -0700 Subject: [PATCH] Updated libopenmpt to version 0.5.2 --- Frameworks/OpenMPT/OpenMPT/LICENSE | 2 +- Frameworks/OpenMPT/OpenMPT/Makefile | 254 ++- Frameworks/OpenMPT/OpenMPT/README.md | 63 +- .../OpenMPT/build/android_ndk/Android.mk | 8 +- .../OpenMPT/build/android_ndk/Application.mk | 2 +- .../build/android_ndk/README.AndroidNDK.txt | 2 +- Frameworks/OpenMPT/OpenMPT/build/dist.mk | 6 +- .../OpenMPT/OpenMPT/build/make/config-afl.mk | 8 - .../OpenMPT/build/make/config-clang.mk | 21 +- .../OpenMPT/build/make/config-defaults.mk | 3 - .../OpenMPT/build/make/config-djgpp.mk | 14 +- .../OpenMPT/build/make/config-emscripten.mk | 71 +- .../OpenMPT/OpenMPT/build/make/config-gcc.mk | 26 +- .../OpenMPT/build/make/config-generic.mk | 2 +- .../build/make/config-mingw64-win32.mk | 2 +- .../build/make/config-mingw64-win64.mk | 2 +- .../build/make/config-mingw64-winrt-amd64.mk | 2 +- .../build/make/config-mingw64-winrt-x86.mk | 2 +- .../OpenMPT/build/make/config-standard.mk | 2 +- .../OpenMPT/build/svn_version/svn_version.h | 8 +- .../OpenMPT/OpenMPT/common/BuildSettings.h | 161 +- .../OpenMPT/OpenMPT/common/CompilerDetect.h | 100 +- .../OpenMPT/common/ComponentManager.cpp | 6 +- .../OpenMPT/OpenMPT/common/ComponentManager.h | 4 +- .../OpenMPT/OpenMPT/common/Endianness.h | 696 +++--- .../OpenMPT/OpenMPT/common/FileReader.h | 1276 ++++++----- .../OpenMPT/OpenMPT/common/FileReaderFwd.h | 14 +- Frameworks/OpenMPT/OpenMPT/common/FlagSet.h | 27 +- Frameworks/OpenMPT/OpenMPT/common/Logging.cpp | 47 +- Frameworks/OpenMPT/OpenMPT/common/Logging.h | 31 +- .../OpenMPT/OpenMPT/common/Profiler.cpp | 2 +- Frameworks/OpenMPT/OpenMPT/common/Profiler.h | 2 +- .../OpenMPT/OpenMPT/common/misc_util.cpp | 12 +- Frameworks/OpenMPT/OpenMPT/common/misc_util.h | 2 +- .../OpenMPT/OpenMPT/common/mptAlloc.cpp | 93 - Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h | 181 +- Frameworks/OpenMPT/OpenMPT/common/mptAssert.h | 22 +- .../OpenMPT/OpenMPT/common/mptBaseMacros.h | 198 +- .../OpenMPT/OpenMPT/common/mptBaseTypes.h | 191 +- .../OpenMPT/OpenMPT/common/mptBaseUtils.h | 426 ++-- .../OpenMPT/OpenMPT/common/mptBufferIO.h | 177 -- Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp | 207 +- Frameworks/OpenMPT/OpenMPT/common/mptCPU.h | 26 +- Frameworks/OpenMPT/OpenMPT/common/mptCRC.h | 10 +- .../OpenMPT/OpenMPT/common/mptException.h | 14 +- .../OpenMPT/OpenMPT/common/mptExceptionText.h | 6 +- .../OpenMPT/OpenMPT/common/mptFileIO.cpp | 276 +-- Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h | 66 +- Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp | 409 ++-- Frameworks/OpenMPT/OpenMPT/common/mptIO.h | 197 +- .../OpenMPT/OpenMPT/common/mptLibrary.cpp | 26 +- .../OpenMPT/OpenMPT/common/mptLibrary.h | 2 +- Frameworks/OpenMPT/OpenMPT/common/mptMemory.h | 160 +- Frameworks/OpenMPT/OpenMPT/common/mptMutex.h | 4 +- Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp | 103 +- Frameworks/OpenMPT/OpenMPT/common/mptOS.h | 40 +- .../OpenMPT/OpenMPT/common/mptOSError.h | 99 + .../OpenMPT/OpenMPT/common/mptOSException.h | 188 ++ .../OpenMPT/OpenMPT/common/mptPathString.cpp | 10 +- .../OpenMPT/OpenMPT/common/mptPathString.h | 60 +- .../OpenMPT/OpenMPT/common/mptRandom.cpp | 109 +- Frameworks/OpenMPT/OpenMPT/common/mptRandom.h | 251 +- Frameworks/OpenMPT/OpenMPT/common/mptSpan.h | 64 +- .../OpenMPT/OpenMPT/common/mptString.cpp | 868 +++---- Frameworks/OpenMPT/OpenMPT/common/mptString.h | 174 +- .../OpenMPT/OpenMPT/common/mptStringBuffer.h | 366 +-- .../OpenMPT/common/mptStringFormat.cpp | 239 +- .../OpenMPT/OpenMPT/common/mptStringFormat.h | 463 ++-- .../OpenMPT/OpenMPT/common/mptStringParse.h | 16 +- Frameworks/OpenMPT/OpenMPT/common/mptThread.h | 12 + Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp | 4 +- Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp | 14 +- Frameworks/OpenMPT/OpenMPT/common/mptUUID.h | 21 +- Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp | 103 +- .../OpenMPT/common/serialization_utils.cpp | 50 +- .../OpenMPT/common/serialization_utils.h | 6 +- Frameworks/OpenMPT/OpenMPT/common/stdafx.h | 24 +- Frameworks/OpenMPT/OpenMPT/common/version.cpp | 66 +- Frameworks/OpenMPT/OpenMPT/common/version.h | 85 +- .../OpenMPT/OpenMPT/common/versionNumber.h | 14 +- .../OpenMPT/contrib/fuzzing/fuzz-master.sh | 5 +- .../OpenMPT/contrib/fuzzing/get-afl.sh | 17 +- .../contrib/libmodplug-0.8.8.5/LICENSE | 26 + .../contrib/libmodplug-0.8.8.5/Makefile.am | 46 + .../contrib/libmodplug-0.8.8.5/autogen.sh | 2 + .../contrib/libmodplug-0.8.8.5/clean.sh | 7 + .../contrib/libmodplug-0.8.8.5/config.h.in | 81 + .../contrib/libmodplug-0.8.8.5/configure.ac | 66 + .../contrib/libmodplug-0.8.8.5/dist.sh | 9 + .../libmodplug-0.8.8.5}/libmodplug.pc.in | 2 +- .../libmodplug-0.8.8.5}/libmodplug/modplug.h | 0 .../libmodplug-0.8.8.5}/libmodplug/sndfile.h | 0 .../libmodplug-0.8.8.5}/libmodplug/stdafx.h | 0 .../libmodplug-0.8.8.5}/libopenmpt_modplug.c | 2 +- .../libopenmpt_modplug.pc.in | 12 + .../libopenmpt_modplug_cpp.cpp | 2 +- .../m4/ax_cxx_compile_stdcxx.m4 | 972 ++++++++ .../contrib/libmodplug-0.8.8.5/test.sh | 10 + .../contrib/libmodplug-0.8.9.0/LICENSE | 26 + .../contrib/libmodplug-0.8.9.0/Makefile.am | 46 + .../contrib/libmodplug-0.8.9.0/autogen.sh | 2 + .../contrib/libmodplug-0.8.9.0/clean.sh | 7 + .../contrib/libmodplug-0.8.9.0/config.h.in | 81 + .../contrib/libmodplug-0.8.9.0/configure.ac | 66 + .../contrib/libmodplug-0.8.9.0/dist.sh | 9 + .../libmodplug-0.8.9.0/libmodplug.pc.in | 13 + .../libmodplug-0.8.9.0/libmodplug/modplug.h | 185 ++ .../libmodplug-0.8.9.0/libmodplug/sndfile.h | 1028 +++++++++ .../libmodplug-0.8.9.0/libmodplug/stdafx.h | 141 ++ .../libmodplug-0.8.9.0/libopenmpt_modplug.c | 604 +++++ .../libopenmpt_modplug.pc.in | 12 + .../libopenmpt_modplug_cpp.cpp | 887 +++++++ .../m4/ax_cxx_compile_stdcxx.m4 | 972 ++++++++ .../contrib/libmodplug-0.8.9.0/test.sh | 10 + .../OpenMPT/contrib/toolchain-djgpp/build.sh | 43 + .../OpenMPT/contrib/toolchain-djgpp/setenv.sh | 1 + .../OpenMPT/OpenMPT/doc/contributing.md | 4 +- .../OpenMPT/OpenMPT/doc/module_formats.md | 15 +- .../OpenMPT/examples/libopenmpt_example_c.c | 10 +- .../examples/libopenmpt_example_c_mem.c | 11 +- .../examples/libopenmpt_example_cxx.cpp | 45 +- .../OpenMPT/include/minimp3/OpenMPT.txt | 2 +- .../OpenMPT/OpenMPT/include/minimp3/minimp3.h | 99 +- .../OpenMPT/include/stb_vorbis/OpenMPT.txt | 7 +- .../OpenMPT/include/stb_vorbis/stb_vorbis.c | 456 ++-- .../OpenMPT/OpenMPT/libopenmpt/.clang-format | 45 + .../bindings/freebasic/libopenmpt.bi | 20 +- .../OpenMPT/libopenmpt/doc/foo_openmpt.txt | 9 - .../OpenMPT/libopenmpt/dox/changelog.md | 280 ++- .../OpenMPT/libopenmpt/dox/dependencies.md | 38 +- .../OpenMPT/libopenmpt/dox/quickstart.md | 14 +- .../OpenMPT/libopenmpt/foo_openmpt.cpp | 323 --- .../OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp | 22 +- .../OpenMPT/OpenMPT/libopenmpt/libopenmpt.h | 127 +- .../OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp | 209 +- .../OpenMPT/libopenmpt/libopenmpt_c.cpp | 391 ++-- .../OpenMPT/libopenmpt/libopenmpt_config.h | 25 +- .../OpenMPT/libopenmpt/libopenmpt_cxx.cpp | 66 + .../OpenMPT/libopenmpt/libopenmpt_ext.hpp | 4 + .../libopenmpt/libopenmpt_ext_impl.cpp | 6 + .../libopenmpt/libopenmpt_ext_impl.hpp | 2 + .../OpenMPT/libopenmpt/libopenmpt_impl.cpp | 854 +++++-- .../OpenMPT/libopenmpt/libopenmpt_impl.hpp | 45 +- .../libopenmpt/libopenmpt_plugin_gui.cpp | 43 +- .../libopenmpt/libopenmpt_plugin_gui.rc | 24 +- .../libopenmpt/libopenmpt_plugin_settings.hpp | 10 +- .../OpenMPT/libopenmpt/libopenmpt_version.h | 4 +- .../OpenMPT/libopenmpt/libopenmpt_version.mk | 10 +- .../OpenMPT/libopenmpt/libopenmpt_version.rc | 2 +- .../OpenMPT/OpenMPT/libopenmpt/resource.h | 1 + .../OpenMPT/libopenmpt/xmp-openmpt.cpp | 76 +- .../OpenMPT/OpenMPT/openmpt123/.clang-format | 45 + .../OpenMPT/OpenMPT/openmpt123/openmpt123.cpp | 263 +-- .../OpenMPT/OpenMPT/openmpt123/openmpt123.hpp | 1072 +++++++-- .../OpenMPT/openmpt123/openmpt123_config.hpp | 46 +- .../OpenMPT/openmpt123/openmpt123_flac.hpp | 7 + .../OpenMPT/openmpt123/openmpt123_mmio.hpp | 4 +- .../openmpt123/openmpt123_portaudio.hpp | 12 +- .../OpenMPT/openmpt123/openmpt123_sdl.hpp | 133 -- .../OpenMPT/openmpt123/openmpt123_sdl2.hpp | 91 +- .../OpenMPT/openmpt123/openmpt123_waveout.hpp | 2 +- .../OpenMPT/OpenMPT/soundbase/Dither.cpp | 34 + Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h | 330 +++ .../OpenMPT/OpenMPT/soundbase/SampleBuffer.h | 135 ++ .../OpenMPT/OpenMPT/soundbase/SampleFormat.h | 122 +- .../soundbase/SampleFormatConverters.h | 586 +++-- .../OpenMPT/soundbase/SampleFormatCopy.h | 116 +- .../OpenMPT/OpenMPT/soundbase/SampleTypes.h | 59 + Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp | 63 +- Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h | 20 + Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp | 137 +- .../OpenMPT/OpenMPT/sounddsp/Reverb.cpp | 337 +-- Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h | 103 +- .../OpenMPT/soundlib/AudioCriticalSection.cpp | 2 +- .../OpenMPT/soundlib/AudioCriticalSection.h | 5 +- .../OpenMPT/soundlib/AudioReadTarget.h | 252 +- .../OpenMPT/OpenMPT/soundlib/BitReader.h | 4 +- .../OpenMPT/soundlib/ContainerMMCMP.cpp | 27 +- .../OpenMPT/soundlib/ContainerPP20.cpp | 91 +- .../OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp | 2 +- .../OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp | 24 +- .../OpenMPT/OpenMPT/soundlib/Dither.cpp | 274 --- Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h | 92 - .../OpenMPT/OpenMPT/soundlib/Dlsbank.cpp | 995 ++++---- Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h | 17 +- .../OpenMPT/OpenMPT/soundlib/Fastmix.cpp | 90 +- .../OpenMPT/soundlib/ITCompression.cpp | 117 +- .../OpenMPT/OpenMPT/soundlib/ITCompression.h | 61 +- .../OpenMPT/OpenMPT/soundlib/ITTools.cpp | 46 +- Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h | 12 +- .../OpenMPT/soundlib/InstrumentExtensions.cpp | 67 +- .../OpenMPT/OpenMPT/soundlib/IntMixer.h | 12 +- .../OpenMPT/OpenMPT/soundlib/Load_669.cpp | 27 +- .../OpenMPT/OpenMPT/soundlib/Load_amf.cpp | 34 +- .../OpenMPT/OpenMPT/soundlib/Load_ams.cpp | 30 +- .../OpenMPT/OpenMPT/soundlib/Load_c67.cpp | 12 +- .../OpenMPT/OpenMPT/soundlib/Load_dbm.cpp | 63 +- .../OpenMPT/OpenMPT/soundlib/Load_digi.cpp | 4 +- .../OpenMPT/OpenMPT/soundlib/Load_dmf.cpp | 28 +- .../OpenMPT/OpenMPT/soundlib/Load_dsm.cpp | 17 +- .../OpenMPT/OpenMPT/soundlib/Load_dtm.cpp | 67 +- .../OpenMPT/OpenMPT/soundlib/Load_far.cpp | 27 +- .../OpenMPT/OpenMPT/soundlib/Load_gdm.cpp | 72 +- .../OpenMPT/OpenMPT/soundlib/Load_imf.cpp | 45 +- .../OpenMPT/OpenMPT/soundlib/Load_it.cpp | 236 +- .../OpenMPT/OpenMPT/soundlib/Load_itp.cpp | 14 +- .../OpenMPT/OpenMPT/soundlib/Load_mdl.cpp | 52 +- .../OpenMPT/OpenMPT/soundlib/Load_med.cpp | 2030 ++++++++++------- .../OpenMPT/OpenMPT/soundlib/Load_mid.cpp | 76 +- .../OpenMPT/OpenMPT/soundlib/Load_mo3.cpp | 385 ++-- .../OpenMPT/OpenMPT/soundlib/Load_mod.cpp | 144 +- .../OpenMPT/OpenMPT/soundlib/Load_mt2.cpp | 46 +- .../OpenMPT/OpenMPT/soundlib/Load_mtm.cpp | 18 +- .../OpenMPT/OpenMPT/soundlib/Load_okt.cpp | 31 +- .../OpenMPT/OpenMPT/soundlib/Load_plm.cpp | 43 +- .../OpenMPT/OpenMPT/soundlib/Load_psm.cpp | 124 +- .../OpenMPT/OpenMPT/soundlib/Load_ptm.cpp | 24 +- .../OpenMPT/OpenMPT/soundlib/Load_s3m.cpp | 212 +- .../OpenMPT/OpenMPT/soundlib/Load_sfx.cpp | 17 +- .../OpenMPT/OpenMPT/soundlib/Load_stm.cpp | 255 ++- .../OpenMPT/OpenMPT/soundlib/Load_stp.cpp | 32 +- .../OpenMPT/OpenMPT/soundlib/Load_uax.cpp | 4 +- .../OpenMPT/OpenMPT/soundlib/Load_ult.cpp | 53 +- .../OpenMPT/OpenMPT/soundlib/Load_wav.cpp | 8 +- .../OpenMPT/OpenMPT/soundlib/Load_xm.cpp | 402 +++- Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h | 4 +- .../OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp | 8 + .../OpenMPT/OpenMPT/soundlib/MIDIMacros.h | 9 +- .../OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp | 10 +- .../OpenMPT/OpenMPT/soundlib/Message.cpp | 8 +- Frameworks/OpenMPT/OpenMPT/soundlib/Message.h | 6 +- .../OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp | 43 +- .../OpenMPT/OpenMPT/soundlib/MixFuncTable.h | 30 +- Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h | 35 +- .../OpenMPT/OpenMPT/soundlib/MixerLoops.cpp | 405 +--- .../OpenMPT/OpenMPT/soundlib/MixerLoops.h | 8 +- .../OpenMPT/soundlib/MixerSettings.cpp | 2 +- .../OpenMPT/OpenMPT/soundlib/ModChannel.cpp | 38 +- .../OpenMPT/OpenMPT/soundlib/ModChannel.h | 105 +- .../OpenMPT/soundlib/ModInstrument.cpp | 67 +- .../OpenMPT/OpenMPT/soundlib/ModInstrument.h | 101 +- .../OpenMPT/OpenMPT/soundlib/ModSample.cpp | 162 +- .../OpenMPT/OpenMPT/soundlib/ModSample.h | 14 +- .../OpenMPT/OpenMPT/soundlib/ModSampleCopy.h | 18 +- .../OpenMPT/OpenMPT/soundlib/ModSequence.cpp | 158 +- .../OpenMPT/OpenMPT/soundlib/ModSequence.h | 43 +- Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp | 51 +- Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h | 12 +- .../OpenMPT/OpenMPT/soundlib/OggStream.cpp | 49 +- .../OpenMPT/OpenMPT/soundlib/OggStream.h | 3 + Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp | 522 +++-- Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h | 49 +- .../OpenMPT/OpenMPT/soundlib/Resampler.h | 21 +- .../OpenMPT/OpenMPT/soundlib/RowVisitor.cpp | 12 +- .../OpenMPT/OpenMPT/soundlib/RowVisitor.h | 6 +- .../OpenMPT/OpenMPT/soundlib/S3MTools.cpp | 12 +- .../OpenMPT/OpenMPT/soundlib/S3MTools.h | 135 +- .../OpenMPT/soundlib/SampleFormatBRR.cpp | 134 ++ .../OpenMPT/soundlib/SampleFormatFLAC.cpp | 37 +- .../OpenMPT/soundlib/SampleFormatMP3.cpp | 28 +- .../soundlib/SampleFormatMediaFoundation.cpp | 4 +- .../OpenMPT/soundlib/SampleFormatOpus.cpp | 13 +- .../OpenMPT/soundlib/SampleFormatSFZ.cpp | 1252 ++++++++++ .../OpenMPT/soundlib/SampleFormatVorbis.cpp | 33 +- .../OpenMPT/soundlib/SampleFormats.cpp | 1029 +-------- .../OpenMPT/OpenMPT/soundlib/SampleIO.cpp | 10 +- .../OpenMPT/OpenMPT/soundlib/SampleIO.h | 2 +- .../OpenMPT/OpenMPT/soundlib/Snd_defs.h | 162 +- .../OpenMPT/OpenMPT/soundlib/Snd_flt.cpp | 5 +- .../OpenMPT/OpenMPT/soundlib/Snd_fx.cpp | 678 +++--- .../OpenMPT/OpenMPT/soundlib/Sndfile.cpp | 144 +- Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h | 141 +- .../OpenMPT/OpenMPT/soundlib/Sndmix.cpp | 242 +- .../OpenMPT/OpenMPT/soundlib/Tables.cpp | 76 +- Frameworks/OpenMPT/OpenMPT/soundlib/Tables.h | 4 +- .../OpenMPT/OpenMPT/soundlib/TinyFFT.cpp | 154 ++ Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h | 42 + .../OpenMPT/soundlib/UpgradeModule.cpp | 303 +-- .../OpenMPT/OpenMPT/soundlib/WAVTools.cpp | 37 +- .../OpenMPT/OpenMPT/soundlib/WAVTools.h | 6 +- .../OpenMPT/OpenMPT/soundlib/XMTools.cpp | 37 +- .../OpenMPT/OpenMPT/soundlib/load_j2b.cpp | 91 +- .../OpenMPT/soundlib/mod_specifications.cpp | 38 +- .../OpenMPT/OpenMPT/soundlib/modcommand.cpp | 64 +- .../OpenMPT/OpenMPT/soundlib/modcommand.h | 157 +- .../OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp | 644 +----- .../OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h | 65 +- Frameworks/OpenMPT/OpenMPT/soundlib/opal.h | 53 +- .../OpenMPT/OpenMPT/soundlib/pattern.cpp | 4 +- .../soundlib/plugins/DigiBoosterEcho.cpp | 6 +- .../soundlib/plugins/DigiBoosterEcho.h | 4 +- .../OpenMPT/soundlib/plugins/LFOPlugin.cpp | 4 +- .../OpenMPT/soundlib/plugins/LFOPlugin.h | 4 +- .../OpenMPT/soundlib/plugins/OpCodes.h | 2 +- .../soundlib/plugins/PlugInterface.cpp | 57 +- .../OpenMPT/soundlib/plugins/PlugInterface.h | 10 +- .../soundlib/plugins/PluginManager.cpp | 226 +- .../OpenMPT/soundlib/plugins/PluginManager.h | 34 +- .../soundlib/plugins/PluginMixBuffer.h | 78 +- .../OpenMPT/soundlib/plugins/PluginStructs.h | 10 +- .../OpenMPT/soundlib/plugins/dmo/Chorus.cpp | 4 +- .../soundlib/plugins/dmo/Compressor.cpp | 6 +- .../OpenMPT/soundlib/plugins/dmo/Compressor.h | 4 +- .../soundlib/plugins/dmo/DMOPlugin.cpp | 33 +- .../OpenMPT/soundlib/plugins/dmo/DMOPlugin.h | 6 +- .../soundlib/plugins/dmo/Distortion.cpp | 2 +- .../OpenMPT/soundlib/plugins/dmo/Distortion.h | 2 +- .../OpenMPT/soundlib/plugins/dmo/Echo.cpp | 2 +- .../OpenMPT/soundlib/plugins/dmo/Echo.h | 2 +- .../OpenMPT/soundlib/plugins/dmo/Flanger.h | 2 +- .../OpenMPT/soundlib/plugins/dmo/Gargle.h | 2 +- .../soundlib/plugins/dmo/I3DL2Reverb.cpp | 68 +- .../soundlib/plugins/dmo/I3DL2Reverb.h | 18 +- .../OpenMPT/soundlib/plugins/dmo/ParamEq.h | 2 +- .../soundlib/plugins/dmo/WavesReverb.h | 2 +- .../OpenMPT/OpenMPT/soundlib/tuning.cpp | 518 +++-- Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h | 221 +- .../OpenMPT/soundlib/tuningCollection.cpp | 93 +- .../OpenMPT/OpenMPT/soundlib/tuningbase.cpp | 160 -- .../OpenMPT/OpenMPT/soundlib/tuningbase.h | 52 +- .../OpenMPT/soundlib/tuningcollection.h | 26 +- .../OpenMPT/OpenMPT/test/TestToolsLib.cpp | 4 +- .../OpenMPT/OpenMPT/test/TestToolsLib.h | 2 +- .../OpenMPT/OpenMPT/test/TestToolsTracker.h | 2 +- Frameworks/OpenMPT/OpenMPT/test/test.cpp | 941 ++++---- .../libOpenMPT.xcodeproj/project.pbxproj | 66 +- .../OpenMPT/OpenMPT.xcodeproj/project.pbxproj | 4 +- 327 files changed, 23363 insertions(+), 15401 deletions(-) delete mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptOSError.h create mode 100644 Frameworks/OpenMPT/OpenMPT/common/mptOSException.h create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh rename Frameworks/OpenMPT/OpenMPT/{include/modplug/include/libmodplug => contrib/libmodplug-0.8.8.5}/libmodplug.pc.in (91%) rename Frameworks/OpenMPT/OpenMPT/{include/modplug/include => contrib/libmodplug-0.8.8.5}/libmodplug/modplug.h (100%) rename Frameworks/OpenMPT/OpenMPT/{include/modplug/include => contrib/libmodplug-0.8.8.5}/libmodplug/sndfile.h (100%) rename Frameworks/OpenMPT/OpenMPT/{include/modplug/include => contrib/libmodplug-0.8.8.5}/libmodplug/stdafx.h (100%) rename Frameworks/OpenMPT/OpenMPT/{libopenmpt => contrib/libmodplug-0.8.8.5}/libopenmpt_modplug.c (99%) create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in rename Frameworks/OpenMPT/OpenMPT/{libopenmpt => contrib/libmodplug-0.8.8.5}/libopenmpt_modplug_cpp.cpp (99%) create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh create mode 100755 Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh create mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/.clang-format delete mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt delete mode 100644 Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/.clang-format delete mode 100644 Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundbase/Dither.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundbase/SampleBuffer.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundbase/SampleTypes.h delete mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp delete mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatBRR.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.cpp create mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h delete mode 100644 Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/LICENSE b/Frameworks/OpenMPT/OpenMPT/LICENSE index 834603bf0..e81fc3ff8 100644 --- a/Frameworks/OpenMPT/OpenMPT/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2019, OpenMPT contributors +Copyright (c) 2004-2020, OpenMPT contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/Makefile b/Frameworks/OpenMPT/OpenMPT/Makefile index 54d039d50..9d77ab7cd 100644 --- a/Frameworks/OpenMPT/OpenMPT/Makefile +++ b/Frameworks/OpenMPT/OpenMPT/Makefile @@ -54,7 +54,7 @@ # 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) +# STDCXX=c++14 C++ standard version (only for GCC and clang) # CHECKED=0 Enable run-time assertions. # CHECKED_ADDRESS=0 Enable address sanitizer # CHECKED_UNDEFINED=0 Enable undefined behaviour sanitizer @@ -69,6 +69,11 @@ # NO_VORBIS=1 Avoid using libvorbis, even if found # NO_VORBISFILE=1 Avoid using libvorbisfile, even if found # +# LOCAL_ZLIB=1 Build local copy of zlib, even if found +# LOCAL_MPG123=1 Build local copy of libmpg123, even if found +# LOCAL_OGG=1 Build local copy of libogg, even if found +# LOCAL_VORBIS=1 Build local copy of libvorbis, even if found +# # NO_MINIMP3=1 Do not fallback to minimp3 # NO_STBVORBIS=1 Do not fallback to stb_vorbis # @@ -86,7 +91,6 @@ # (defaults are 0): # # NO_PULSEAUDIO=1 Avoid using PulseAudio, even if found -# NO_SDL=1 Avoid using SDL, even if found # NO_SDL2=1 Avoid using SDL2, even if found # NO_FLAC=1 Avoid using FLAC, even if found # NO_SNDFILE=1 Avoid using libsndfile, even if found @@ -158,6 +162,7 @@ TEST=1 ONLY_TEST=0 SOSUFFIX=.so SOSUFFIXWINDOWS=0 +NO_SHARED_LINKER_FLAG=0 OPENMPT123=1 MODERN=0 STRICT=0 @@ -290,7 +295,7 @@ INSTALL_LIB = $(INSTALL) -m 0644 INSTALL_DATA_DIR = $(INSTALL_DIR) INSTALL_MAKE_DIR += -m 0755 -CPPFLAGS += -Icommon -I. -Iinclude/modplug/include -Iinclude +CPPFLAGS += -Icommon -I. -Iinclude ifeq ($(MPT_COMPILER_GENERIC),1) @@ -407,6 +412,30 @@ ifeq ($(HACK_ARCHIVE_SUPPORT),1) NO_ZLIB:=1 endif +ifeq ($(LOCAL_ZLIB),1) +CPPFLAGS_ZLIB := -DMPT_WITH_ZLIB +LDFLAGS_ZLIB := +LDLIBS_ZLIB := +CPPFLAGS_ZLIB += -Iinclude/zlib/ +LOCAL_ZLIB_SOURCES := +LOCAL_ZLIB_SOURCES += include/zlib/adler32.c +LOCAL_ZLIB_SOURCES += include/zlib/compress.c +LOCAL_ZLIB_SOURCES += include/zlib/crc32.c +LOCAL_ZLIB_SOURCES += include/zlib/deflate.c +LOCAL_ZLIB_SOURCES += include/zlib/gzclose.c +LOCAL_ZLIB_SOURCES += include/zlib/gzlib.c +LOCAL_ZLIB_SOURCES += include/zlib/gzread.c +LOCAL_ZLIB_SOURCES += include/zlib/gzwrite.c +LOCAL_ZLIB_SOURCES += include/zlib/infback.c +LOCAL_ZLIB_SOURCES += include/zlib/inffast.c +LOCAL_ZLIB_SOURCES += include/zlib/inflate.c +LOCAL_ZLIB_SOURCES += include/zlib/inftrees.c +LOCAL_ZLIB_SOURCES += include/zlib/trees.c +LOCAL_ZLIB_SOURCES += include/zlib/uncompr.c +LOCAL_ZLIB_SOURCES += include/zlib/zutil.c +include/zlib/%.o : CFLAGS+=$(CFLAGS_SILENT) -DSTDC -DZ_HAVE_UNISTD_H +include/zlib/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DSTDC -DZ_HAVE_UNISTD_H +else ifeq ($(NO_ZLIB),1) else #LDLIBS += -lz @@ -424,7 +453,44 @@ endif NO_ZLIB:=1 endif endif +endif +ifeq ($(LOCAL_MPG123),1) +CPPFLAGS_MPG123 := -DMPT_WITH_MPG123 +LDFLAGS_MPG123 := +LDLIBS_MPG123 := +CPPFLAGS_MPG123 += -Iinclude/mpg123/src/libmpg123/ -Iinclude/mpg123/src/compat/ -Iinclude/mpg123/src/ -Iinclude/mpg123/ports/makefile/ +LOCAL_MPG123_SOURCES := +LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat.c +LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat_str.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/dct64.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/equalizer.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/feature.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/format.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/frame.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/icy.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/icy2utf8.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/id3.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/index.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer1.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer2.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer3.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/libmpg123.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/ntom.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/optimize.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/parse.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/readers.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/stringbuf.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_8bit.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_real.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_s32.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/tabinit.c +include/mpg123/src/compat/%.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/compat/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/libmpg123/%.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/libmpg123/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +else ifeq ($(NO_MPG123),1) else #LDLIBS += -lmpg123 @@ -442,7 +508,19 @@ endif NO_MPG123:=1 endif endif +endif +ifeq ($(LOCAL_OGG),1) +CPPFLAGS_OGG := -DMPT_WITH_OGG +LDFLAGS_OGG := +LDLIBS_OGG := +CPPFLAGS_OGG += -Iinclude/ogg/include/ -Iinclude/ogg/ports/makefile/ +LOCAL_OGG_SOURCES := +LOCAL_OGG_SOURCES += include/ogg/src/bitwise.c +LOCAL_OGG_SOURCES += include/ogg/src/framing.c +include/ogg/src/%.o : CFLAGS+=$(CFLAGS_SILENT) +include/ogg/src/%.test.o : CFLAGS+=$(CFLAGS_SILENT) +else ifeq ($(NO_OGG),1) else #LDLIBS += -logg @@ -460,7 +538,39 @@ endif NO_OGG:=1 endif endif +endif +ifeq ($(LOCAL_VORBIS),1) +CPPFLAGS_VORBIS := -DMPT_WITH_VORBIS +LDFLAGS_VORBIS := +LDLIBS_VORBIS := +CPPFLAGS_VORBIS += -Iinclude/vorbis/include/ -Iinclude/vorbis/lib/ -DHAVE_ALLOCA_H +LOCAL_VORBIS_SOURCES := +LOCAL_VORBIS_SOURCES += include/vorbis/lib/analysis.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/bitrate.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/block.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/codebook.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/envelope.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/floor0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/floor1.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/info.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lookup.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lpc.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lsp.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/mapping0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/mdct.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/psy.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/registry.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/res0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/sharedbook.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/smallft.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/synthesis.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/vorbisenc.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/vorbisfile.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/window.c +include/vorbis/lib/%.o : CFLAGS+=$(CFLAGS_SILENT) +include/vorbis/lib/%.test.o : CFLAGS+=$(CFLAGS_SILENT) +else ifeq ($(NO_VORBIS),1) else #LDLIBS += -lvorbis @@ -478,7 +588,13 @@ endif NO_VORBIS:=1 endif endif +endif +ifeq ($(LOCAL_VORBIS),1) +CPPFLAGS_VORBISFILE := -DMPT_WITH_VORBISFILE +LDFLAGS_VORBISFILE := +LDLIBS_VORBISFILE := +else ifeq ($(NO_VORBISFILE),1) else #LDLIBS += -lvorbisfile @@ -496,15 +612,15 @@ endif NO_VORBISFILE:=1 endif endif +endif ifeq ($(NO_SDL2),1) else #LDLIBS += -lsdl2 -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 +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists 'sdl2 >= 2.0.4' && echo yes),yes) +CPPFLAGS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I 'sdl2 >= 2.0.4' ) -DMPT_WITH_SDL2 +LDFLAGS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L 'sdl2 >= 2.0.4' ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other 'sdl2 >= 2.0.4' ) +LDLIBS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l 'sdl2 >= 2.0.4' ) else ifeq ($(FORCE_DEPS),1) $(error sdl2 not found) @@ -515,23 +631,6 @@ NO_SDL2:=1 endif endif -ifeq ($(NO_SDL),1) -else -#LDLIBS += -lsdl -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) -else -$(warning warning: sdl not found) -endif -NO_SDL:=1 -endif -endif - ifeq ($(NO_PORTAUDIO),1) else #LDLIBS += -lportaudio @@ -629,7 +728,7 @@ CPPFLAGS += -DMPT_BUILD_HACK_ARCHIVE_SUPPORT endif CPPCHECK_FLAGS += -j $(NUMTHREADS) -CPPCHECK_FLAGS += --std=c99 --std=c++11 +CPPCHECK_FLAGS += --std=c99 --std=c++14 CPPCHECK_FLAGS += --quiet CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]' CPPCHECK_FLAGS += --suppress=missingIncludeSystem @@ -639,9 +738,9 @@ CPPFLAGS += $(CPPFLAGS_ZLIB) $(CPPFLAGS_MPG123) $(CPPFLAGS_OGG) $(CPPFLAGS_VORBI 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) $(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) +CPPFLAGS_OPENMPT123 += $(CPPFLAGS_SDL2) $(CPPFLAGS_PORTAUDIO) $(CPPFLAGS_PULSEAUDIO) $(CPPFLAGS_FLAC) $(CPPFLAGS_SNDFILE) $(CPPFLAGS_ALLEGRO42) +LDFLAGS_OPENMPT123 += $(LDFLAGS_SDL2) $(LDFLAGS_PORTAUDIO) $(LDFLAGS_PULSEAUDIO) $(LDFLAGS_FLAC) $(LDFLAGS_SNDFILE) $(LDFLAGS_ALLEGRO42) +LDLIBS_OPENMPT123 += $(LDLIBS_SDL2) $(LDLIBS_PORTAUDIO) $(LDLIBS_PULSEAUDIO) $(LDLIBS_FLAC) $(LDLIBS_SNDFILE) $(LDLIBS_ALLEGRO42) %: %.o @@ -761,14 +860,23 @@ LIBOPENMPT_CXX_SOURCES += \ include/miniz/miniz.o : CFLAGS+=$(CFLAGS_SILENT) include/miniz/miniz.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_ZLIB),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_ZLIB_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_ZLIB_SOURCES) +else ifeq ($(NO_ZLIB),1) LIBOPENMPT_C_SOURCES += include/miniz/miniz.c LIBOPENMPTTEST_C_SOURCES += include/miniz/miniz.c CPPFLAGS += -DMPT_WITH_MINIZ endif +endif include/minimp3/minimp3.o : CFLAGS+=$(CFLAGS_SILENT) include/minimp3/minimp3.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_MPG123),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_MPG123_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_MPG123_SOURCES) +else ifeq ($(NO_MPG123),1) ifeq ($(NO_MINIMP3),1) else @@ -777,9 +885,18 @@ LIBOPENMPTTEST_C_SOURCES += include/minimp3/minimp3.c CPPFLAGS += -DMPT_WITH_MINIMP3 endif endif +endif include/stb_vorbis/stb_vorbis.o : CFLAGS+=$(CFLAGS_SILENT) include/stb_vorbis/stb_vorbis.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_VORBIS),1) +ifeq ($(LOCAL_OGG),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_OGG_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_OGG_SOURCES) +endif +LIBOPENMPT_C_SOURCES += $(LOCAL_VORBIS_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_VORBIS_SOURCES) +else ifeq ($(NO_OGG),1) ifeq ($(NO_STBVORBIS),1) else @@ -807,6 +924,7 @@ else endif endif endif +endif LIBOPENMPT_OBJECTS += $(LIBOPENMPT_CXX_SOURCES:.cpp=.o) $(LIBOPENMPT_C_SOURCES:.c=.o) LIBOPENMPT_DEPENDS = $(LIBOPENMPT_OBJECTS:.o=.d) @@ -820,18 +938,6 @@ OBJECTS_LIBOPENMPT += $(LIBOPENMPT_OBJECTS) endif -LIBOPENMPT_MODPLUG_C_SOURCES += \ - libopenmpt/libopenmpt_modplug.c \ - -LIBOPENMPT_MODPLUG_CPP_SOURCES += \ - libopenmpt/libopenmpt_modplug_cpp.cpp \ - -LIBOPENMPT_MODPLUG_OBJECTS = $(LIBOPENMPT_MODPLUG_C_SOURCES:.c=.o) $(LIBOPENMPT_MODPLUG_CPP_SOURCES:.cpp=.o) -LIBOPENMPT_MODPLUG_DEPENDS = $(LIBOPENMPT_MODPLUG_OBJECTS:.o=.d) -ALL_OBJECTS += $(LIBOPENMPT_MODPLUG_OBJECTS) -ALL_DEPENDS += $(LIBOPENMPT_MODPLUG_DEPENDS) - - OPENMPT123_CXX_SOURCES += \ $(sort $(wildcard openmpt123/*.cpp)) \ @@ -879,7 +985,6 @@ all: ifeq ($(DYNLINK),1) OUTPUTS += bin/libopenmpt$(SOSUFFIX) -OUTPUTS += bin/libopenmpt_modplug$(SOSUFFIX) endif ifeq ($(SHARED_LIB),1) OUTPUTS += bin/libopenmpt$(SOSUFFIX) @@ -937,22 +1042,30 @@ 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.wasm.js 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.wasm.js MISC_OUTPUTS += bin/libopenmpt.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_mem.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_mem.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_mem.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_probe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_probe.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_probe.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.js.mem MISC_OUTPUTS += bin/openmpt.a #old @@ -1095,24 +1208,6 @@ ifeq ($(MPT_WITH_DOXYGEN),1) $(INSTALL_DATA_DIR) bin/docs/html $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/html endif -.PHONY: install-openmpt-modplug -install-openmpt-modplug: $(OUTPUTS) -ifeq ($(SHARED_LIB),1) - $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib - $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt_modplug$(SOSUFFIX) - $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt_modplug$(SOSUFFIX).1 - $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt_modplug$(SOSUFFIX).1.0.0 -endif - -.PHONY: install-modplug -install-modplug: $(OUTPUTS) -ifeq ($(SHARED_LIB),1) - $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib - $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libmodplug$(SOSUFFIX) - $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libmodplug$(SOSUFFIX).1 - $(INSTALL_LIB) bin/libopenmpt_modplug$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libmodplug$(SOSUFFIX).1.0.0 -endif - .PHONY: dist dist: bin/dist-tar.tar bin/dist-zip.tar bin/dist-doc.tar @@ -1209,6 +1304,7 @@ bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin svn export ./LICENSE bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE svn export ./README.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md svn export ./Makefile bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/Makefile + svn export ./.clang-format bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/.clang-format svn export ./bin bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin svn export ./build/android_ndk bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/android_ndk svn export ./build/make bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/make @@ -1229,7 +1325,6 @@ bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin 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 svn export ./include/stb_vorbis bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/stb_vorbis cp bin/dist.mk bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/dist.mk cp bin/svn_version_dist.h bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version/svn_version.h @@ -1248,6 +1343,7 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn 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 ./.clang-format bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/.clang-format --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 @@ -1257,13 +1353,14 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn 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 + svn export ./build/vs2017win7 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win7 --native-eol CRLF + svn export ./build/vs2017win81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win81 --native-eol CRLF + svn export ./build/vs2017win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win10 --native-eol CRLF + svn export ./build/vs2017uwp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017uwp --native-eol CRLF + svn export ./build/vs2019win7 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win7 --native-eol CRLF + svn export ./build/vs2019win81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win81 --native-eol CRLF + svn export ./build/vs2019win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win10 --native-eol CRLF + svn export ./build/vs2019uwp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019uwp --native-eol CRLF svn export ./build/download_externals.cmd bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/download_externals.cmd --native-eol CRLF svn export ./common bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/common --native-eol CRLF svn export ./doc/contributing.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/contributing.md --native-eol CRLF @@ -1282,8 +1379,6 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn svn export ./include/mpg123 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/mpg123 --native-eol CRLF svn export ./include/flac bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/flac --native-eol CRLF svn export ./include/portaudio bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/portaudio --native-eol CRLF - svn export ./include/modplug bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/modplug --native-eol CRLF - svn export ./include/foobar2000sdk bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/foobar2000sdk --native-eol CRLF svn export ./include/ogg bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/ogg --native-eol CRLF svn export ./include/pugixml bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/pugixml --native-eol CRLF svn export ./include/stb_vorbis bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/stb_vorbis --native-eol CRLF @@ -1314,15 +1409,14 @@ bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: 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 mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all + cp bin/stage/all/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.js + cp bin/stage/all/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.js.mem + cp bin/stage/all/libopenmpt.wasm bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.wasm + cp bin/stage/all/libopenmpt.wasm.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.wasm.js 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 @@ -1361,16 +1455,16 @@ bin/libopenmpt.a: $(LIBOPENMPT_OBJECTS) bin/libopenmpt$(SOSUFFIX): $(LIBOPENMPT_OBJECTS) $(INFO) [LD] $@ +ifeq ($(NO_SHARED_LINKER_FLAG),1) + $(SILENT)$(LINK.cc) $(LIBOPENMPT_LDFLAGS) $(SO_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ +else $(SILENT)$(LINK.cc) -shared $(LIBOPENMPT_LDFLAGS) $(SO_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ +endif ifeq ($(SHARED_SONAME),1) $(SILENT)mv bin/libopenmpt$(SOSUFFIX) bin/$(LIBOPENMPT_SONAME) $(SILENT)ln -sf $(LIBOPENMPT_SONAME) bin/libopenmpt$(SOSUFFIX) endif -bin/libopenmpt_modplug$(SOSUFFIX): $(LIBOPENMPT_MODPLUG_OBJECTS) $(OUTPUT_LIBOPENMPT) - $(INFO) [LD] $@ - $(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] $@ $(SILENT)help2man --no-discard-stderr --no-info --version-option=--man-version --help-option=--man-help --include=openmpt123/openmpt123.h2m $< > $@ diff --git a/Frameworks/OpenMPT/OpenMPT/README.md b/Frameworks/OpenMPT/OpenMPT/README.md index a9d8d0a18..94c32e0fb 100644 --- a/Frameworks/OpenMPT/OpenMPT/README.md +++ b/Frameworks/OpenMPT/OpenMPT/README.md @@ -35,44 +35,19 @@ How to compile - Supported Visual Studio versions: - - Visual Studio 2015 Update 3 Community/Professional/Enterprise + - Visual Studio 2017 and 2019 Community/Professional/Enterprise - To compile the project, open `build/vs2015/OpenMPT.sln` and hit the - compile button. - - - Visual Studio 2017 Community/Professional/Enterprise - - To compile the project, open `build/vs2017/OpenMPT.sln` and hit the - compile button. + To compile the project, open `build/vsVERSIONwin7/OpenMPT.sln` (VERSION + being 2017 or 2019) and hit the compile button. Other target systems can + be found in the `vs2017*` and `vs2019*` sibling folders. - 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 Windows 8.1 SDK is required to build OpenMPT with Visual Studio 2017 + (this is included with Visual Studio, however may need to be selected + explicitly during setup). - - The ASIO SDK is needed for compiling with ASIO support. - - If you don't want this, comment out `#define MPT_WITH_ASIO` in the file - `common/BuildSettings.h`. - - 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: - - - Visit - [steinberg.net](https://www.steinberg.net/en/company/developers.html) to - download the SDK. - - - 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 need further help with the ASIO SDK, get in touch with the - main OpenMPT developers. + - Microsoft Foundation Classes (MFC) are required to build OpenMPT. ### libopenmpt and openmpt123 @@ -104,15 +79,13 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - 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/`. - Projects that target Windows 10 1709 Desktop (10.0.16299.0, including - ARM and ARM64) or later versions are available in - `build/vsVERSIONwin10/`. + - You will find solutions for Visual Studio 2017 and 2019 in the + corresponding `build/vsVERSIONwin7/` folder. + Projects that target Windows 10 Desktop (including ARM and ARM64) are + available in `build/vsVERSIONwin10/` (Visual Studio 2017 requires SDK + version 10.0.16299). Minimal projects that target Windows 10 UWP are available in - `build/winstore82/`. + `build/vsVERSIONuwp/`. Most projects are supported with any of the mentioned Visual Studio verions, with the following exceptions: @@ -161,7 +134,7 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - mingw-w64: - The required version is at least 4.8. + The required compiler version is at least GCC 7. make CONFIG=mingw64-win32 # for win32 @@ -172,9 +145,9 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. The minimum required compiler versions are: - - gcc 4.8 + - gcc 7 - - clang 3.6 + - clang 5 The Makefile requires pkg-config for native builds. For sound output in openmpt123, PortAudio or SDL is required. @@ -256,7 +229,7 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - other compilers: - To compile libopenmpt with other C++11 compliant compilers, run: + To compile libopenmpt with other C++14 compliant compilers, run: make CONFIG=generic diff --git a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk index a6b6f2187..4661aef61 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk @@ -7,7 +7,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := openmpt LOCAL_CFLAGS +=#-std=c99 -LOCAL_CPPFLAGS += -std=c++11 -fexceptions -frtti +LOCAL_CPPFLAGS += -std=c++17 -fexceptions -frtti LOCAL_CPP_FEATURES += exceptions rtti @@ -111,12 +111,12 @@ LOCAL_SRC_FILES += \ libopenmpt/libopenmpt_cxx.cpp \ libopenmpt/libopenmpt_impl.cpp \ libopenmpt/libopenmpt_ext_impl.cpp \ + soundbase/Dither.cpp \ soundlib/AudioCriticalSection.cpp \ soundlib/ContainerMMCMP.cpp \ soundlib/ContainerPP20.cpp \ soundlib/ContainerUMX.cpp \ soundlib/ContainerXPK.cpp \ - soundlib/Dither.cpp \ soundlib/Dlsbank.cpp \ soundlib/Fastmix.cpp \ soundlib/InstrumentExtensions.cpp \ @@ -178,10 +178,12 @@ LOCAL_SRC_FILES += \ soundlib/RowVisitor.cpp \ soundlib/S3MTools.cpp \ soundlib/SampleFormats.cpp \ + soundlib/SampleFormatBRR.cpp \ soundlib/SampleFormatFLAC.cpp \ soundlib/SampleFormatMediaFoundation.cpp \ soundlib/SampleFormatMP3.cpp \ soundlib/SampleFormatOpus.cpp \ + soundlib/SampleFormatSFZ.cpp \ soundlib/SampleFormatVorbis.cpp \ soundlib/SampleIO.cpp \ soundlib/Sndfile.cpp \ @@ -193,7 +195,7 @@ LOCAL_SRC_FILES += \ soundlib/UpgradeModule.cpp \ soundlib/Tables.cpp \ soundlib/Tagging.cpp \ - soundlib/tuningbase.cpp \ + soundlib/TinyFFT.cpp \ soundlib/tuningCollection.cpp \ soundlib/tuning.cpp \ soundlib/WAVTools.cpp \ diff --git a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk index 7ca8cd0df..6b0c65285 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk @@ -1,5 +1,5 @@ APP_CFLAGS :=#-std=c99 -APP_CPPFLAGS := -std=c++11 -fexceptions -frtti +APP_CPPFLAGS := -std=c++17 -fexceptions -frtti APP_LDFLAGS := APP_STL := c++_shared diff --git a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt index 54453bc02..248c36c78 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt +++ b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt @@ -3,7 +3,7 @@ This is preliminary documentation. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - 0. The minimum required Android NDK version is r16b. + 0. The minimum required Android NDK version is r18b. 1. Copy the whole libopenmpt source tree below your jni directory. 2. Copy build/android_ndk/* into the root of libopenmpt, i.e. also into the jni directory and adjust as needed. diff --git a/Frameworks/OpenMPT/OpenMPT/build/dist.mk b/Frameworks/OpenMPT/OpenMPT/build/dist.mk index 8088a8c8f..fe859472f 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/dist.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/dist.mk @@ -1,4 +1,4 @@ -MPT_SVNVERSION=12263 -MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.10 -MPT_SVNDATE=2019-10-30T10:43:15.521271Z +MPT_SVNVERSION=13555 +MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.2 +MPT_SVNDATE=2020-08-30T13:42:32.941871Z diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk index c1064d103..fd073cd26 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk @@ -9,14 +9,6 @@ CXXFLAGS_STDCXX = -std=$(STDCXX) else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++17 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++17' ; fi ), c++17) CXXFLAGS_STDCXX = -std=c++17 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) -CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif -endif endif endif CFLAGS_STDC = -std=c99 diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk index f8283f5d8..cb5e4fe3e 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk @@ -12,10 +12,6 @@ CXXFLAGS_STDCXX = -std=c++17 else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif endif endif endif @@ -43,20 +39,27 @@ endif CXXFLAGS_WARNINGS += -Wmissing-declarations -Wshift-count-negative -Wshift-count-overflow -Wshift-overflow -Wshift-sign-overflow -Wshift-op-parentheses CFLAGS_WARNINGS += -Wmissing-prototypes -Wshift-count-negative -Wshift-count-overflow -Wshift-overflow -Wshift-sign-overflow -Wshift-op-parentheses +CXXFLAGS_WARNINGS += -Wdeprecated -Wextra-semi -Wnon-virtual-dtor -Wreserved-id-macro -Wglobal-constructors -Wimplicit-fallthrough + #CXXFLAGS_WARNINGS += -Wdocumentation #CXXFLAGS_WARNINGS += -Wconversion #CXXFLAGS_WARNINGS += -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-shadow -Wno-sign-conversion -Wno-weak-vtables ifeq ($(MODERN),1) LDFLAGS += -fuse-ld=lld -CXXFLAGS_WARNINGS += -Wpedantic -Wframe-larger-than=20000 -#CXXFLAGS_WARNINGS += -Wdouble-promotion -Wframe-larger-than=16000 +CXXFLAGS_WARNINGS += -Wpedantic -Wframe-larger-than=16000 CFLAGS_WARNINGS += -Wpedantic -Wframe-larger-than=4000 -#CFLAGS_WARNINGS += -Wdouble-promotion LDFLAGS_WARNINGS += -Wl,-no-undefined -Wl,--detect-odr-violations -CXXFLAGS_WARNINGS += -Wdeprecated -Wextra-semi -Wnon-virtual-dtor -Wreserved-id-macro +# re-renable after 1.29 branch +#CXXFLAGS_WARNINGS += -Wdouble-promotion +#CFLAGS_WARNINGS += -Wdouble-promotion endif -CFLAGS_SILENT += -Wno-unused-parameter -Wno-unused-function -Wno-cast-qual +CFLAGS_SILENT += -Wno-cast-align +CFLAGS_SILENT += -Wno-cast-qual +CFLAGS_SILENT += -Wno-missing-prototypes +CFLAGS_SILENT += -Wno-sign-compare +CFLAGS_SILENT += -Wno-unused-function +CFLAGS_SILENT += -Wno-unused-parameter EXESUFFIX= diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk index 18acb5a3b..b876e777b 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk @@ -7,9 +7,6 @@ include build/make/config-clang.mk # Mac OS X overrides DYNLINK=0 SHARED_SONAME=0 -# when using iconv -#CPPFLAGS += -DMPT_WITH_ICONV -#LDLIBS += -liconv else ifeq ($(HOST_FLAVOUR),LINUX) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk index ea582894f..ae0afeaeb 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk @@ -1,19 +1,19 @@ -CC = i586-pc-msdosdjgpp-gcc -CXX = i586-pc-msdosdjgpp-g++ -LD = i586-pc-msdosdjgpp-g++ -AR = i586-pc-msdosdjgpp-ar +CC = i386-pc-msdosdjgpp-gcc +CXX = i386-pc-msdosdjgpp-g++ +LD = i386-pc-msdosdjgpp-g++ +AR = i386-pc-msdosdjgpp-ar # Note that we are using GNU extensions instead of 100% standards-compliant # mode, because otherwise DJGPP-specific headers/functions are unavailable. -CXXFLAGS_STDCXX = -std=gnu++11 +CXXFLAGS_STDCXX = -std=gnu++17 CFLAGS_STDC = -std=gnu99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) CPPFLAGS += -CXXFLAGS += -march=pentium -mtune=pentium -CFLAGS += -march=pentium -mtune=pentium +CXXFLAGS += -march=i386 -m80387 -mtune=pentium +CFLAGS += -march=i386 -m80387 -mtune=pentium LDFLAGS += LDLIBS += -lm ARFLAGS := rcs diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk index aaf08715b..3a9670d78 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk @@ -1,8 +1,9 @@ -CC = emcc -CXX = em++ +CC = emcc -c +CXX = em++ -c LD = em++ AR = emar +LINK.cc = em++ $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) EMSCRIPTEN_TARGET?=default @@ -11,14 +12,6 @@ CXXFLAGS_STDCXX = -std=$(STDCXX) else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++17 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++17' ; fi ), c++17) CXXFLAGS_STDCXX = -std=c++17 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) -CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif -endif endif endif CFLAGS_STDC = -std=c99 @@ -45,50 +38,49 @@ LDFLAGS += LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 -else ifeq ($(EMSCRIPTEN_TARGET),wasm) -# emits native wasm AND an emulator for running wasm in asmjs/js with full wasm optimizations. +else ifeq ($(EMSCRIPTEN_TARGET),all) +# emits native wasm AND javascript with full wasm optimizations. # as of emscripten 1.38, this is equivalent to default. CPPFLAGS += -DMPT_BUILD_WASM -CXXFLAGS += -s WASM=1 -s BINARYEN_METHOD='native-wasm' -CFLAGS += -s WASM=1 -s BINARYEN_METHOD='native-wasm' -LDFLAGS += -s WASM=1 -s BINARYEN_METHOD='native-wasm' +CXXFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 +CFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 +LDFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 -else ifeq ($(EMSCRIPTEN_TARGET),asmjs128m) -# emits only asmjs -CPPFLAGS += -DMPT_BUILD_ASMJS -CXXFLAGS += -s WASM=0 -s ASM_JS=1 -CFLAGS += -s WASM=0 -s ASM_JS=1 -LDFLAGS += -s WASM=0 -s ASM_JS=1 +else ifeq ($(EMSCRIPTEN_TARGET),wasm) +# emits native wasm. +CPPFLAGS += -DMPT_BUILD_WASM +CXXFLAGS += -s WASM=1 +CFLAGS += -s WASM=1 +LDFLAGS += -s WASM=1 -LDFLAGS += -s ALLOW_MEMORY_GROWTH=0 -s ABORTING_MALLOC=0 -s TOTAL_MEMORY=134217728 - -else ifeq ($(EMSCRIPTEN_TARGET),asmjs) -# emits only asmjs -CPPFLAGS += -DMPT_BUILD_ASMJS -CXXFLAGS += -s WASM=0 -s ASM_JS=1 -CFLAGS += -s WASM=0 -s ASM_JS=1 -LDFLAGS += -s WASM=0 -s ASM_JS=1 - -LDFLAGS += -s ALLOW_MEMORY_GROWTH=0 -s ABORTING_MALLOC=0 +LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 else ifeq ($(EMSCRIPTEN_TARGET),js) # emits only plain javascript with plain javascript focused optimizations. CPPFLAGS += -DMPT_BUILD_ASMJS -CXXFLAGS += -s WASM=0 -s ASM_JS=2 -s LEGACY_VM_SUPPORT=1 -CFLAGS += -s WASM=0 -s ASM_JS=2 -s LEGACY_VM_SUPPORT=1 -LDFLAGS += -s WASM=0 -s ASM_JS=2 -s LEGACY_VM_SUPPORT=1 +CXXFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 +CFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 +LDFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 endif -CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -ffast-math -CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -ffast-math -fno-strict-aliasing -LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s EXPORT_NAME="'libopenmpt'" +CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -ffast-math +CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -ffast-math -fno-strict-aliasing +LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -s EXPORT_NAME="'libopenmpt'" -CFLAGS_SILENT += -Wno-unused-parameter -Wno-unused-function -Wno-cast-qual +CFLAGS_SILENT += -Wno-\#warnings +CFLAGS_SILENT += -Wno-cast-align +CFLAGS_SILENT += -Wno-cast-qual +CFLAGS_SILENT += -Wno-format +CFLAGS_SILENT += -Wno-missing-prototypes +CFLAGS_SILENT += -Wno-sign-compare +CFLAGS_SILENT += -Wno-unused-function +CFLAGS_SILENT += -Wno-unused-parameter +CFLAGS_SILENT += -Wno-unused-variable CXXFLAGS_WARNINGS += -Wmissing-declarations CFLAGS_WARNINGS += -Wmissing-prototypes @@ -98,7 +90,7 @@ REQUIRES_RUNPREFIX=1 EXESUFFIX=.js SOSUFFIX=.js RUNPREFIX=node -TEST_LDFLAGS= --pre-js build/make/test-pre.js +TEST_LDFLAGS= --pre-js build/make/test-pre.js -lnodefs.js DYNLINK=0 SHARED_LIB=1 @@ -106,6 +98,7 @@ STATIC_LIB=0 EXAMPLES=1 OPENMPT123=0 SHARED_SONAME=0 +NO_SHARED_LINKER_FLAG=1 # Disable the generic compiler optimization flags as emscripten is sufficiently different. # Optimization flags are hard-coded for emscripten in this file. diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk index e92d26fc7..4c763bd51 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk @@ -9,14 +9,6 @@ CXXFLAGS_STDCXX = -std=$(STDCXX) else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++17 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++17' ; fi ), c++17) CXXFLAGS_STDCXX = -std=c++17 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) -CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif -endif endif endif CFLAGS_STDC = -std=c99 @@ -40,17 +32,27 @@ CXXFLAGS += -fsanitize=undefined CFLAGS += -fsanitize=undefined endif +CXXFLAGS_WARNINGS += -Wsuggest-override -Wno-psabi + ifeq ($(MODERN),1) LDFLAGS += -fuse-ld=gold CXXFLAGS_WARNINGS += -Wpedantic -Wlogical-op -Wframe-larger-than=16000 -#CXXFLAGS_WARNINGS += -Wdouble-promotion CFLAGS_WARNINGS += -Wpedantic -Wlogical-op -Wframe-larger-than=4000 -#CFLAGS_WARNINGS += -Wdouble-promotion LDFLAGS_WARNINGS += -Wl,-no-undefined -Wl,--detect-odr-violations -CXXFLAGS_WARNINGS += -Wsuggest-override +# re-renable after 1.29 branch +#CXXFLAGS_WARNINGS += -Wdouble-promotion +#CFLAGS_WARNINGS += -Wdouble-promotion endif -CFLAGS_SILENT += -Wno-unused-parameter -Wno-unused-function -Wno-cast-qual -Wno-old-style-declaration -Wno-type-limits -Wno-unused-but-set-variable +CFLAGS_SILENT += -Wno-cast-qual +CFLAGS_SILENT += -Wno-empty-body +CFLAGS_SILENT += -Wno-implicit-fallthrough +CFLAGS_SILENT += -Wno-old-style-declaration +CFLAGS_SILENT += -Wno-sign-compare +CFLAGS_SILENT += -Wno-type-limits +CFLAGS_SILENT += -Wno-unused-but-set-variable +CFLAGS_SILENT += -Wno-unused-function +CFLAGS_SILENT += -Wno-unused-parameter EXESUFFIX= diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk index ea7066285..fc1ebbec6 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk @@ -4,7 +4,7 @@ CXX ?= c++ LD ?= c++ AR = ar -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk index e5c68df8f..a991f1a09 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk @@ -4,7 +4,7 @@ CXX = i686-w64-mingw32-g++$(MINGW_FLAVOUR) LD = i686-w64-mingw32-g++$(MINGW_FLAVOUR) AR = i686-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk index c0ac0a824..0b6f50001 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk @@ -4,7 +4,7 @@ CXX = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) LD = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) AR = x86_64-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk index 01cb215d8..dd848ce3c 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk @@ -4,7 +4,7 @@ CXX = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) LD = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) AR = x86_64-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk index 776023a18..62cc0e210 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk @@ -4,7 +4,7 @@ CXX = i686-w64-mingw32-g++$(MINGW_FLAVOUR) LD = i686-w64-mingw32-g++$(MINGW_FLAVOUR) AR = i686-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk index 57e3fd811..d9757fae2 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk @@ -4,7 +4,7 @@ CXX ?= c++ LD ?= c++ AR = ar -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h index 07bcdfaa4..73789667e 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h +++ b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h @@ -1,10 +1,10 @@ #pragma once -#define OPENMPT_VERSION_SVNVERSION "12263" -#define OPENMPT_VERSION_REVISION 12263 +#define OPENMPT_VERSION_SVNVERSION "13555" +#define OPENMPT_VERSION_REVISION 13555 #define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0 -#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.10" -#define OPENMPT_VERSION_DATE "2019-10-30T10:43:15.521271Z" +#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.2" +#define OPENMPT_VERSION_DATE "2020-08-30T13:42:32.941871Z" #define OPENMPT_VERSION_IS_PACKAGE 1 diff --git a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h index ce631f794..ea7fc264e 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h +++ b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h @@ -20,36 +20,10 @@ #if MPT_OS_WINDOWS -#if defined(MPT_BUILD_MSVC) - -#if defined(MPT_BUILD_TARGET_XP) - -#if defined(_M_X64) -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0502 // _WIN32_WINNT_WS03 -#endif -#else // !_M_X64 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP -#endif -#endif // _M_X64 - -#else // MPT_BUILD_TARGET - #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7 #endif -#endif // MPT_BUILD_TARGET - -#else // !MPT_BUILD_MSVC - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP -#endif - -#endif // MPT_BUILD_MSVC - #ifndef WINVER #define WINVER _WIN32_WINNT #endif @@ -98,8 +72,15 @@ #if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#if !defined(MPT_BUILD_WINESUPPORT) +#define MPT_WITH_MFC +#endif // !MPT_BUILD_WINESUPPORT +#endif // MPT_OS_WINDOWS + // OpenMPT-only dependencies #define MPT_WITH_ASIO +#define MPT_WITH_DMO #define MPT_WITH_LAME #define MPT_WITH_LHASA #define MPT_WITH_MINIZIP @@ -118,13 +99,10 @@ // OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples) //#define MPT_WITH_DL #define MPT_WITH_FLAC -//#define MPT_WITH_ICONV //#define MPT_WITH_LTDL #if MPT_OS_WINDOWS -#if (_WIN32_WINNT >= 0x0601) #define MPT_WITH_MEDIAFOUNDATION #endif -#endif //#define MPT_WITH_MINIMP3 //#define MPT_WITH_MINIZ #define MPT_WITH_MPG123 @@ -132,6 +110,11 @@ //#define MPT_WITH_STBVORBIS #define MPT_WITH_VORBIS #define MPT_WITH_VORBISFILE +#if MPT_OS_WINDOWS +#if (_WIN32_WINNT >= 0x0A00) +#define MPT_WITH_WINDOWS10 +#endif +#endif #define MPT_WITH_ZLIB #endif // MODPLUG_TRACKER @@ -147,7 +130,6 @@ //#define MPT_WITH_DL //#define MPT_WITH_FLAC -//#define MPT_WITH_ICONV //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION #define MPT_WITH_MINIMP3 @@ -163,7 +145,6 @@ //#define MPT_WITH_DL //#define MPT_WITH_FLAC -//#define MPT_WITH_ICONV //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION //#define MPT_WITH_MINIMP3 @@ -194,7 +175,6 @@ //#define MPT_WITH_DL //#define MPT_WITH_FLAC -//#define MPT_WITH_ICONV //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION //#define MPT_WITH_MINIMP3 @@ -229,14 +209,14 @@ #define MPT_LOG_GLOBAL_LEVEL 0 #endif +// Enable all individual logging macros and MPT_LOG calls +//#define MPT_ALL_LOGGING + // Disable all runtime asserts #if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT) #define NO_ASSERTS #endif -// Enable std::istream support in class FileReader (this is generally not needed for the tracker, local files can easily be mmapped as they have been before introducing std::istream support) -//#define MPT_FILEREADER_STD_ISTREAM - // Enable callback stream wrapper for FileReader (required by libopenmpt C API). //#define MPT_FILEREADER_CALLBACK_STREAM @@ -267,9 +247,6 @@ // Define to build without VST plugin support; makes build possible without VST SDK. //#define NO_VST -// Define to build without DMO plugin support -//#define NO_DMO - // (HACK) Define to build without any plugin support //#define NO_PLUGINS @@ -300,7 +277,7 @@ #define NO_ASSERTS #endif //#define NO_LOGGING -#define MPT_FILEREADER_STD_ISTREAM +//#define MPT_ALL_LOGGING #define MPT_FILEREADER_CALLBACK_STREAM //#define MPT_EXTERNAL_SAMPLES #if defined(ENABLE_TESTS) || defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) @@ -320,9 +297,6 @@ #define NO_EQ #define NO_AGC #define NO_VST -//#if !MPT_OS_WINDOWS || MPT_OS_WINDOWS_WINRT || !MPT_COMPILER_MSVC || !defined(LIBOPENMPT_BUILD_FULL) -#define NO_DMO -//#endif //#define NO_PLUGINS //#define NO_LIBOPENMPT_C //#define NO_LIBOPENMPT_CXX @@ -333,51 +307,28 @@ #if MPT_OS_WINDOWS - #define MPT_CHARSET_WIN32 #ifndef MPT_ENABLE_CHARSET_LOCALE #define MPT_ENABLE_CHARSET_LOCALE #endif #elif MPT_OS_LINUX - #define MPT_CHARSET_ICONV - #elif MPT_OS_ANDROID - #define MPT_CHARSET_INTERNAL - #elif MPT_OS_EMSCRIPTEN - #define MPT_CHARSET_INTERNAL #ifndef MPT_LOCALE_ASSUME_CHARSET - #define MPT_LOCALE_ASSUME_CHARSET CharsetUTF8 + #define MPT_LOCALE_ASSUME_CHARSET Charset::UTF8 #endif #elif MPT_OS_MACOSX_OR_IOS - #if defined(MPT_WITH_ICONV) - #define MPT_CHARSET_ICONV - #ifndef MPT_ICONV_NO_WCHAR - #define MPT_ICONV_NO_WCHAR - #endif - #else - #define MPT_CHARSET_INTERNAL - #endif - //#ifndef MPT_LOCALE_ASSUME_CHARSET - //#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 + #define MPT_LOCALE_ASSUME_CHARSET Charset::CP437 #endif -#elif defined(MPT_WITH_ICONV) - - #define MPT_CHARSET_ICONV - #endif @@ -457,6 +408,7 @@ #if defined(ENABLE_ASM) #if MPT_COMPILER_MSVC && defined(_M_IX86) +#define ENABLE_CPUID // Generate general x86 inline assembly and intrinsics. #define ENABLE_X86 // Generate MMX instructions (only used when the CPU supports it). @@ -469,14 +421,14 @@ #define ENABLE_SSE3 // Generate SSE4 instructions (only used when the CPU supports it). #define ENABLE_SSE4 - -#if defined(MPT_BUILD_TARGET_XP) -// Generate AMD specific instruction set extensions (only used when the CPU supports it). -#define ENABLE_X86_AMD -#endif +// Generate AVX instructions (only used when the CPU supports it). +#define ENABLE_AVX +// Generate AVX2 instructions (only used when the CPU supports it). +#define ENABLE_AVX2 #elif MPT_COMPILER_MSVC && defined(_M_X64) +#define ENABLE_CPUID // Generate general x64 intrinsics. #define ENABLE_X64 // Generate SSE instructions (only used when the CPU supports it). @@ -487,18 +439,14 @@ #define ENABLE_SSE3 // Generate SSE4 instructions (only used when the CPU supports it). #define ENABLE_SSE4 +// Generate AVX instructions (only used when the CPU supports it). +#define ENABLE_AVX +// Generate AVX2 instructions (only used when the CPU supports it). +#define ENABLE_AVX2 #endif // arch #endif // ENABLE_ASM -#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 - #if defined(ENABLE_TESTS) && defined(MODPLUG_NO_FILESAVE) #undef MODPLUG_NO_FILESAVE // tests recommend file saving #endif @@ -524,26 +472,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 - #if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_DYNBIND) #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 - #if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_DYNBIND) #define MPT_ENABLE_DYNBIND // MediaFoundation needs dynamic loading in order to test availability of delay loaded libs #endif @@ -556,10 +488,6 @@ #define MPT_ENABLE_FILEIO // Test suite requires PathString for file loading. #endif -#if !MPT_OS_WINDOWS && !defined(MPT_FILEREADER_STD_ISTREAM) -#define MPT_FILEREADER_STD_ISTREAM // MMAP is only supported on Windows -#endif - #if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_FILEIO) #define MPT_ENABLE_FILEIO // Tracker requires disk file io #endif @@ -575,7 +503,6 @@ #if defined(NO_PLUGINS) // Any plugin type requires NO_PLUGINS to not be defined. #define NO_VST -#define NO_DMO #endif #if defined(ENABLE_ASM) || !defined(NO_VST) @@ -626,6 +553,10 @@ // platform configuration +#ifdef MPT_WITH_MFC +//#define MPT_MFC_FULL // use full MFC, including MFC controls +#endif // MPT_WITH_MFC + #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS #if !defined(MPT_BUILD_WINESUPPORT) @@ -721,11 +652,13 @@ #pragma warning(disable:4512) // assignment operator could not be generated #pragma warning(error:4309) // Treat "truncation of constant value"-warning as error. +#pragma warning(error:4463) // Treat overflow; assigning value to bit-field that can only hold values from low_value to high_value"-warning as error. #ifdef MPT_BUILD_ANALYZED // Disable Visual Studio static analyzer warnings that generate too many false positives in VS2010. //#pragma warning(disable:6246) //#pragma warning(disable:6262) +#pragma warning(disable:6297) // 32-bit value is shifted, then cast to 64-bit value. Results might not be an expected value. #pragma warning(disable:6326) // Potential comparison of a constant with another constant //#pragma warning(disable:6385) //#pragma warning(disable:6386) @@ -733,32 +666,30 @@ #endif // MPT_COMPILER_MSVC +#if MPT_COMPILER_CLANG + +#if defined(MPT_BUILD_MSVC) +#pragma clang diagnostic warning "-Wimplicit-fallthrough" +#endif // MPT_BUILD_MSVC + +#if defined(MODPLUG_TRACKER) +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif // MODPLUG_TRACKER + +#endif // MPT_COMPILER_CLANG + // standard library quirks -#if MPT_CXX_AT_LEAST(17) -#if (MPT_COMPILER_GCC || MPT_COMPILER_CLANG) -// we need to detect the standard library via macro __GLIBCXX__ -#include -#endif -#if MPT_COMPILER_MSVC || MPT_GCC_BEFORE(8,1,0) || MPT_CLANG_BEFORE(5,0,0) || (MPT_COMPILER_GCC && defined(__GLIBCXX__) && (defined(__MINGW32__) || defined(__MINGW64__))) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) || (MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) || MPT_OS_OPENBSD || MPT_OS_EMSCRIPTEN || MPT_OS_HAIKU || (defined(__clang__) && defined(_MSC_VER)) -#define MPT_COMPILER_QUIRK_NO_ALIGNEDALLOC -#endif -#endif - // 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 diff --git a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h index 5087163c3..3b1d75951 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h +++ b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h @@ -34,8 +34,8 @@ #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,6,0) -#error "clang version 3.6 required" +#if MPT_CLANG_BEFORE(5,0,0) +#error "clang version 5 required" #endif #if defined(__clang_analyzer__) @@ -51,14 +51,22 @@ #define MPT_GCC_AT_LEAST(major,minor,patch) (MPT_COMPILER_GCC_VERSION >= MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) #define MPT_GCC_BEFORE(major,minor,patch) (MPT_COMPILER_GCC_VERSION < MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch))) -#if MPT_GCC_BEFORE(4,8,0) -#error "GCC version 4.8 required" +#if MPT_GCC_BEFORE(7,1,0) +#error "GCC version 7.1 required" #endif #elif defined(_MSC_VER) #define MPT_COMPILER_MSVC 1 -#if (_MSC_VER >= 1922) +#if (_MSC_VER >= 1926) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,6) +#elif (_MSC_VER >= 1925) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,5) +#elif (_MSC_VER >= 1924) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,4) +#elif (_MSC_VER >= 1923) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,3) +#elif (_MSC_VER >= 1922) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,2) #elif (_MSC_VER >= 1921) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,1) @@ -96,8 +104,8 @@ #define MPT_MSVC_AT_LEAST(version,sp) (MPT_COMPILER_MSVC_VERSION >= MPT_COMPILER_MAKE_VERSION2((version),(sp))) #define MPT_MSVC_BEFORE(version,sp) (MPT_COMPILER_MSVC_VERSION < MPT_COMPILER_MAKE_VERSION2((version),(sp))) -#if MPT_MSVC_BEFORE(2015,0) -#error "MSVC version 2015 required" +#if MPT_MSVC_BEFORE(2017,9) +#error "MSVC version 2017 15.9 required" #endif #if defined(_PREFAST_) @@ -139,29 +147,21 @@ #if (__cplusplus >= 201703) #define MPT_CXX 17 -#elif (__cplusplus >= 201402) -#define MPT_CXX 14 #else -#define MPT_CXX 11 +#define MPT_CXX 17 #endif #elif MPT_COMPILER_MSVC -#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 -#endif -#else -#define MPT_CXX 11 +#define MPT_CXX 17 #endif #else -#define MPT_CXX 11 +#define MPT_CXX 17 #endif @@ -205,11 +205,13 @@ #define MPT_OS_EMSCRIPTEN 1 #if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__) #if (__EMSCRIPTEN_major__ > 1) - // ok - #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ >= 38) - // ok + // ok + #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ > 39) + // ok + #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ == 39) && (__EMSCRIPTEN_tiny__ >= 7) + // ok #else - #error "Emscripten >= 1.38 is required." + #error "Emscripten >= 1.39.7 is required." #endif #endif #elif defined(_WIN32) @@ -306,13 +308,59 @@ #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 + +#if defined(__arm__) + +#if defined(__SOFTFP__) +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1 +#else +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 +#endif +#if defined(__VFP_FP__) +// native-endian IEEE754 +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 +#elif defined(__MAVERICK__) +// little-endian IEEE754, we assume native-endian though +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 +#else +// not IEEE754 +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 1 #endif +#elif defined(__mips__) + +#if defined(__mips_soft_float) +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1 +#else +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 +#endif + +#endif + +#if MPT_OS_EMSCRIPTEN +#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 1 +#endif + +#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER32 +#define MPT_COMPILER_QUIRK_FLOAT_PREFER32 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER64 +#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_EMULATED +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp index 5ba5a4d5b..ace006272 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp @@ -191,7 +191,7 @@ void ComponentFactoryBase::PreConstruct() const { MPT_LOG(LogInformation, "Components", mpt::format(U_("Constructing Component %1")) - ( mpt::ToUnicode(mpt::CharsetASCII, m_ID) + ( mpt::ToUnicode(mpt::Charset::ASCII, m_ID) ) ); } @@ -233,7 +233,7 @@ static ComponentListEntry * & ComponentListHead() bool ComponentListPush(ComponentListEntry *entry) { - MPT_LOCK_GUARD guard(ComponentListMutex()); + mpt::lock_guard guard(ComponentListMutex()); entry->next = ComponentListHead(); ComponentListHead() = entry; return true; @@ -267,7 +267,7 @@ std::shared_ptr ComponentManager::Instance() ComponentManager::ComponentManager(const IComponentManagerSettings &settings) : m_Settings(settings) { - MPT_LOCK_GUARD guard(ComponentListMutex()); + mpt::lock_guard guard(ComponentListMutex()); for(ComponentListEntry *entry = ComponentListHead(); entry; entry = entry->next) { entry->reg(*this); diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h index 6586cae61..f8c555fca 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h @@ -318,6 +318,8 @@ public: virtual bool KeepLoaded() const = 0; virtual bool IsBlocked(const std::string &key) const = 0; virtual mpt::PathString Path() const = 0; +protected: + virtual ~IComponentManagerSettings() = default; }; @@ -440,7 +442,7 @@ std::shared_ptr GetComponent() { static std::weak_ptr cache; static mpt::mutex m; - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); std::shared_ptr component = cache.lock(); if(!component) { diff --git a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h index 1960f1884..b2a80612c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h @@ -13,6 +13,9 @@ #include "BuildSettings.h" #include +#if MPT_CXX_AT_LEAST(20) +#include +#endif // C++20 #include #include @@ -27,15 +30,43 @@ +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + #if MPT_CXX_AT_LEAST(20) -// nothing +using std::endian; -#elif MPT_COMPILER_GENERIC +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -// rely on runtime detection instead of using non-standard macros +static constexpr mpt::endian get_endian() noexcept +{ + return mpt::endian::native; +} -#else +static constexpr bool endian_is_little() noexcept +{ + return get_endian() == mpt::endian::little; +} + +static constexpr bool endian_is_big() noexcept +{ + return get_endian() == mpt::endian::big; +} + +static constexpr bool endian_is_weird() noexcept +{ + return !endian_is_little() && !endian_is_big(); +} + +#else // !C++20 + +#if !MPT_COMPILER_GENERIC #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN @@ -75,111 +106,36 @@ #endif #endif -#endif +#endif // !MPT_COMPILER_GENERIC -#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 - - - -OPENMPT_NAMESPACE_BEGIN - - - -namespace mpt { - - - -// 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 +#if MPT_COMPILER_GENERIC + native = 0u, +#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) + native = little, +#elif defined(MPT_PLATFORM_BIG_ENDIAN) + native = big, #else - native = 0u + 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 +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); namespace detail { static MPT_FORCEINLINE mpt::endian endian_probe() noexcept { - typedef uint32 endian_probe_type; - MPT_STATIC_ASSERT(sizeof(endian_probe_type) == 4); + using endian_probe_type = uint32; + static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; - const 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)); + const std::array probe{ {mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78)} }; + const endian_probe_type test = mpt::bit_cast(probe); mpt::endian result = mpt::endian::native; switch(test) { @@ -196,17 +152,24 @@ namespace detail { return result; } -} +} // namespace detail static MPT_FORCEINLINE mpt::endian get_endian() noexcept { - MPT_CONSTANT_IF(mpt::endian_known()) +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:6285) // false-positive: ( || ) is always a non-zero constant. +#endif // MPT_COMPILER_MSVC + if constexpr((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else { return detail::endian_probe(); } +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC } static MPT_FORCEINLINE bool endian_is_little() noexcept @@ -234,12 +197,12 @@ static MPT_FORCEINLINE bool endian_is_weird() noexcept struct BigEndian_tag { - static MPT_CONSTEXPR11_VAR mpt::endian endian = mpt::endian::big; + static constexpr mpt::endian endian = mpt::endian::big; }; struct LittleEndian_tag { - static MPT_CONSTEXPR11_VAR mpt::endian endian = mpt::endian::little; + static constexpr mpt::endian endian = mpt::endian::little; }; @@ -249,7 +212,7 @@ namespace mpt { template inline void SwapBufferEndian(std::size_t elementSize, Tbyte * buffer, std::size_t elements) { - MPT_STATIC_ASSERT(sizeof(Tbyte) == 1); + static_assert(sizeof(Tbyte) == 1); for(std::size_t element = 0; element < elements; ++element) { std::reverse(&buffer[0], &buffer[elementSize]); @@ -261,7 +224,32 @@ inline void SwapBufferEndian(std::size_t elementSize, Tbyte * buffer, std::size_ -#if !MPT_ENDIAN_IS_CONSTEXPR +#define MPT_constexpr_bswap16(x) \ + ( uint16(0) \ + | ((static_cast(x) >> 8) & 0x00FFu) \ + | ((static_cast(x) << 8) & 0xFF00u) \ + ) \ +/**/ +#define MPT_constexpr_bswap32(x) \ + ( uint32(0) \ + | ((static_cast(x) & 0x000000FFu) << 24) \ + | ((static_cast(x) & 0x0000FF00u) << 8) \ + | ((static_cast(x) & 0x00FF0000u) >> 8) \ + | ((static_cast(x) & 0xFF000000u) >> 24) \ + ) \ +/**/ +#define MPT_constexpr_bswap64(x) \ + ( uint64(0) \ + | (((static_cast(x) >> 0) & 0xffull) << 56) \ + | (((static_cast(x) >> 8) & 0xffull) << 48) \ + | (((static_cast(x) >> 16) & 0xffull) << 40) \ + | (((static_cast(x) >> 24) & 0xffull) << 32) \ + | (((static_cast(x) >> 32) & 0xffull) << 24) \ + | (((static_cast(x) >> 40) & 0xffull) << 16) \ + | (((static_cast(x) >> 48) & 0xffull) << 8) \ + | (((static_cast(x) >> 56) & 0xffull) << 0) \ + ) \ +/**/ #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 @@ -295,85 +283,60 @@ 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 -#define MPT_bswap16(x) \ - ( uint16(0) \ - | ((static_cast(x) >> 8) & 0x00FFu) \ - | ((static_cast(x) << 8) & 0xFF00u) \ - ) \ -/**/ +#define MPT_bswap16(x) MPT_constexpr_bswap16(x) #endif #ifndef MPT_bswap32 -#define MPT_bswap32(x) \ - ( uint32(0) \ - | ((static_cast(x) & 0x000000FFu) << 24) \ - | ((static_cast(x) & 0x0000FF00u) << 8) \ - | ((static_cast(x) & 0x00FF0000u) >> 8) \ - | ((static_cast(x) & 0xFF000000u) >> 24) \ - ) \ -/**/ +#define MPT_bswap32(x) MPT_constexpr_bswap32(x) #endif #ifndef MPT_bswap64 -#define MPT_bswap64(x) \ - ( uint64(0) \ - | (((static_cast(x) >> 0) & 0xffull) << 56) \ - | (((static_cast(x) >> 8) & 0xffull) << 48) \ - | (((static_cast(x) >> 16) & 0xffull) << 40) \ - | (((static_cast(x) >> 24) & 0xffull) << 32) \ - | (((static_cast(x) >> 32) & 0xffull) << 24) \ - | (((static_cast(x) >> 40) & 0xffull) << 16) \ - | (((static_cast(x) >> 48) & 0xffull) << 8) \ - | (((static_cast(x) >> 56) & 0xffull) << 0) \ - ) \ -/**/ +#define MPT_bswap64(x) MPT_constexpr_bswap64(x) #endif + template -static MPT_CONSTEXPR17_FUN std::array EndianEncode(T val) noexcept +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); - typedef T base_type; - typedef typename std::make_unsigned::type unsigned_base_type; - typedef Tendian endian_type; + static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + static_assert(sizeof(T) == size); + using base_type = T; + using unsigned_base_type = typename std::make_unsigned::type; + using endian_type = Tendian; unsigned_base_type uval = static_cast(val); - std::array data; - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::little) + std::array data{}; + if constexpr(endian_type::endian == mpt::endian::little) { for(std::size_t i = 0; i < sizeof(base_type); ++i) { - data[i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); + data[i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); } } else { for(std::size_t i = 0; i < sizeof(base_type); ++i) { - data[(sizeof(base_type)-1) - i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); + data[(sizeof(base_type)-1) - i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); } } return data; } template -static MPT_CONSTEXPR17_FUN T EndianDecode(std::array data) noexcept +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); - typedef T base_type; - typedef typename std::make_unsigned::type unsigned_base_type; - typedef Tendian endian_type; + static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + static_assert(sizeof(T) == size); + using base_type = T; + using unsigned_base_type = typename std::make_unsigned::type; + using endian_type = Tendian; base_type val = base_type(); unsigned_base_type uval = unsigned_base_type(); - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::little) + if constexpr(endian_type::endian == mpt::endian::little) { for(std::size_t i = 0; i < sizeof(base_type); ++i) { @@ -396,24 +359,28 @@ namespace mpt namespace detail { -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); } +static MPT_CONSTEXPR20_FUN uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } +static MPT_CONSTEXPR20_FUN int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } // Do NOT remove these overloads, even if they seem useless. // We do not want risking to extend 8bit integers to int and then // endian-converting and casting back to int. // Thus these overloads. -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_CONSTEXPR20_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN int8 SwapBytes(int8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return value; } } // namespace detail } // namespace mpt +#undef MPT_constexpr_bswap16 +#undef MPT_constexpr_bswap32 +#undef MPT_constexpr_bswap64 + #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 @@ -422,7 +389,7 @@ static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return // 1.0f --> 0x3f800000u static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(f); } else @@ -454,7 +421,7 @@ static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) } static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(f); } else @@ -488,7 +455,7 @@ static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) // 0x3f800000u --> 1.0f static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(i); } else @@ -514,7 +481,7 @@ static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) } static MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(i); } else @@ -545,14 +512,14 @@ template struct IEEE754binary32Emulated { public: - typedef IEEE754binary32Emulated self_t; - mpt::byte bytes[4]; + using self_t = IEEE754binary32Emulated; + std::byte bytes[4]; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } - MPT_FORCEINLINE IEEE754binary32Emulated() { } + IEEE754binary32Emulated() = default; MPT_FORCEINLINE explicit IEEE754binary32Emulated(float32 f) { SetInt32(EncodeIEEE754binary32(f)); @@ -560,7 +527,7 @@ public: // b0...b3 are in memory order, i.e. depend on the endianness of this type // little endian: (0x00,0x00,0x80,0x3f) // big endian: (0x3f,0x80,0x00,0x00) - MPT_FORCEINLINE explicit IEEE754binary32Emulated(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3) + MPT_FORCEINLINE explicit IEEE754binary32Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { bytes[0] = b0; bytes[1] = b1; @@ -573,10 +540,10 @@ public: } MPT_FORCEINLINE self_t & SetInt32(uint32 i) { - bytes[hihi] = static_cast(i >> 24); - bytes[hilo] = static_cast(i >> 16); - bytes[lohi] = static_cast(i >> 8); - bytes[lolo] = static_cast(i >> 0); + bytes[hihi] = static_cast(i >> 24); + bytes[hilo] = static_cast(i >> 16); + bytes[lohi] = static_cast(i >> 8); + bytes[lolo] = static_cast(i >> 0); return *this; } MPT_FORCEINLINE uint32 GetInt32() const @@ -606,19 +573,19 @@ template self_t; - mpt::byte bytes[8]; + using self_t = IEEE754binary64Emulated; + std::byte bytes[8]; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } - MPT_FORCEINLINE IEEE754binary64Emulated() { } + IEEE754binary64Emulated() = default; MPT_FORCEINLINE explicit IEEE754binary64Emulated(float64 f) { SetInt64(EncodeIEEE754binary64(f)); } - MPT_FORCEINLINE explicit IEEE754binary64Emulated(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3, mpt::byte b4, mpt::byte b5, mpt::byte b6, mpt::byte b7) + MPT_FORCEINLINE explicit IEEE754binary64Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { bytes[0] = b0; bytes[1] = b1; @@ -635,14 +602,14 @@ public: } MPT_FORCEINLINE self_t & SetInt64(uint64 i) { - bytes[hihihi] = static_cast(i >> 56); - bytes[hihilo] = static_cast(i >> 48); - bytes[hilohi] = static_cast(i >> 40); - bytes[hilolo] = static_cast(i >> 32); - bytes[lohihi] = static_cast(i >> 24); - bytes[lohilo] = static_cast(i >> 16); - bytes[lolohi] = static_cast(i >> 8); - bytes[lololo] = static_cast(i >> 0); + bytes[hihihi] = static_cast(i >> 56); + bytes[hihilo] = static_cast(i >> 48); + bytes[hilohi] = static_cast(i >> 40); + bytes[hilolo] = static_cast(i >> 32); + bytes[lohihi] = static_cast(i >> 24); + bytes[lohilo] = static_cast(i >> 16); + bytes[lolohi] = static_cast(i >> 8); + bytes[lololo] = static_cast(i >> 0); return *this; } MPT_FORCEINLINE uint64 GetInt64() const @@ -677,10 +644,10 @@ public: } }; -typedef IEEE754binary32Emulated<0,1,2,3> IEEE754binary32EmulatedBE; -typedef IEEE754binary32Emulated<3,2,1,0> IEEE754binary32EmulatedLE; -typedef IEEE754binary64Emulated<0,1,2,3,4,5,6,7> IEEE754binary64EmulatedBE; -typedef IEEE754binary64Emulated<7,6,5,4,3,2,1,0> IEEE754binary64EmulatedLE; +using IEEE754binary32EmulatedBE = IEEE754binary32Emulated<0,1,2,3>; +using IEEE754binary32EmulatedLE = IEEE754binary32Emulated<3,2,1,0>; +using IEEE754binary64EmulatedBE = IEEE754binary64Emulated<0,1,2,3,4,5,6,7>; +using IEEE754binary64EmulatedLE = IEEE754binary64Emulated<7,6,5,4,3,2,1,0>; MPT_BINARY_STRUCT(IEEE754binary32EmulatedBE, 4) MPT_BINARY_STRUCT(IEEE754binary32EmulatedLE, 4) @@ -693,19 +660,19 @@ struct IEEE754binary32Native public: float32 value; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { - return static_cast(EncodeIEEE754binary32(value) >> (i*8)); + return static_cast(EncodeIEEE754binary32(value) >> (i*8)); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { - return static_cast(EncodeIEEE754binary32(value) >> ((4-1-i)*8)); + return static_cast(EncodeIEEE754binary32(value) >> ((4-1-i)*8)); } } - MPT_FORCEINLINE IEEE754binary32Native() { } + IEEE754binary32Native() = default; MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) { value = f; @@ -713,10 +680,10 @@ public: // b0...b3 are in memory order, i.e. depend on the endianness of this type // little endian: (0x00,0x00,0x80,0x3f) // big endian: (0x3f,0x80,0x00,0x00) - MPT_FORCEINLINE explicit IEEE754binary32Native(mpt::byte b0, mpt::byte b1, mpt::byte b2, mpt::byte b3) + MPT_FORCEINLINE explicit IEEE754binary32Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 0) @@ -725,7 +692,7 @@ public: | (static_cast(b3) << 24) ); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 24) @@ -764,27 +731,27 @@ struct IEEE754binary64Native public: float64 value; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { - return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> (i*8))); + return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> (i*8))); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { - return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> ((8-1-i)*8))); + return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> ((8-1-i)*8))); } } - MPT_FORCEINLINE IEEE754binary64Native() { } + IEEE754binary64Native() = default; MPT_FORCEINLINE explicit IEEE754binary64Native(float64 f) { value = f; } - 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) + MPT_FORCEINLINE explicit IEEE754binary64Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 0) @@ -797,7 +764,7 @@ public: | (static_cast(b7) << 56) ); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 56) @@ -834,68 +801,68 @@ public: } }; -MPT_STATIC_ASSERT((sizeof(IEEE754binary32Native<>) == 4)); -MPT_STATIC_ASSERT((sizeof(IEEE754binary64Native<>) == 8)); +static_assert((sizeof(IEEE754binary32Native<>) == 4)); +static_assert((sizeof(IEEE754binary64Native<>) == 8)); namespace mpt { template <> struct is_binary_safe< IEEE754binary32Native<> > : public std::true_type { }; template <> struct is_binary_safe< IEEE754binary64Native<> > : public std::true_type { }; } -template struct IEEE754binary_types { - typedef IEEE754binary32EmulatedLE IEEE754binary32LE; - typedef IEEE754binary32EmulatedBE IEEE754binary32BE; - typedef IEEE754binary64EmulatedLE IEEE754binary64LE; - typedef IEEE754binary64EmulatedBE IEEE754binary64BE; +template struct IEEE754binary_types { + using IEEE754binary32LE = IEEE754binary32EmulatedLE; + using IEEE754binary32BE = IEEE754binary32EmulatedBE; + using IEEE754binary64LE = IEEE754binary64EmulatedLE; + using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { - typedef IEEE754binary32Native<> IEEE754binary32LE; - typedef IEEE754binary32EmulatedBE IEEE754binary32BE; - typedef IEEE754binary64Native<> IEEE754binary64LE; - typedef IEEE754binary64EmulatedBE IEEE754binary64BE; + using IEEE754binary32LE = IEEE754binary32Native<>; + using IEEE754binary32BE = IEEE754binary32EmulatedBE; + using IEEE754binary64LE = IEEE754binary64Native<>; + using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { - typedef IEEE754binary32EmulatedLE IEEE754binary32LE; - typedef IEEE754binary32Native<> IEEE754binary32BE; - typedef IEEE754binary64EmulatedLE IEEE754binary64LE; - typedef IEEE754binary64Native<> IEEE754binary64BE; + using IEEE754binary32LE = IEEE754binary32EmulatedLE; + using IEEE754binary32BE = IEEE754binary32Native<>; + using IEEE754binary64LE = IEEE754binary64EmulatedLE; + using IEEE754binary64BE = IEEE754binary64Native<>; }; -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; +using IEEE754binary32LE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32LE; +using IEEE754binary32BE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32BE; +using IEEE754binary64LE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64LE; +using IEEE754binary64BE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64BE; -STATIC_ASSERT(sizeof(IEEE754binary32LE) == 4); -STATIC_ASSERT(sizeof(IEEE754binary32BE) == 4); -STATIC_ASSERT(sizeof(IEEE754binary64LE) == 8); -STATIC_ASSERT(sizeof(IEEE754binary64BE) == 8); +static_assert(sizeof(IEEE754binary32LE) == 4); +static_assert(sizeof(IEEE754binary32BE) == 4); +static_assert(sizeof(IEEE754binary64LE) == 8); +static_assert(sizeof(IEEE754binary64BE) == 8); // unaligned -typedef IEEE754binary32EmulatedLE float32le; -typedef IEEE754binary32EmulatedBE float32be; -typedef IEEE754binary64EmulatedLE float64le; -typedef IEEE754binary64EmulatedBE float64be; +using float32le = IEEE754binary32EmulatedLE; +using float32be = IEEE754binary32EmulatedBE; +using float64le = IEEE754binary64EmulatedLE; +using float64be = IEEE754binary64EmulatedBE; -STATIC_ASSERT(sizeof(float32le) == 4); -STATIC_ASSERT(sizeof(float32be) == 4); -STATIC_ASSERT(sizeof(float64le) == 8); -STATIC_ASSERT(sizeof(float64be) == 8); +static_assert(sizeof(float32le) == 4); +static_assert(sizeof(float32be) == 4); +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; +using float32le_fast = IEEE754binary32LE; +using float32be_fast = IEEE754binary32BE; +using float64le_fast = IEEE754binary64LE; +using float64be_fast = IEEE754binary64BE; -STATIC_ASSERT(sizeof(float32le_fast) == 4); -STATIC_ASSERT(sizeof(float32be_fast) == 4); -STATIC_ASSERT(sizeof(float64le_fast) == 8); -STATIC_ASSERT(sizeof(float64be_fast) == 8); +static_assert(sizeof(float32le_fast) == 4); +static_assert(sizeof(float32be_fast) == 4); +static_assert(sizeof(float64le_fast) == 8); +static_assert(sizeof(float64be_fast) == 8); @@ -908,54 +875,53 @@ template struct packed { public: - typedef T base_type; - typedef Tendian endian_type; + using base_type = T; + using endian_type = Tendian; public: -#if MPT_ENDIAN_IS_CONSTEXPR - mpt::byte data[sizeof(base_type)]{}; -#else // !MPT_ENDIAN_IS_CONSTEXPR - std::array data; -#endif // MPT_ENDIAN_IS_CONSTEXPR + std::array data; public: - MPT_ENDIAN_CONSTEXPR_FUN void set(base_type val) noexcept + MPT_CONSTEXPR20_FUN void set(base_type val) noexcept { - STATIC_ASSERT(std::numeric_limits::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::big) + static_assert(std::numeric_limits::is_integer); + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { + if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned::type uval = val; for(std::size_t i = 0; i < sizeof(base_type); ++i) { - data[i] = static_cast((uval >> (8*(sizeof(base_type)-1-i))) & 0xffu); + 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); + data[i] = static_cast((uval >> (8*i)) & 0xffu); } } - #else // !MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) + } else + { + if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { - MPT_CONSTANT_IF(mpt::endian::native != endian_type::endian) + if constexpr(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; + using unsigned_base_type = typename std::make_unsigned::type; data = EndianEncode(val); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN base_type get() const noexcept + MPT_CONSTEXPR20_FUN base_type get() const noexcept { - STATIC_ASSERT(std::numeric_limits::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::big) + static_assert(std::numeric_limits::is_integer); + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { + if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned::type uval = 0; for(std::size_t i = 0; i < sizeof(base_type); ++i) @@ -972,57 +938,58 @@ public: } return static_cast(uval); } - #else // !MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) + } else + { + if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { base_type val = base_type(); std::memcpy(&val, data.data(), sizeof(val)); - MPT_CONSTANT_IF(mpt::endian::native != endian_type::endian) + if constexpr(mpt::endian::native != endian_type::endian) { val = mpt::detail::SwapBytes(val); } return val; } else { - typedef typename std::make_unsigned::type unsigned_base_type; + using unsigned_base_type = typename std::make_unsigned::type; return EndianDecode(data); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - 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(); } + MPT_CONSTEXPR20_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } + MPT_CONSTEXPR20_FUN operator base_type () const noexcept { return get(); } public: - MPT_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 + MPT_CONSTEXPR20_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix + MPT_CONSTEXPR20_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix }; -typedef packed< int64, LittleEndian_tag> int64le; -typedef packed< int32, LittleEndian_tag> int32le; -typedef packed< int16, LittleEndian_tag> int16le; -typedef packed< int8 , LittleEndian_tag> int8le; -typedef packed uint64le; -typedef packed uint32le; -typedef packed uint16le; -typedef packed uint8le; +using int64le = packed< int64, LittleEndian_tag>; +using int32le = packed< int32, LittleEndian_tag>; +using int16le = packed< int16, LittleEndian_tag>; +using int8le = packed< int8 , LittleEndian_tag>; +using uint64le = packed; +using uint32le = packed; +using uint16le = packed; +using uint8le = packed; -typedef packed< int64, BigEndian_tag> int64be; -typedef packed< int32, BigEndian_tag> int32be; -typedef packed< int16, BigEndian_tag> int16be; -typedef packed< int8 , BigEndian_tag> int8be; -typedef packed uint64be; -typedef packed uint32be; -typedef packed uint16be; -typedef packed uint8be; +using int64be = packed< int64, BigEndian_tag>; +using int32be = packed< int32, BigEndian_tag>; +using int16be = packed< int16, BigEndian_tag>; +using int8be = packed< int8 , BigEndian_tag>; +using uint64be = packed; +using uint32be = packed; +using uint16be = packed; +using uint8be = packed; namespace mpt { template struct limits> : mpt::limits {}; @@ -1048,28 +1015,28 @@ MPT_BINARY_STRUCT(uint8be , 1) namespace mpt { -template struct make_le { typedef packed::type, LittleEndian_tag> type; }; -template struct make_be { typedef packed::type, BigEndian_tag> type; }; +template struct make_le { using type = packed::type, LittleEndian_tag>; }; +template struct make_be { using type = packed::type, BigEndian_tag>; }; template -MPT_ENDIAN_CONSTEXPR_FUN auto as_le(T v) noexcept -> typename mpt::make_le::type>::type +MPT_CONSTEXPR20_FUN auto as_le(T v) noexcept -> typename mpt::make_le::type>::type { - typename mpt::make_le::type>::type res; + 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 +MPT_CONSTEXPR20_FUN auto as_be(T v) noexcept -> typename mpt::make_be::type>::type { - typename mpt::make_be::type>::type res; + 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 +MPT_CONSTEXPR20_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept { - Tpacked res; + Tpacked res{}; res = v; return res; } @@ -1111,108 +1078,11 @@ struct int24 } } }; -MPT_STATIC_ASSERT(sizeof(int24) == 3); -#define int24_min (0-0x00800000) -#define int24_max (0+0x007fffff) +static_assert(sizeof(int24) == 3); +static constexpr int32 int24_min = (0 - 0x00800000); +static constexpr int32 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. -template -class const_unaligned_ptr_le -{ -public: - typedef T value_type; -private: - const mpt::byte *mem; - value_type Read() const - { - 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; } - 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; } - const_unaligned_ptr_le & operator -- () { mem -= sizeof(value_type); return *this; } - const_unaligned_ptr_le operator ++ (int) { const_unaligned_ptr_le result = *this; ++result; return result; } - const_unaligned_ptr_le operator -- (int) { const_unaligned_ptr_le result = *this; --result; return result; } - const_unaligned_ptr_le operator + (std::size_t count) const { const_unaligned_ptr_le result = *this; result += count; return result; } - const_unaligned_ptr_le operator - (std::size_t count) const { const_unaligned_ptr_le result = *this; result -= count; return result; } - const value_type operator * () const { return Read(); } - const value_type operator [] (std::size_t i) const { return *((*this) + i); } - operator bool () const { return mem != nullptr; } -}; - -template -class const_unaligned_ptr_be -{ -public: - typedef T value_type; -private: - const mpt::byte *mem; - value_type Read() const - { - 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; } - 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; } - const_unaligned_ptr_be & operator -- () { mem -= sizeof(value_type); return *this; } - const_unaligned_ptr_be operator ++ (int) { const_unaligned_ptr_be result = *this; ++result; return result; } - const_unaligned_ptr_be operator -- (int) { const_unaligned_ptr_be result = *this; --result; return result; } - const_unaligned_ptr_be operator + (std::size_t count) const { const_unaligned_ptr_be result = *this; result += count; return result; } - const_unaligned_ptr_be operator - (std::size_t count) const { const_unaligned_ptr_be result = *this; result -= count; return result; } - const value_type operator * () const { return Read(); } - const value_type operator [] (std::size_t i) const { return *((*this) + i); } - operator bool () const { return mem != nullptr; } -}; - -template -class const_unaligned_ptr -{ -public: - typedef T value_type; -private: - const mpt::byte *mem; - value_type Read() const - { - value_type val = value_type(); - std::memcpy(&val, mem, sizeof(value_type)); - return val; - } -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; } - 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; } - const_unaligned_ptr & operator -- () { mem -= sizeof(value_type); return *this; } - const_unaligned_ptr operator ++ (int) { const_unaligned_ptr result = *this; ++result; return result; } - const_unaligned_ptr operator -- (int) { const_unaligned_ptr result = *this; --result; return result; } - const_unaligned_ptr operator + (std::size_t count) const { const_unaligned_ptr result = *this; result += count; return result; } - const_unaligned_ptr operator - (std::size_t count) const { const_unaligned_ptr result = *this; result -= count; return result; } - const value_type operator * () const { return Read(); } - const value_type operator [] (std::size_t i) const { return *((*this) + i); } - operator bool () const { return mem != nullptr; } -}; - - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReader.h b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h index d85fce384..20772bc2c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FileReader.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h @@ -29,7 +29,7 @@ OPENMPT_NAMESPACE_BEGIN // change to show warnings for functions which trigger pre-caching the whole file for unseekable streams -//#define FILEREADER_DEPRECATED MPT_DEPRECATED +//#define FILEREADER_DEPRECATED [[deprecated]] #define FILEREADER_DEPRECATED @@ -38,12 +38,12 @@ class FileReaderTraitsMemory public: - typedef FileDataContainerMemory::off_t off_t; + using off_t = FileDataContainerMemory::off_t; - typedef FileDataContainerMemory data_type; - typedef const FileDataContainerMemory & ref_data_type; - typedef const FileDataContainerMemory & shared_data_type; - typedef FileDataContainerMemory value_data_type; + using data_type = FileDataContainerMemory; + using ref_data_type = const FileDataContainerMemory &; + using shared_data_type = const FileDataContainerMemory &; + using value_data_type = FileDataContainerMemory; static shared_data_type get_shared(const data_type & data) { return data; } static ref_data_type get_ref(const data_type & data) { return data; } @@ -58,19 +58,17 @@ public: }; -#if defined(MPT_FILEREADER_STD_ISTREAM) - class FileReaderTraitsStdStream { public: - typedef IFileDataContainer::off_t off_t; + using off_t = IFileDataContainer::off_t; - typedef std::shared_ptr data_type; - typedef const IFileDataContainer & ref_data_type; - typedef std::shared_ptr shared_data_type; - typedef std::shared_ptr value_data_type; + using data_type = std::shared_ptr; + using ref_data_type = const IFileDataContainer &; + using shared_data_type = std::shared_ptr; + using value_data_type = std::shared_ptr; static shared_data_type get_shared(const data_type & data) { return data; } static ref_data_type get_ref(const data_type & data) { return *data; } @@ -85,13 +83,647 @@ public: }; -typedef FileReaderTraitsStdStream FileReaderTraitsDefault; +using FileReaderTraitsDefault = FileReaderTraitsStdStream; -#else // !MPT_FILEREADER_STD_ISTREAM +namespace mpt +{ +namespace FileReader +{ -typedef FileReaderTraitsMemory FileReaderTraitsDefault; + // Read a "T" object from the stream. + // If not enough bytes can be read, false is returned. + // If successful, the file cursor is advanced by the size of "T". + template + bool Read(TFileCursor &f, T &target) + { + // cppcheck false-positive + // cppcheck-suppress uninitvar + mpt::byte_span dst = mpt::as_raw_memory(target); + if(dst.size() != f.GetRaw(dst)) + { + return false; + } + f.Skip(dst.size()); + return true; + } -#endif // MPT_FILEREADER_STD_ISTREAM + // Read some kind of integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + T ReadIntLE(TFileCursor &f) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + typename mpt::make_le::type target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read some kind of integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + T ReadIntBE(TFileCursor &f) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + typename mpt::make_be::type target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read a integer in little-endian format which has some of its higher bytes not stored in file. + // If successful, the file cursor is advanced by the given size. + template + T ReadTruncatedIntLE(TFileCursor &f, typename TFileCursor::off_t size) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + MPT_ASSERT(sizeof(T) >= size); + if(size == 0) + { + return 0; + } + if(!f.CanRead(size)) + { + return 0; + } + uint8 buf[sizeof(T)]; + bool negative = false; + for(std::size_t i = 0; i < sizeof(T); ++i) + { + uint8 byte = 0; + if(i < size) + { + Read(f, byte); + negative = std::numeric_limits::is_signed && ((byte & 0x80) != 0x00); + } else + { + // sign or zero extend + byte = negative ? 0xff : 0x00; + } + buf[i] = byte; + } + typename mpt::make_le::type target; + std::memcpy(&target, buf, sizeof(T)); + return target; + } + + // Read a supplied-size little endian integer to a fixed size variable. + // The data is properly sign-extended when fewer bytes are stored. + // If more bytes are stored, higher order bytes are silently ignored. + // If successful, the file cursor is advanced by the given size. + template + T ReadSizedIntLE(TFileCursor &f, typename TFileCursor::off_t size) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + if(size == 0) + { + return 0; + } + if(!f.CanRead(size)) + { + return 0; + } + if(size < sizeof(T)) + { + return ReadTruncatedIntLE(f, size); + } + T retval = ReadIntLE(f); + f.Skip(size - sizeof(T)); + return retval; + } + + // Read unsigned 32-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint32 ReadUint32LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read unsigned 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint32 ReadUint32BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read signed 32-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int32 ReadInt32LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read signed 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int32 ReadInt32BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read unsigned 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint16 ReadUint16LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read unsigned 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint16 ReadUint16BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read signed 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int16 ReadInt16LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read signed 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int16 ReadInt16BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read a single 8bit character. + // If successful, the file cursor is advanced by the size of the integer. + template + char ReadChar(TFileCursor &f) + { + char target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read unsigned 8-Bit integer. + // If successful, the file cursor is advanced by the size of the integer. + template + uint8 ReadUint8(TFileCursor &f) + { + uint8 target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read signed 8-Bit integer. If successful, the file cursor is advanced by the size of the integer. + template + int8 ReadInt8(TFileCursor &f) + { + int8 target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read 32-Bit float in little-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + float ReadFloatLE(TFileCursor &f) + { + IEEE754binary32LE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read 32-Bit float in big-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + float ReadFloatBE(TFileCursor &f) + { + IEEE754binary32BE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read 64-Bit float in little-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + double ReadDoubleLE(TFileCursor &f) + { + IEEE754binary64LE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0; + } + } + + // Read 64-Bit float in big-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + double ReadDoubleBE(TFileCursor &f) + { + IEEE754binary64BE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0; + } + } + + // Read a struct. + // If successful, the file cursor is advanced by the size of the struct. Otherwise, the target is zeroed. + template + bool ReadStruct(TFileCursor &f, T &target) + { + static_assert(mpt::is_binary_safe::value); + if(Read(f, target)) + { + return true; + } else + { + Clear(target); + return false; + } + } + + // Allow to read a struct partially (if there's less memory available than the struct's size, fill it up with zeros). + // The file cursor is advanced by "partialSize" bytes. + template + typename TFileCursor::off_t ReadStructPartial(TFileCursor &f, T &target, typename TFileCursor::off_t partialSize = sizeof(T)) + { + static_assert(mpt::is_binary_safe::value); + typename TFileCursor::off_t copyBytes = std::min(partialSize, sizeof(T)); + if(!f.CanRead(copyBytes)) + { + copyBytes = f.BytesLeft(); + } + f.GetRaw(mpt::as_raw_memory(target).data(), copyBytes); + std::memset(mpt::as_raw_memory(target).data() + copyBytes, 0, sizeof(target) - copyBytes); + f.Skip(partialSize); + return copyBytes; + } + + // Read a string of length srcSize into fixed-length char array destBuffer using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one byte could be read or 0 bytes were requested. + template + bool ReadString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::off_t srcSize) + { + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + mpt::String::WriteAutoBuf(destBuffer) = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string of length srcSize into a std::string dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t srcSize) + { + dest.clear(); + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string of length srcSize into a mpt::charbuf dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::off_t srcSize) + { + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(TFileCursor &f, mpt::ustring &dest, mpt::Charset charset, const typename TFileCursor::off_t srcSize) + { + dest.clear(); + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::ToUnicode(charset, mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize)); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(f, srcSize)) + return false; + return ReadString(f, destBuffer, std::min(static_cast(srcSize), maxLength)); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(f, srcSize)) + return false; + return ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a mpt::charbuf dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(f, srcSize)) + return false; + return ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); + } + + // Read a null-terminated string into a std::string + template + bool ReadNullString(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!f.CanRead(1)) + return false; + try + { + char buffer[64]; + typename TFileCursor::off_t avail = 0; + while((avail = std::min(f.GetRaw(buffer, std::size(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find(buffer, buffer + avail, '\0'); + dest.insert(dest.end(), buffer, end); + f.Skip(end - buffer); + if(end < buffer + avail) + { + // Found null char + f.Skip(1); + break; + } + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + return dest.length() != 0; + } + + // Read a string up to the next line terminator into a std::string + template + bool ReadLine(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!f.CanRead(1)) + return false; + try + { + char buffer[64]; + char c = '\0'; + typename TFileCursor::off_t avail = 0; + while((avail = std::min(f.GetRaw(buffer, std::size(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find_if(buffer, buffer + avail, mpt::String::Traits::IsLineEnding); + dest.insert(dest.end(), buffer, end); + f.Skip(end - buffer); + if(end < buffer + avail) + { + // Found line ending + f.Skip(1); + // Handle CRLF line ending + if(*end == '\r') + { + if(Read(f, c) && c != '\n') + f.SkipBack(1); + } + break; + } + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + return true; + } + + // Read an array of binary-safe T values. + // If successful, the file cursor is advanced by the size of the array. + // Otherwise, the target is zeroed. + template + bool ReadArray(TFileCursor &f, T (&destArray)[destSize]) + { + static_assert(mpt::is_binary_safe::value); + if(f.CanRead(sizeof(destArray))) + { + for(auto &element : destArray) + { + Read(f, element); + } + return true; + } else + { + Clear(destArray); + return false; + } + } + + // Read an array of binary-safe T values. + // If successful, the file cursor is advanced by the size of the array. + // Otherwise, the target is zeroed. + template + bool ReadArray(TFileCursor &f, std::array &destArray) + { + static_assert(mpt::is_binary_safe::value); + if(f.CanRead(sizeof(destArray))) + { + for(auto &element : destArray) + { + Read(f, element); + } + return true; + } else + { + destArray.fill(T()); + return false; + } + } + + // Read destSize elements of binary-safe type T into a vector. + // If successful, the file cursor is advanced by the size of the vector. + // Otherwise, the vector is resized to destSize, but possibly existing contents are not cleared. + template + bool ReadVector(TFileCursor &f, std::vector &destVector, size_t destSize) + { + static_assert(mpt::is_binary_safe::value); + destVector.resize(destSize); + if(f.CanRead(sizeof(T) * destSize)) + { + for(auto &element : destVector) + { + Read(f, element); + } + return true; + } else + { + return false; + } + } + + template + std::array ReadArray(TFileCursor &f) + { + std::array destArray; + ReadArray(f, destArray); + return destArray; + } + + // Compare a magic string with the current stream position. + // Returns true if they are identical and advances the file cursor by the the length of the "magic" string. + // Returns false if the string could not be found. The file cursor is not advanced in this case. + template + bool ReadMagic(TFileCursor &f, const char *const magic, typename TFileCursor::off_t magicLength) + { + std::byte buffer[16] = { std::byte(0) }; + typename TFileCursor::off_t bytesRead = 0; + typename TFileCursor::off_t bytesRemain = magicLength; + while(bytesRemain) + { + typename TFileCursor::off_t numBytes = std::min(static_cast(sizeof(buffer)), bytesRemain); + if(f.GetRawWithOffset(bytesRead, buffer, numBytes) != numBytes) + return false; + if(memcmp(buffer, magic + bytesRead, numBytes)) + return false; + bytesRemain -= numBytes; + bytesRead += numBytes; + } + f.Skip(magicLength); + return true; + } + template + bool ReadMagic(TFileCursor &f, const char (&magic)[N]) + { + MPT_ASSERT(magic[N - 1] == '\0'); + for(std::size_t i = 0; i < N - 1; ++i) + { + MPT_ASSERT(magic[i] != '\0'); + } + return ReadMagic(f, static_cast(magic), static_cast(N - 1)); + } + + // Read variable-length unsigned integer (as found in MIDI files). + // If successful, the file cursor is advanced by the size of the integer and true is returned. + // False is returned if not enough bytes were left to finish reading of the integer or if an overflow happened (source doesn't fit into target integer). + // In case of an overflow, the target is also set to the maximum value supported by its data type. + template + bool ReadVarInt(TFileCursor &f, T &target) + { + static_assert(std::numeric_limits::is_integer == true + && std::numeric_limits::is_signed == false, + "Target type is not an unsigned integer"); + + if(f.NoBytesLeft()) + { + target = 0; + return false; + } + + std::byte bytes[16]; // More than enough for any valid VarInt + typename TFileCursor::off_t avail = f.GetRaw(bytes, sizeof(bytes)); + typename TFileCursor::off_t readPos = 1; + + size_t writtenBits = 0; + uint8 b = mpt::byte_cast(bytes[0]); + target = (b & 0x7F); + + // Count actual bits used in most significant byte (i.e. this one) + for(size_t bit = 0; bit < 7; bit++) + { + if((b & (1u << bit)) != 0) + { + writtenBits = bit + 1; + } + } + + while(readPos < avail && (b & 0x80) != 0) + { + b = mpt::byte_cast(bytes[readPos++]); + target <<= 7; + target |= (b & 0x7F); + writtenBits += 7; + if(readPos == avail) + { + f.Skip(readPos); + avail = f.GetRaw(bytes, sizeof(bytes)); + readPos = 0; + } + } + f.Skip(readPos); + + if(writtenBits > sizeof(target) * 8u) + { + // Overflow + target = Util::MaxValueOfType(target); + return false; + } else if((b & 0x80) != 0) + { + // Reached EOF + return false; + } + return true; + } + +} // namespace FileReader +} // namespace mpt + +namespace FR = mpt::FileReader; namespace detail { @@ -101,16 +733,16 @@ class FileReader private: - typedef Ttraits traits_type; + using traits_type = Ttraits; public: - typedef typename traits_type::off_t off_t; + using off_t = typename traits_type::off_t; - typedef typename traits_type::data_type data_type; - typedef typename traits_type::ref_data_type ref_data_type; - typedef typename traits_type::shared_data_type shared_data_type; - typedef typename traits_type::value_data_type value_data_type; + using data_type = typename traits_type::data_type; + using ref_data_type = typename traits_type::ref_data_type; + using shared_data_type = typename traits_type::shared_data_type; + using value_data_type = typename traits_type::value_data_type; protected: @@ -315,8 +947,8 @@ public: { private: std::size_t size_; - const mpt::byte *pinnedData; - std::vector cache; + const std::byte *pinnedData; + std::vector cache; private: void Init(const FileReader &file, std::size_t size) { @@ -386,13 +1018,13 @@ public: } } mpt::const_byte_span span() const { return GetSpan(); } - void invalidate() { size_ = 0; pinnedData = nullptr; cache = std::vector(); } - const mpt::byte *data() const { return span().data(); } + void invalidate() { size_ = 0; pinnedData = nullptr; cache = std::vector(); } + const std::byte *data() const { return span().data(); } std::size_t size() const { return size_; } - mpt::const_byte_span::iterator begin() const { return span().begin(); } - mpt::const_byte_span::iterator end() const { return span().end(); } - mpt::const_byte_span::const_iterator cbegin() const { return span().cbegin(); } - mpt::const_byte_span::const_iterator cend() const { return span().cend(); } + mpt::const_byte_span::pointer begin() const { return span().data(); } + mpt::const_byte_span::pointer end() const { return span().data() + span().size(); } + mpt::const_byte_span::const_pointer cbegin() const { return span().data(); } + mpt::const_byte_span::const_pointer cend() const { return span().data() + span().size(); } }; // Returns a pinned view into the remaining raw data from cursor position. @@ -422,7 +1054,7 @@ public: // Returns raw stream data at cursor position. // Should only be used if absolutely necessary, for example for sample reading, or when used with a small chunk of the file retrieved by ReadChunk(). // Use GetPinnedRawDataView(size) whenever possible. - FILEREADER_DEPRECATED const mpt::byte *GetRawData() const + FILEREADER_DEPRECATED const std::byte *GetRawData() const { // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file return DataContainer().GetRawData() + streamPos; @@ -437,670 +1069,300 @@ public: 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)); + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos + offset, count)); + } + std::size_t GetRawWithOffset(std::size_t offset, mpt::byte_span dst) const + { + return static_cast(DataContainer().Read(streamPos + offset, dst)); } template std::size_t GetRaw(T *dst, std::size_t count) const { - return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + } + std::size_t GetRaw(mpt::byte_span dst) const + { + return static_cast(DataContainer().Read(streamPos, dst)); } template std::size_t ReadRaw(T *dst, std::size_t count) { - std::size_t result = static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + std::size_t result = static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + streamPos += result; + return result; + } + std::size_t ReadRaw(mpt::byte_span dst) + { + std::size_t result = static_cast(DataContainer().Read(streamPos, dst)); streamPos += result; return result; } - std::vector GetRawDataAsByteVector() const + std::vector GetRawDataAsByteVector() const { PinnedRawDataView view = GetPinnedRawDataView(); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } - std::vector ReadRawDataAsByteVector() + std::vector ReadRawDataAsByteVector() { PinnedRawDataView view = ReadPinnedRawDataView(); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } - std::vector GetRawDataAsByteVector(std::size_t size) const + std::vector GetRawDataAsByteVector(std::size_t size) const { PinnedRawDataView view = GetPinnedRawDataView(size); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } - std::vector ReadRawDataAsByteVector(std::size_t size) + std::vector ReadRawDataAsByteVector(std::size_t size) { PinnedRawDataView view = ReadPinnedRawDataView(size); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } std::string GetRawDataAsString() const { PinnedRawDataView view = GetPinnedRawDataView(); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); } std::string ReadRawDataAsString() { PinnedRawDataView view = ReadPinnedRawDataView(); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); } std::string GetRawDataAsString(std::size_t size) const { PinnedRawDataView view = GetPinnedRawDataView(size); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); } std::string ReadRawDataAsString(std::size_t size) { PinnedRawDataView view = ReadPinnedRawDataView(size); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); } -protected: - - // Read a "T" object from the stream. - // If not enough bytes can be read, false is returned. - // If successful, the file cursor is advanced by the size of "T". template bool Read(T &target) { - mpt::byte_span dst = mpt::as_raw_memory(target); - if(dst.size() != DataContainer().Read(streamPos, dst)) - { - return false; - } - streamPos += dst.size(); - return true; + return mpt::FileReader::Read(*this, target); } -public: - - // Read some kind of integer in little-endian format. - // If successful, the file cursor is advanced by the size of the integer. template T ReadIntLE() { - static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); - typename mpt::make_le::type target; - if(Read(target)) - { - return target; - } else - { - return 0; - } + return mpt::FileReader::ReadIntLE(*this); } - // Read some kind of integer in big-endian format. - // If successful, the file cursor is advanced by the size of the integer. template T ReadIntBE() { - static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); - typename mpt::make_be::type target; - if(Read(target)) - { - return target; - } else - { - return 0; - } + return mpt::FileReader::ReadIntLE(*this); } - // Read a integer in little-endian format which has some of its higher bytes not stored in file. - // If successful, the file cursor is advanced by the given size. template T ReadTruncatedIntLE(off_t size) { - static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); - MPT_ASSERT(sizeof(T) >= size); - if(size == 0) - { - return 0; - } - if(!CanRead(size)) - { - return 0; - } - uint8 buf[sizeof(T)]; - bool negative = false; - for(std::size_t i = 0; i < sizeof(T); ++i) - { - uint8 byte = 0; - if(i < size) - { - Read(byte); - negative = std::numeric_limits::is_signed && ((byte & 0x80) != 0x00); - } else - { - // sign or zero extend - byte = negative ? 0xff : 0x00; - } - buf[i] = byte; - } - typename mpt::make_le::type target; - std::memcpy(&target, buf, sizeof(T)); - return target; + return mpt::FileReader::ReadTruncatedIntLE(*this, size); } - // Read a supplied-size little endian integer to a fixed size variable. - // The data is properly sign-extended when fewer bytes are stored. - // If more bytes are stored, higher order bytes are silently ignored. - // If successful, the file cursor is advanced by the given size. template T ReadSizedIntLE(off_t size) { - static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); - if(size == 0) - { - return 0; - } - if(!CanRead(size)) - { - return 0; - } - if(size < sizeof(T)) - { - return ReadTruncatedIntLE(size); - } - T retval = ReadIntLE(); - Skip(size - sizeof(T)); - return retval; + return mpt::FileReader::ReadSizedIntLE(*this, size); } - // Read unsigned 32-Bit integer in little-endian format. - // If successful, the file cursor is advanced by the size of the integer. uint32 ReadUint32LE() { - return ReadIntLE(); + return mpt::FileReader::ReadUint32LE(*this); } - // Read unsigned 32-Bit integer in big-endian format. - // If successful, the file cursor is advanced by the size of the integer. uint32 ReadUint32BE() { - return ReadIntBE(); + return mpt::FileReader::ReadUint32BE(*this); } - // Read signed 32-Bit integer in little-endian format. - // If successful, the file cursor is advanced by the size of the integer. int32 ReadInt32LE() { - return ReadIntLE(); + return mpt::FileReader::ReadInt32LE(*this); } - // Read signed 32-Bit integer in big-endian format. - // If successful, the file cursor is advanced by the size of the integer. int32 ReadInt32BE() { - return ReadIntBE(); + return mpt::FileReader::ReadInt32BE(*this); } - // Read unsigned 16-Bit integer in little-endian format. - // If successful, the file cursor is advanced by the size of the integer. uint16 ReadUint16LE() { - return ReadIntLE(); + return mpt::FileReader::ReadUint16LE(*this); } - // Read unsigned 16-Bit integer in big-endian format. - // If successful, the file cursor is advanced by the size of the integer. uint16 ReadUint16BE() { - return ReadIntBE(); + return mpt::FileReader::ReadUint16BE(*this); } - // Read signed 16-Bit integer in little-endian format. - // If successful, the file cursor is advanced by the size of the integer. int16 ReadInt16LE() { - return ReadIntLE(); + return mpt::FileReader::ReadInt16LE(*this); } - // Read signed 16-Bit integer in big-endian format. - // If successful, the file cursor is advanced by the size of the integer. int16 ReadInt16BE() { - return ReadIntBE(); + return mpt::FileReader::ReadInt16BE(*this); } - // 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; - } + return mpt::FileReader::ReadChar(*this); } - // Read unsigned 8-Bit integer. - // If successful, the file cursor is advanced by the size of the integer. uint8 ReadUint8() { - uint8 target; - if(Read(target)) - { - return target; - } else - { - return 0; - } + return mpt::FileReader::ReadUint8(*this); } - // Read signed 8-Bit integer. If successful, the file cursor is advanced by the size of the integer. int8 ReadInt8() { - int8 target; - if(Read(target)) - { - return target; - } else - { - return 0; - } + return mpt::FileReader::ReadInt8(*this); } - // Read 32-Bit float in little-endian format. - // If successful, the file cursor is advanced by the size of the float. float ReadFloatLE() { - IEEE754binary32LE target; - if(Read(target)) - { - return target; - } else - { - return 0.0f; - } + return mpt::FileReader::ReadFloatLE(*this); } - // Read 32-Bit float in big-endian format. - // If successful, the file cursor is advanced by the size of the float. float ReadFloatBE() { - IEEE754binary32BE target; - if(Read(target)) - { - return target; - } else - { - return 0.0f; - } + return mpt::FileReader::ReadFloatBE(*this); } - // Read 64-Bit float in little-endian format. - // If successful, the file cursor is advanced by the size of the float. double ReadDoubleLE() { - IEEE754binary64LE target; - if(Read(target)) - { - return target; - } else - { - return 0.0; - } + return mpt::FileReader::ReadDoubleLE(*this); } - // Read 64-Bit float in big-endian format. - // If successful, the file cursor is advanced by the size of the float. double ReadDoubleBE() { - IEEE754binary64BE target; - if(Read(target)) - { - return target; - } else - { - return 0.0; - } + return mpt::FileReader::ReadDoubleBE(*this); } - // Read a struct. - // If successful, the file cursor is advanced by the size of the struct. Otherwise, the target is zeroed. template bool ReadStruct(T &target) { - STATIC_ASSERT(mpt::is_binary_safe::value); - if(Read(target)) - { - return true; - } else - { - Clear(target); - return false; - } + return mpt::FileReader::ReadStruct(*this, target); } - // Allow to read a struct partially (if there's less memory available than the struct's size, fill it up with zeros). - // The file cursor is advanced by "partialSize" bytes. template - bool ReadStructPartial(T &target, off_t partialSize = sizeof(T)) + size_t ReadStructPartial(T &target, size_t partialSize = sizeof(T)) { - STATIC_ASSERT(mpt::is_binary_safe::value); - off_t copyBytes = std::min(partialSize, sizeof(T)); - if(!CanRead(copyBytes)) - { - copyBytes = BytesLeft(); - } - 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; + return mpt::FileReader::ReadStructPartial(*this, target, partialSize); } - // Read a string of length srcSize into fixed-length char array destBuffer using a given read mode. - // The file cursor is advanced by "srcSize" bytes. - // Returns true if at least one byte could be read or 0 bytes were requested. template bool ReadString(char (&destBuffer)[destSize], const off_t srcSize) { - FileReader::PinnedRawDataView source = ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. - off_t realSrcSize = source.size(); // In case fewer bytes are available - mpt::String::Read(destBuffer, mpt::byte_cast(source.data()), realSrcSize); - return (realSrcSize > 0 || srcSize == 0); + return mpt::FileReader::ReadString(*this, destBuffer, srcSize); } - // Read a string of length srcSize into a std::string dest using a given read mode. - // The file cursor is advanced by "srcSize" bytes. - // Returns true if at least one character could be read or 0 characters were requested. template bool ReadString(std::string &dest, const off_t srcSize) { - FileReader::PinnedRawDataView source = ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. - off_t realSrcSize = source.size(); // In case fewer bytes are available - mpt::String::Read(dest, mpt::byte_cast(source.data()), realSrcSize); - return (realSrcSize > 0 || srcSize == 0); + return mpt::FileReader::ReadString(*this, dest, srcSize); + } + + template + bool ReadString(mpt::charbuf &dest, const off_t srcSize) + { + return mpt::FileReader::ReadString(*this, dest, srcSize); } - // Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode. - // The file cursor is advanced by "srcSize" bytes. - // Returns true if at least one character could be read or 0 characters were requested. template bool ReadString(mpt::ustring &dest, mpt::Charset charset, const off_t srcSize) { - FileReader::PinnedRawDataView source = ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. - off_t realSrcSize = source.size(); // In case fewer bytes are available - mpt::String::Read(dest, charset, mpt::byte_cast(source.data()), realSrcSize); - return (realSrcSize > 0 || srcSize == 0); + return mpt::FileReader::ReadString(*this, dest, charset, srcSize); } - // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. - // The file cursor is advanced by the string length. - // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. template bool ReadSizedString(char (&destBuffer)[destSize], const off_t maxLength = std::numeric_limits::max()) { - packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs - if(!Read(srcSize)) - return false; - return ReadString(destBuffer, std::min(srcSize, maxLength)); + return mpt::FileReader::ReadSizedString(*this, destBuffer, maxLength); } - // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. - // The file cursor is advanced by the string length. - // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. template bool ReadSizedString(std::string &dest, const off_t maxLength = std::numeric_limits::max()) { - packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs - if(!Read(srcSize)) - return false; - return ReadString(dest, std::min(srcSize, maxLength)); + return mpt::FileReader::ReadSizedString(*this, dest, maxLength); + } + + template + bool ReadSizedString(mpt::charbuf &dest, const off_t maxLength = std::numeric_limits::max()) + { + return mpt::FileReader::ReadSizedString(*this, dest, maxLength); } - // Read a null-terminated string into a std::string bool ReadNullString(std::string &dest, const off_t maxLength = std::numeric_limits::max()) { - dest.clear(); - if(!CanRead(1)) - return false; - try - { - char buffer[64]; - off_t avail = 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); - Skip(end - buffer); - if(end < buffer + avail) - { - // Found null char - Skip(1); - break; - } - } - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); - } - return dest.length() != 0; + return mpt::FileReader::ReadNullString(*this, dest, maxLength); } - // 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()) { - dest.clear(); - if(!CanRead(1)) - return false; - try - { - char buffer[64]; - char c = '\0'; - off_t avail = 0; - while((avail = std::min(GetRaw(buffer, mpt::size(buffer)), maxLength - dest.length())) != 0) - { - auto end = std::find_if(buffer, buffer + avail, mpt::String::Traits::IsLineEnding); - dest.insert(dest.end(), buffer, end); - Skip(end - buffer); - if(end < buffer + avail) - { - // Found line ending - Skip(1); - // Handle CRLF line ending - if(*end == '\r') - { - if(Read(c) && c != '\n') - SkipBack(1); - } - break; - } - } - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); - } - return true; + return mpt::FileReader::ReadLine(*this, dest, maxLength); } - // Read an array of binary-safe T values. - // If successful, the file cursor is advanced by the size of the array. - // Otherwise, the target is zeroed. template bool ReadArray(T (&destArray)[destSize]) { - STATIC_ASSERT(mpt::is_binary_safe::value); - if(CanRead(sizeof(destArray))) - { - for(auto &element : destArray) - { - Read(element); - } - return true; - } else - { - Clear(destArray); - return false; - } + return mpt::FileReader::ReadArray(*this, destArray); } + 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; - } + return mpt::FileReader::ReadArray(*this, destArray); + } + + template + std::array ReadArray() + { + return mpt::FileReader::ReadArray(*this); } - // Read destSize elements of binary-safe type T into a vector. - // If successful, the file cursor is advanced by the size of the vector. - // Otherwise, the vector is resized to destSize, but possibly existing contents are not cleared. template bool ReadVector(std::vector &destVector, size_t destSize) { - STATIC_ASSERT(mpt::is_binary_safe::value); - destVector.resize(destSize); - if(CanRead(sizeof(T) * destSize)) - { - for(auto &element : destVector) - { - Read(element); - } - return true; - } else - { - return false; - } + return mpt::FileReader::ReadVector(*this, destVector, destSize); } - // Compare a magic string with the current stream position. - // Returns true if they are identical and advances the file cursor by the the length of the "magic" string. - // Returns false if the string could not be found. The file cursor is not advanced in this case. template bool ReadMagic(const char (&magic)[N]) { - MPT_ASSERT(magic[N - 1] == '\0'); - for(std::size_t i = 0; i < N - 1; ++i) - { - MPT_ASSERT(magic[i] != '\0'); - } - if(CanRead(N - 1)) - { - mpt::byte bytes[N - 1]; - STATIC_ASSERT(sizeof(bytes) == sizeof(magic) - 1); - GetRaw(bytes, N - 1); - if(!std::memcmp(bytes, magic, N - 1)) - { - Skip(N - 1); - return true; - } - } - return false; + return mpt::FileReader::ReadMagic(*this, magic); } bool ReadMagic(const char *const magic, off_t magicLength) { - if(CanRead(magicLength)) - { - bool identical = true; - for(std::size_t i = 0; i < magicLength; ++i) - { - mpt::byte c = mpt::as_byte(0); - GetRawWithOffset(i, &c, 1); - if(c != mpt::byte_cast(magic[i])) - { - identical = false; - break; - } - } - if(identical) - { - Skip(magicLength); - return true; - } else - { - return false; - } - } else - { - return false; - } + return mpt::FileReader::ReadMagic(*this, magic, magicLength); } - // Read variable-length unsigned integer (as found in MIDI files). - // If successful, the file cursor is advanced by the size of the integer and true is returned. - // False is returned if not enough bytes were left to finish reading of the integer or if an overflow happened (source doesn't fit into target integer). - // In case of an overflow, the target is also set to the maximum value supported by its data type. template bool ReadVarInt(T &target) { - static_assert(std::numeric_limits::is_integer == true - && std::numeric_limits::is_signed == false, - "Target type is not an unsigned integer"); - - if(NoBytesLeft()) - { - target = 0; - return false; - } - - mpt::byte bytes[16]; // More than enough for any valid VarInt - off_t avail = GetRaw(bytes, sizeof(bytes)); - off_t readPos = 1; - - size_t writtenBits = 0; - uint8 b = mpt::byte_cast(bytes[0]); - target = (b & 0x7F); - - // Count actual bits used in most significant byte (i.e. this one) - for(size_t bit = 0; bit < 7; bit++) - { - if((b & (1u << bit)) != 0) - { - writtenBits = bit + 1; - } - } - - while(readPos < avail && (b & 0x80) != 0) - { - b = mpt::byte_cast(bytes[readPos++]); - target <<= 7; - target |= (b & 0x7F); - writtenBits += 7; - if(readPos == avail) - { - Skip(readPos); - avail = GetRaw(bytes, sizeof(bytes)); - readPos = 0; - } - } - Skip(readPos); - - if(writtenBits > sizeof(target) * 8u) - { - // Overflow - target = Util::MaxValueOfType(target); - return false; - } else if((b & 0x80) != 0) - { - // Reached EOF - return false; - } - return true; + return mpt::FileReader::ReadVarInt(*this, target); } }; } // namespace detail -typedef detail::FileReader FileReader; +using FileReader = detail::FileReader; -typedef detail::FileReader MemoryFileReader; +using MemoryFileReader = detail::FileReader; // Initialize file reader object with pointer to data and data length. @@ -1109,8 +1371,6 @@ template static inline FileReader make_FileReader(mpt::span(bytedata), filename); } -#if defined(MPT_FILEREADER_STD_ISTREAM) - #if defined(MPT_FILEREADER_CALLBACK_STREAM) // Initialize file reader object with a CallbackStream. @@ -1138,29 +1398,23 @@ static inline FileReader make_FileReader(std::istream *s, const mpt::PathString ); } -#endif // MPT_FILEREADER_STD_ISTREAM - #if defined(MPT_ENABLE_FILEIO) // templated in order to reduce header inter-dependencies template FileReader GetFileReader(TInputFile &file) { - #if defined(MPT_FILEREADER_STD_ISTREAM) - typename TInputFile::ContentsRef tmp = file.Get(); - if(!tmp.first) - { - return FileReader(); - } - if(!tmp.first->good()) - { - return FileReader(); - } - return make_FileReader(tmp.first, tmp.second); - #else - typename TInputFile::ContentsRef tmp = file.Get(); - return make_FileReader(mpt::as_span(tmp.first.data, tmp.first.size), tmp.second); - #endif + if(!file.IsValid()) + { + return FileReader(); + } + if(file.IsCached()) + { + return make_FileReader(file.GetCache(), &file.GetFilenameRef()); + } else + { + return make_FileReader(file.GetStream(), &file.GetFilenameRef()); + } } #endif // MPT_ENABLE_FILEIO diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h index 6f73a69f7..2004bd625 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h @@ -16,17 +16,9 @@ OPENMPT_NAMESPACE_BEGIN class FileReaderTraitsMemory; -#if defined(MPT_FILEREADER_STD_ISTREAM) - class FileReaderTraitsStdStream; -typedef FileReaderTraitsStdStream FileReaderTraitsDefault; - -#else // !MPT_FILEREADER_STD_ISTREAM - -typedef FileReaderTraitsMemory FileReaderTraitsDefault; - -#endif // MPT_FILEREADER_STD_ISTREAM +using FileReaderTraitsDefault = FileReaderTraitsStdStream; namespace detail { @@ -35,9 +27,9 @@ class FileReader; } // namespace detail -typedef detail::FileReader FileReader; +using FileReader = detail::FileReader; -typedef detail::FileReader MemoryFileReader; +using MemoryFileReader = detail::FileReader; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h index e0aa76dc7..fa75bbdad 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h @@ -24,7 +24,7 @@ OPENMPT_NAMESPACE_BEGIN template struct enum_traits { - typedef typename std::make_unsigned::type store_type; + using store_type = typename std::make_unsigned::type; }; @@ -35,9 +35,9 @@ template class enum_value_type { public: - typedef enum_t enum_type; - typedef enum_value_type value_type; - typedef typename enum_traits::store_type store_type; + using enum_type = enum_t; + using value_type = enum_value_type; + using store_type = typename enum_traits::store_type; private: store_type bits; public: @@ -94,10 +94,10 @@ template class Enum { public: - typedef Enum self_type; - typedef enum_t enum_type; - typedef enum_value_type value_type; - typedef typename value_type::store_type store_type; + using self_type = Enum; + using enum_type = enum_t; + using value_type = enum_value_type; + using store_type = typename value_type::store_type; private: enum_type value; public: @@ -150,10 +150,10 @@ template :: class FlagSet { public: - typedef FlagSet self_type; - typedef enum_t enum_type; - typedef enum_value_type value_type; - typedef store_t store_type; + using self_type = FlagSet; + using enum_type = enum_t; + using value_type = enum_value_type; + using store_type = store_t; private: @@ -195,8 +195,7 @@ public: { return load(); } - // The macro-based extended instrument fields writer in InstrumentExtensions.cpp currently needs this conversion. - /*MPT_DEPRECATED*/ MPT_CONSTEXPR11_FUN operator store_type () const noexcept + MPT_CONSTEXPR11_FUN explicit operator store_type () const noexcept { return load().as_bits(); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp index 722556a84..6b4042c89 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp @@ -110,8 +110,8 @@ void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, con #endif // MODPLUG_TRACKER // remove eol if already present and add log level prefix 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 file = mpt::ToUnicode(mpt::CharsetSource, loc.file_name() ? loc.file_name() : ""); + const mpt::ustring function = mpt::ToUnicode(mpt::CharsetSource, loc.function_name() ? loc.function_name() : ""); const mpt::ustring line = mpt::ufmt::dec(loc.line()); #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) #if MPT_OS_WINDOWS @@ -132,7 +132,7 @@ void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, con } if(s_logfile) { - mpt::IO::WriteText(s_logfile, mpt::ToCharset(mpt::CharsetUTF8, mpt::format(U_("%1+%2 %3(%4): %5 [%6]\n")) + mpt::IO::WriteText(s_logfile, mpt::ToCharset(mpt::CharsetLogfile, mpt::format(U_("%1+%2 %3(%4): %5 [%6]\n")) ( mpt::Date::ANSI::ToUString(cur) , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) , file @@ -168,42 +168,21 @@ void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, con #elif defined(MODPLUG_TRACKER) && defined(MPT_BUILD_WINESUPPORT) std::clog << "NativeSupport: " - << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, file) << "(" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, line) << ")" << ": " - << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, message) - << " [" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, function) << "]" + << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetStdIO, message) + << " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]" << std::endl; #else // !MODPLUG_TRACKER std::clog << "libopenmpt: " - << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, file) << "(" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, line) << ")" << ": " - << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, message) - << " [" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, function) << "]" + << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetStdIO, message) + << " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]" << std::endl; #endif // MODPLUG_TRACKER #endif // MPT_LOG_IS_DISABLED } -void LegacyLogger::operator () (const AnyStringLocale &text) -{ - SendLogMessage(loc, MPT_LEGACY_LOGLEVEL, "", text); -} - -void LegacyLogger::operator () (const char *format, ...) -{ - static const std::size_t LOGBUF_SIZE = 1024; - char message[LOGBUF_SIZE]; - va_list va; - va_start(va, format); - vsnprintf(message, LOGBUF_SIZE, format, va); - va_end(va); - message[LOGBUF_SIZE - 1] = '\0'; - SendLogMessage(loc, MPT_LEGACY_LOGLEVEL, "", mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, message)); -} - -void LegacyLogger::operator () (LogLevel level, const mpt::ustring &text) -{ - SendLogMessage(loc, level, "", text); -} @@ -219,7 +198,7 @@ namespace Trace { // Debugging functionality will use simple globals. -bool volatile g_Enabled = false; +std::atomic g_Enabled = ATOMIC_VAR_INIT(false); static bool g_Sealed = false; @@ -338,7 +317,7 @@ bool Dump(const mpt::PathString &filename) mpt::ofstream f(filename); - f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetUTF8, Build::GetVersionStringExtended()) << std::endl; + f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetLogfile, Build::GetVersionStringExtended()) << std::endl; bool qpcValid = false; @@ -350,7 +329,7 @@ bool Dump(const mpt::PathString &filename) qpcValid = true; } - f << "Dump: " << mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToUString(ftNow)) << std::endl; + f << "Dump: " << mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString(ftNow)) << std::endl; f << "Captured events: " << Entries.size() << std::endl; if(qpcValid && (Entries.size() > 0)) { @@ -367,7 +346,7 @@ bool Dump(const mpt::PathString &filename) std::string time; if(qpcValid) { - time = mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToUString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); + time = mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); } else { time = mpt::format("0x%1")(mpt::fmt::hex0<16>(entry.Timestamp)); diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.h b/Frameworks/OpenMPT/OpenMPT/common/Logging.h index 0d7c2e349..5a8bee674 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Logging.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.h @@ -11,6 +11,9 @@ #include "BuildSettings.h" +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS +#include +#endif OPENMPT_NAMESPACE_BEGIN @@ -110,7 +113,7 @@ namespace log // All logging code gets compiled and immediately dead-code eliminated. #define MPT_LOG_IS_DISABLED #endif -static const int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ; +static constexpr int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ; #else extern int GlobalLogLevel; #endif @@ -155,35 +158,11 @@ public: /**/ -#define MPT_LEGACY_LOGLEVEL LogDebug - -class LegacyLogger : public Logger -{ -private: - const mpt::source_location loc; -public: - 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_SOURCE_LOCATION_CURRENT()) - - #else // !NO_LOGGING #define MPT_LOG(level, facility, text) MPT_DO { } MPT_WHILE_0 -struct LegacyLogger -{ - inline void MPT_PRINTF_FUNC(2,3) operator () (const char * /*format*/ , ...) {} - inline void operator () (const AnyStringLocale & /*text*/ ) {} - inline void operator () (LogLevel /*level*/ , const mpt::ustring & /*text*/ ) {} -}; -#define Log MPT_CONSTANT_IF(true) {} else mpt::log::LegacyLogger() // completely compile out arguments to Log() so that they do not even get evaluated - #endif // NO_LOGGING @@ -199,7 +178,7 @@ namespace Trace { // This cacheline bouncing does not matter at all // if there are not multiple thread adding trace points at high frequency (way greater than 1000Hz), // which, in OpenMPT, is only ever the case for just a single thread (the audio thread), if at all. -extern bool volatile g_Enabled; +extern std::atomic g_Enabled; static inline bool IsEnabled() { return g_Enabled; } enum class Direction : int8 diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp index 037fd969e..fd3c7f6eb 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp @@ -52,7 +52,7 @@ struct ProfileBlock class Statistics * stats; }; -static const std::size_t MAX_PROFILES = 1024; +static constexpr std::size_t MAX_PROFILES = 1024; static ProfileBlock Profiles[ MAX_PROFILES ]; diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.h b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h index 41a4b9008..86f11dbf9 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Profiler.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h @@ -98,7 +98,7 @@ public: /**/ -#define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __FUNCTION__) +#define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __func__) #else // !USE_PROFILER diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp index 88319051a..1e43ebae8 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp @@ -20,13 +20,13 @@ namespace Util { -static const MPT_UCHAR_TYPE EncodeNibble[16] = { +static constexpr mpt::uchar EncodeNibble[16] = { 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) +static inline bool DecodeByte(uint8 &byte, mpt::uchar c1, mpt::uchar c2) { byte = 0; if(UC_('0') <= c1 && c1 <= UC_('9')) @@ -62,7 +62,7 @@ mpt::ustring BinToHex(mpt::const_byte_span src) { mpt::ustring result; result.reserve(src.size() * 2); - for(mpt::byte byte : src) + for(std::byte byte : src) { result.push_back(EncodeNibble[(mpt::byte_cast(byte) & 0xf0) >> 4]); result.push_back(EncodeNibble[mpt::byte_cast(byte) & 0x0f]); @@ -70,9 +70,9 @@ mpt::ustring BinToHex(mpt::const_byte_span src) return result; } -std::vector HexToBin(const mpt::ustring &src) +std::vector HexToBin(const mpt::ustring &src) { - std::vector result; + std::vector result; result.reserve(src.size() / 2); for(std::size_t i = 0; (i + 1) < src.size(); i += 2) { @@ -81,7 +81,7 @@ std::vector HexToBin(const mpt::ustring &src) { return result; } - result.push_back(mpt::byte_cast(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 3415a508d..720e08c14 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/misc_util.h +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.h @@ -207,7 +207,7 @@ namespace Util namespace Util { -std::vector HexToBin(const mpt::ustring &src); +std::vector HexToBin(const mpt::ustring &src); mpt::ustring BinToHex(mpt::const_byte_span src); template inline mpt::ustring BinToHex(mpt::span src) { return Util::BinToHex(mpt::byte_cast(src)); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp index faaa7d04a..760978fab 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp @@ -12,18 +12,6 @@ #include "stdafx.h" #include "mptAlloc.h" -#include "mptBaseTypes.h" - -#include -#include - -#include -#include - -#if MPT_COMPILER_MSVC -#include -#endif - OPENMPT_NAMESPACE_BEGIN @@ -37,87 +25,6 @@ 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) && !defined(MPT_COMPILER_QUIRK_NO_ALIGNEDALLOC) - 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 || (defined(__clang__) && defined(_MSC_VER)) - 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) && !defined(MPT_COMPILER_QUIRK_NO_ALIGNEDALLOC) - std::free(raw.mem); - #elif MPT_COMPILER_MSVC || (defined(__clang__) && defined(_MSC_VER)) - _aligned_free(raw.mem); - #else - std::free(raw.mem); - #endif -} - - - } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h index 42e93a3f3..2c33e031d 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h @@ -18,8 +18,9 @@ #include "mptMemory.h" #include "mptSpan.h" +#include #include -#include +#include #include @@ -32,9 +33,9 @@ namespace mpt { -template inline span as_span(std::vector & cont) { return span(cont); } +template inline mpt::span as_span(std::vector & cont) { return mpt::span(cont); } -template inline span as_span(const std::vector & cont) { return span(cont); } +template inline mpt::span as_span(const std::vector & cont) { return mpt::span(cont); } @@ -53,13 +54,13 @@ 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)); + 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)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } }; @@ -68,8 +69,8 @@ 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)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } }; @@ -79,22 +80,6 @@ struct GetRawBytesFunctor> -#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 defined(MPT_ENABLE_ALIGNED_ALLOC) @@ -104,7 +89,7 @@ namespace mpt -#if MPT_CXX_AT_LEAST(17) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) +#if !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) using std::launder; #else template @@ -115,147 +100,17 @@ MPT_NOINLINE T* launder(T* p) noexcept #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 +template +struct alignas(static_cast(alignment)) aligned_array + : std::array { - void* aligned; - void* mem; + static_assert(static_cast(alignment) >= alignof(T)); + static_assert(((count * sizeof(T)) % static_cast(alignment)) == 0); + static_assert(sizeof(std::array) == (sizeof(T) * count)); }; -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; } -}; +static_assert(sizeof(mpt::aligned_array) == sizeof(std::array)); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h b/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h index dca2b3dfd..7c2687adc 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h @@ -56,7 +56,7 @@ OPENMPT_NAMESPACE_BEGIN -#if defined(_MFC_VER) && !defined(MPT_CPPCHECK_CUSTOM) +#if defined(MPT_WITH_MFC) && !defined(MPT_CPPCHECK_CUSTOM) #if !defined(ASSERT) #error "MFC is expected to #define ASSERT" @@ -72,7 +72,7 @@ OPENMPT_NAMESPACE_BEGIN // let MFC handle our asserts #define MPT_ASSERT_USE_FRAMEWORK 1 -#else // !_MFC_VER +#else // !MPT_WITH_MFC #if defined(ASSERT) #define MPT_FRAMEWORK_ASSERT_IS_DEFINED @@ -86,7 +86,7 @@ OPENMPT_NAMESPACE_BEGIN // handle assert in our own way without relying on some platform-/framework-specific assert implementation #define MPT_ASSERT_USE_FRAMEWORK 0 -#endif // _MFC_VER +#endif // MPT_WITH_MFC #if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1) @@ -117,7 +117,7 @@ OPENMPT_NAMESPACE_BEGIN #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_NOTREACHED() MPT_DO { if constexpr(!(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 @@ -136,17 +136,9 @@ MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *exp -#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 +#define MPT_CONSTEXPR11_ASSERT static_assert +#define MPT_CONSTEXPR14_ASSERT static_assert +#define MPT_CONSTEXPR17_ASSERT static_assert diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h index 4425abcf4..211fdaac9 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h @@ -14,7 +14,9 @@ +#include #include +#include #include #include @@ -28,14 +30,44 @@ OPENMPT_NAMESPACE_BEGIN -// Compile time assert. -#if (MPT_CXX >= 17) -#define MPT_STATIC_ASSERT static_assert +#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__) + +#define MPT_PP_STRINGIFY(x) #x + +#define MPT_PP_JOIN_HELPER(a, b) a ## b +#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b) + +#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix , __LINE__) + + + +#if MPT_COMPILER_MSVC + +#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) +#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) + +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG + +#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) + #else -#define MPT_STATIC_ASSERT(expr) static_assert((expr), "compile time assertion failed: " #expr) + +// portable #pragma message or #warning replacement +#define MPT_WARNING(text) \ + static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) () noexcept { \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + } \ +/**/ +#define MPT_WARNING_STATEMENT(text) \ + int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = [](){ \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + }() \ +/**/ + #endif -// legacy -#define STATIC_ASSERT(x) MPT_STATIC_ASSERT(x) @@ -55,43 +87,76 @@ OPENMPT_NAMESPACE_BEGIN // 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 +#if MPT_CXX_AT_LEAST(20) +#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR constexpr +#else // !C++20 +#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR const +#endif // C++20 -// C++17 std::size -#if MPT_CXX_AT_LEAST(17) -namespace mpt { -using std::size; -} // namespace mpt -#else -namespace mpt { +namespace mpt +{ +template struct constant_value { static constexpr decltype(V) value() { return V; } }; +#define MPT_FORCE_CONSTEXPR(expr) (mpt::constant_value<( expr )>::value()) +} // namespace mpt + + + +#if MPT_CXX_AT_LEAST(20) +#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated() +#define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated() +#else // !C++20 +#define MPT_IS_CONSTANT_EVALUATED20() false +// this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code +#define MPT_IS_CONSTANT_EVALUATED() true +#endif // C++20 + + + +namespace mpt +{ + template -MPT_CONSTEXPR11_FUN auto size(const T & v) -> decltype(v.size()) -{ - return v.size(); -} +struct stdarray_extent : std::integral_constant {}; + template -MPT_CONSTEXPR11_FUN std::size_t size(const T(&)[N]) noexcept +struct stdarray_extent> : std::integral_constant {}; + +template +struct is_stdarray : std::false_type {}; + +template +struct is_stdarray> : std::true_type {}; + +// mpt::extent is the same as std::extent, +// but also works for std::array, +// and asserts that the given type is actually an array type instead of returning 0. +// use as: +// mpt::extent() +// mpt::extent() +// mpt::extent() +// mpt::extent() +template +constexpr std::size_t extent() noexcept { - return N; + using Tarray = typename std::remove_cv::type>::type; + static_assert(std::is_array::value || mpt::is_stdarray::value); + if constexpr(mpt::is_stdarray::value) + { + return mpt::stdarray_extent(); + } else + { + return std::extent(); + } } + } // namespace mpt -#endif + // legacy #if MPT_COMPILER_MSVC OPENMPT_NAMESPACE_END @@ -115,41 +180,12 @@ OPENMPT_NAMESPACE_BEGIN -// 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 +#define MPT_ATTR_NODISCARD [[nodiscard]] +#define MPT_DISCARD(expr) static_cast(expr) -#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)) \ @@ -178,11 +214,6 @@ OPENMPT_NAMESPACE_BEGIN /**/ #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) @@ -217,35 +248,6 @@ OPENMPT_NAMESPACE_BEGIN -// 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. diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h index cdef4e10e..1cc2932a5 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h @@ -18,13 +18,13 @@ #include #include +#if MPT_CXX_AT_LEAST(20) +#include +#endif // C++20 #include #include -#if MPT_GCC_BEFORE(4,9,0) -#include -#endif #include @@ -33,14 +33,34 @@ 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; +namespace mpt +{ +template +struct select_type +{ +}; +template +struct select_type +{ + using type = Ta; +}; +template +struct select_type +{ + using type = Tb; +}; +} // namespace mpt + + + +using int8 = std::int8_t; +using int16 = std::int16_t; +using int32 = std::int32_t; +using int64 = std::int64_t; +using uint8 = std::uint8_t; +using uint16 = std::uint16_t; +using uint32 = std::uint32_t; +using uint64 = std::uint64_t; constexpr int8 int8_min = std::numeric_limits::min(); constexpr int16 int16_min = std::numeric_limits::min(); @@ -58,45 +78,124 @@ 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); +// fp half +// n/a +// fp single +using single = float; +constexpr single operator"" _fs(long double lit) +{ + return static_cast(lit); +} -MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); +// fp double +constexpr double operator"" _fd(long double lit) +{ + return static_cast(lit); +} +// fp extended +constexpr long double operator"" _fe(long double lit) +{ + return static_cast(lit); +} -MPT_STATIC_ASSERT(std::numeric_limits::digits == 8); +// fp quad +// n/a -MPT_STATIC_ASSERT(sizeof(char) == 1); +using float32 = mpt::select_type::type + >::type + >::type; +constexpr float32 operator"" _f32(long double lit) +{ + return static_cast(lit); +} -#if MPT_CXX_AT_LEAST(17) -namespace mpt { -using byte = std::byte; -} // namespace mpt -#define MPT_BYTE_IS_STD_BYTE 1 +using float64 = mpt::select_type::type + >::type + >::type; +constexpr float64 operator"" _f64(long double lit) +{ + return static_cast(lit); +} + +namespace mpt +{ +template +struct float_traits +{ + static constexpr bool is_float = !std::numeric_limits::is_integer; + static constexpr bool is_hard = is_float && !MPT_COMPILER_QUIRK_FLOAT_EMULATED; + static constexpr bool is_soft = is_float && MPT_COMPILER_QUIRK_FLOAT_EMULATED; + static constexpr bool is_float32 = is_float && (sizeof(T) == 4); + static constexpr bool is_float64 = is_float && (sizeof(T) == 8); + static constexpr bool is_native_endian = is_float && !MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN; + static constexpr bool is_ieee754_binary = is_float && std::numeric_limits::is_iec559 && !MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754; + static constexpr bool is_ieee754_binary32 = is_float && is_ieee754_binary && is_float32; + static constexpr bool is_ieee754_binary64 = is_float && is_ieee754_binary && is_float64; + static constexpr bool is_ieee754_binary32ne = is_float && is_ieee754_binary && is_float32 && is_native_endian; + static constexpr bool is_ieee754_binary64ne = is_float && is_ieee754_binary && is_float64 && is_native_endian; +}; +} // namespace mpt + +#if MPT_COMPILER_QUIRK_FLOAT_PREFER32 +using nativefloat = float32; +#elif MPT_COMPILER_QUIRK_FLOAT_PREFER64 +using nativefloat = float64; #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 +// prefer smaller floats, but try to use IEEE754 floats +using nativefloat = mpt::select_type::is_iec559, + float + , + mpt::select_type::is_iec559, + double + , + mpt::select_type::is_iec559, + long double + , + float + >::type + >::type + >::type; #endif -MPT_STATIC_ASSERT(sizeof(mpt::byte) == 1); -MPT_STATIC_ASSERT(alignof(mpt::byte) == 1); +constexpr nativefloat operator"" _nf(long double lit) +{ + return static_cast(lit); +} -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 + +static_assert(sizeof(std::uintptr_t) == sizeof(void*)); + + + +static_assert(std::numeric_limits::digits == 8); + +static_assert(sizeof(char) == 1); + +static_assert(sizeof(std::byte) == 1); +static_assert(alignof(std::byte) == 1); namespace mpt { @@ -104,7 +203,7 @@ 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); +static_assert(mpt::arch_bits == static_cast(mpt::pointer_size) * 8); @@ -124,6 +223,14 @@ struct limits namespace mpt { +#if MPT_CXX_AT_LEAST(20) + +using std::source_location; + +#define MPT_SOURCE_LOCATION_CURRENT() std::source_location::current() + +#else // !C++20 + // compatible with std::experimental::source_location from Library Fundamentals TS v2. struct source_location { @@ -172,7 +279,9 @@ public: } }; -#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current( __FILE__ , __FUNCTION__ , __LINE__ , 0 ) +#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current( __FILE__ , __func__ , __LINE__ , 0 ) + +#endif // C++20 } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h index 78127526f..a5f37a31b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h @@ -30,6 +30,10 @@ #include #include +#if MPT_COMPILER_MSVC +#include +#endif + OPENMPT_NAMESPACE_BEGIN @@ -49,46 +53,66 @@ OPENMPT_NAMESPACE_BEGIN +namespace mpt +{ +template +MPT_CONSTEXPR14_FUN std::array init_array(const Tx & x) +{ + std::array result{}; + for(std::size_t i = 0; i < N; ++i) + { + result[i] = x; + } + return result; +} +} // namespace mpt + + + +namespace mpt +{ + +// Work-around for the requirement of at least 1 non-throwing function argument combination in C++ (17,2a). + +template +MPT_CONSTEXPR14_FUN bool constexpr_throw_helper(Exception && e, bool really = true) +{ + //return !really ? really : throw std::forward(e); + if(really) + { + throw std::forward(e); + } + // cppcheck-suppress identicalConditionAfterEarlyExit + return really; +} +template +MPT_CONSTEXPR14_FUN bool constexpr_throw(Exception && e) +{ + return mpt::constexpr_throw_helper(std::forward(e)); +} + +template +constexpr T constexpr_throw_helper(Exception && e, bool really = true) +{ + //return !really ? really : throw std::forward(e); + if(really) + { + throw std::forward(e); + } + return T{}; +} +template +constexpr T constexpr_throw(Exception && e) +{ + return mpt::constexpr_throw_helper(std::forward(e)); +} + +} // namespace mpt + + 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). @@ -118,46 +142,46 @@ 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) + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); + if constexpr(std::numeric_limits::is_signed && std::numeric_limits::is_signed) { - MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) { return static_cast(src); } else { - return static_cast(std::max(static_cast(std::numeric_limits::min()), std::min(src, static_cast(std::numeric_limits::max())))); + 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) + } else if constexpr(!std::numeric_limits::is_signed && !std::numeric_limits::is_signed) { - MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) { return static_cast(src); } else { - 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 MPT_CONSTANT_IF(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + } else if constexpr(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) { - MPT_CONSTANT_IF(sizeof(Tdst) > sizeof(Tsrc)) + if constexpr(sizeof(Tdst) > sizeof(Tsrc)) { return static_cast(src); - } else MPT_CONSTANT_IF(sizeof(Tdst) == sizeof(Tsrc)) + } else if constexpr(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 { - 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)) + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) { - return static_cast(std::max(0, src)); + return static_cast(std::max(static_cast(0), src)); } else { - return static_cast(std::max(0, std::min(src, static_cast(std::numeric_limits::max())))); + return static_cast(std::max(static_cast(0), std::min(src, static_cast(std::numeric_limits::max())))); } } } @@ -165,11 +189,11 @@ inline Tdst saturate_cast(Tsrc src) template inline Tdst saturate_cast(double src) { - if(src >= std::numeric_limits::max()) + if(src >= static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } - if(src <= std::numeric_limits::min()) + if(src <= static_cast(std::numeric_limits::min())) { return std::numeric_limits::min(); } @@ -179,11 +203,11 @@ inline Tdst saturate_cast(double src) template inline Tdst saturate_cast(float src) { - if(src >= std::numeric_limits::max()) + if(src >= static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } - if(src <= std::numeric_limits::min()) + if(src <= static_cast(std::numeric_limits::min())) { return std::numeric_limits::min(); } @@ -191,51 +215,51 @@ inline Tdst saturate_cast(float 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; +using std::popcount; +using std::has_single_bit; +using std::bit_ceil; +using std::bit_floor; +using std::bit_width; +using std::rotl; +using std::rotr; #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_CONSTEXPR14_FUN int popcount(T val) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); - return mpt::weight(x) == 1; + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + int result = 0; + while(val > 0) + { + if(val & 0x1) + { + result++; + } + val >>= 1; + } + return result; } template -MPT_CONSTEXPR14_FUN T ceil2(T x) noexcept +MPT_CONSTEXPR14_FUN bool has_single_bit(T x) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + return mpt::popcount(x) == 1; +} + +template +MPT_CONSTEXPR14_FUN T bit_ceil(T x) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); T result = 1; while(result < x) { @@ -250,10 +274,10 @@ MPT_CONSTEXPR14_FUN T ceil2(T x) noexcept } template -MPT_CONSTEXPR14_FUN T floor2(T x) noexcept +MPT_CONSTEXPR14_FUN T bit_floor(T x) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); if(x == 0) { return 0; @@ -272,10 +296,10 @@ MPT_CONSTEXPR14_FUN T floor2(T x) noexcept } template -MPT_CONSTEXPR14_FUN T log2p1(T x) noexcept +MPT_CONSTEXPR14_FUN T bit_width(T x) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); T result = 0; while(x > 0) { @@ -285,58 +309,50 @@ MPT_CONSTEXPR14_FUN T log2p1(T x) noexcept return result; } +namespace detail +{ + +template +MPT_CONSTEXPR14_FUN T rotl(T x, int r) noexcept +{ + auto N = std::numeric_limits::digits; + return (x >> (N - r)) | (x << r); +} + +template +MPT_CONSTEXPR14_FUN T rotr(T x, int r) noexcept +{ + auto N = std::numeric_limits::digits; + return (x << (N - r)) | (x >> r); +} + +} // namespace detail + +template +MPT_CONSTEXPR14_FUN T rotl(T x, int s) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + auto N = std::numeric_limits::digits; + auto r = s % N; + return (s < 0) ? detail::rotr(x, -s) : ((x >> (N - r)) | (x << r)); +} + +template +MPT_CONSTEXPR14_FUN T rotr(T x, int s) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + auto N = std::numeric_limits::digits; + auto r = s % N; + return (s < 0) ? detail::rotl(x, -s) : ((x << (N - r)) | (x >> r)); +} + #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 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); + 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); } }; @@ -389,8 +405,8 @@ inline T ExponentialGrow(const T &x, const Tlimit &limit) { return 2; } - T add = std::min(x >> 1, std::numeric_limits::max() - x); - return std::min(x + add, mpt::saturate_cast(limit)); + T add = std::min(x >> 1, std::numeric_limits::max() - x); + return std::min(x + add, mpt::saturate_cast(limit)); } template @@ -402,34 +418,6 @@ inline T ExponentialGrow(const T &x) } //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. @@ -489,8 +477,8 @@ 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); + static_assert(std::numeric_limits::is_integer); + 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); @@ -505,8 +493,8 @@ MPT_FORCEINLINE auto rshift_signed_standard(T x, int y) -> decltype(x >> y) 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); + static_assert(std::numeric_limits::is_integer); + 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); @@ -523,16 +511,16 @@ MPT_FORCEINLINE auto lshift_signed_standard(T x, int y) -> decltype(x << y) 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); + static_assert(std::numeric_limits::is_integer); + 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); + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_signed); return x << y; } @@ -564,9 +552,22 @@ MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) #endif -} // namespace mpt +template +struct array_size; +template +struct array_size> +{ + static constexpr std::size_t size = N; +}; +template +struct array_size +{ + static constexpr std::size_t size = N; +}; + +} // namespace mpt namespace Util { @@ -574,8 +575,7 @@ namespace Util // Returns maximum value of given integer type. template constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits::max)();} -} - +} // namespace Util namespace mpt { @@ -677,58 +677,4 @@ 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 deleted file mode 100644 index fb5cf369c..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * mptBufferIO.h - * ------------- - * Purpose: A wrapper around std::stringstream, fixing MSVC tell/seek problems with empty streams. - * Notes : You should only ever use these wrappers instead of plain std::stringstream classes. - * 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 - - -// MSVC std::stringbuf (and thereby std::ostringstream, std::istringstream and -// std::stringstream) fail seekpos() and seekoff() when the stringbuf is -// currently empty. -// seekpos() and seekoff() can get called via tell*() or seek*() iostream -// members. seekoff() (and thereby tell*()), but not seekpos(), has been fixed -// from VS2010 onwards to handle this specific case and changed to not fail -// when the stringbuf is empty. -// Work-around strategy: -// As re-implementing or duplicating the whole std::stringbuf semantics would be -// rather convoluted, we make use of the knowledge of specific inner workings of -// the MSVC implementation here and just fix-up where it causes problems. This -// keeps the additional code at a minimum size. - - -namespace mpt -{ - -#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - -class stringbuf - : public std::stringbuf -{ -public: - stringbuf(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - : std::stringbuf(mode) - { - return; - } - stringbuf(const std::string &str, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - : std::stringbuf(str, mode) - { - return; - } -public: - virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) - { - pos_type result = std::stringbuf::seekoff(off, way, which); - if(result == pos_type(-1)) - { - if((which & std::ios_base::in) || (which & std::ios_base::out)) - { - if(off == 0) - { - result = 0; - } - } - } - return result; - } - virtual pos_type seekpos(pos_type ptr, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - { - pos_type result = std::stringbuf::seekpos(ptr, mode); - if(result == pos_type(-1)) - { - if((mode & std::ios_base::in) || (mode & std::ios_base::out)) - { - if(static_cast(ptr) == 0) - { - result = 0; - } - } - } - return result; - } -}; - -class istringstream - : public std::basic_istream -{ -private: - mpt::stringbuf buf; -public: - istringstream(std::ios_base::openmode mode = std::ios_base::in) - : std::basic_istream(&buf) - , buf(mode | std::ios_base::in) - { - } - istringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::in) - : std::basic_istream(&buf) - , buf(str, mode | std::ios_base::in) - { - } - ~istringstream() - { - } -public: - mpt::stringbuf *rdbuf() const { return const_cast(&buf); } - std::string str() const { return buf.str(); } - void str(const std::string &str) { buf.str(str); } -}; - -class ostringstream - : public std::basic_ostream -{ -private: - mpt::stringbuf buf; -public: - ostringstream(std::ios_base::openmode mode = std::ios_base::out) - : std::basic_ostream(&buf) - , buf(mode | std::ios_base::out) - { - } - ostringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::out) - : std::basic_ostream(&buf) - , buf(str, mode | std::ios_base::out) - { - } - ~ostringstream() - { - } -public: - mpt::stringbuf *rdbuf() const { return const_cast(&buf); } - std::string str() const { return buf.str(); } - void str(const std::string &str) { buf.str(str); } -}; - -class stringstream - : public std::basic_iostream -{ -private: - mpt::stringbuf buf; -public: - stringstream(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - : std::basic_iostream(&buf) - , buf(mode | std::ios_base::in | std::ios_base::out) - { - } - stringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - : std::basic_iostream(&buf) - , buf(str, mode | std::ios_base::in | std::ios_base::out) - { - } - ~stringstream() - { - } -public: - mpt::stringbuf *rdbuf() const { return const_cast(&buf); } - std::string str() const { return buf.str(); } - void str(const std::string &str) { buf.str(str); } -}; - -#else - -typedef std::stringbuf stringbuf; -typedef std::istringstream istringstream; -typedef std::ostringstream ostringstream; -typedef std::stringstream stringstream; - -#endif - -} // namespace mpt - - -OPENMPT_NAMESPACE_END - diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp index 5fffb670c..c56708faf 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp @@ -29,7 +29,7 @@ uint8 ProcModel = 0; uint8 ProcStepping = 0; -#if MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64)) +#if MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64)) && defined(ENABLE_CPUID) #include @@ -97,42 +97,25 @@ static cpuid_result cpuid(uint32 function) } -#if 0 - -static cpuid_result cpuidex(uint32 function_a, uint32 function_c) -{ - cpuid_result result; - int CPUInfo[4]; - __cpuidex(CPUInfo, function_a, function_c); - result.a = CPUInfo[0]; - result.b = CPUInfo[1]; - result.c = CPUInfo[2]; - result.d = CPUInfo[3]; - return result; -} - -#endif - - void InitProcSupport() { RealProcSupport = 0; ProcSupport = 0; - MemsetZero(ProcVendorID); - MemsetZero(ProcBrandID); + mpt::String::WriteAutoBuf(ProcVendorID) = ""; + mpt::String::WriteAutoBuf(ProcBrandID) = ""; ProcFamily = 0; ProcModel = 0; ProcStepping = 0; + ProcSupport |= PROCSUPPORT_ASM_INTRIN; + ProcSupport |= PROCSUPPORT_CPUID; + { cpuid_result VendorString = cpuid(0x00000000u); mpt::String::WriteAutoBuf(ProcVendorID) = VendorString.as_string(); - - // Cyrix 6x86 and 6x86MX do not specify the value returned in eax. - // They both support 0x00000001u however. - if((VendorString.as_string() == "CyrixInstead") || (VendorString.a >= 0x00000001u)) + if(VendorString.a >= 0x00000001u) { cpuid_result StandardFeatureFlags = cpuid(0x00000001u); uint32 Stepping = (StandardFeatureFlags.a >> 0) & 0x0f; @@ -140,40 +123,21 @@ void InitProcSupport() uint32 BaseFamily = (StandardFeatureFlags.a >> 8) & 0x0f; uint32 ExtModel = (StandardFeatureFlags.a >> 16) & 0x0f; uint32 ExtFamily = (StandardFeatureFlags.a >> 20) & 0xff; - if(VendorString.as_string() == "GenuineIntel") + if(BaseFamily == 0xf) { - if(BaseFamily == 0xf) - { - ProcFamily = static_cast(ExtFamily + BaseFamily); - } else - { - ProcFamily = static_cast(BaseFamily); - } - if(BaseFamily == 0x6 || BaseFamily == 0xf) - { - ProcModel = static_cast((ExtModel << 4) | (BaseModel << 0)); - } else - { - ProcModel = static_cast(BaseModel); - } - } else if(VendorString.as_string() == "AuthenticAMD") - { - if(BaseFamily == 0xf) - { - ProcFamily = static_cast(ExtFamily + BaseFamily); - ProcModel = static_cast((ExtModel << 4) | (BaseModel << 0)); - } else - { - ProcFamily = static_cast(BaseFamily); - ProcModel = static_cast(BaseModel); - } + ProcFamily = static_cast(ExtFamily + BaseFamily); } else { ProcFamily = static_cast(BaseFamily); + } + if((BaseFamily == 0x6) || (BaseFamily == 0xf)) + { + ProcModel = static_cast((ExtModel << 4) | (BaseModel << 0)); + } else + { ProcModel = static_cast(BaseModel); } ProcStepping = static_cast(Stepping); - if(StandardFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; if(StandardFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX; if(StandardFeatureFlags.d & (1<<25)) ProcSupport |= PROCSUPPORT_SSE; if(StandardFeatureFlags.d & (1<<26)) ProcSupport |= PROCSUPPORT_SSE2; @@ -181,115 +145,56 @@ void InitProcSupport() if(StandardFeatureFlags.c & (1<< 9)) ProcSupport |= PROCSUPPORT_SSSE3; if(StandardFeatureFlags.c & (1<<19)) ProcSupport |= PROCSUPPORT_SSE4_1; if(StandardFeatureFlags.c & (1<<20)) ProcSupport |= PROCSUPPORT_SSE4_2; + if(StandardFeatureFlags.c & (1<<28)) ProcSupport |= PROCSUPPORT_AVX; } - bool canExtended = false; - // 3DNow! manual recommends to just execute 0x80000000u. - // It is totally unknown how earlier CPUs from other vendors - // would behave. - // Thus we only execute 0x80000000u on other vendors CPUs for the earliest - // 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() == "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. - canExtended = true; - } - - } else if(VendorString.as_string() == "CentaurHauls") - { // Centaur (IDT WinChip or VIA C3) - - if(ProcFamily == 5) - { // IDT - - if(ProcModel >= 8) - { // >= WinChip 2 - canExtended = true; - } - - } else if(ProcFamily >= 6) - { // VIA - - if((ProcFamily >= 7) || ((ProcFamily == 6) && (ProcModel >= 7))) - { // >= C3 Samuel 2 - canExtended = true; - } - - } - - } else if(VendorString.as_string() == "CyrixInstead") - { // Cyrix - - // 6x86 : 5.2.x - // 6x86L : 5.2.x - // MediaGX : 4.4.x - // 6x86MX : 6.0.x - // MII : 6.0.x - // MediaGXm: 5.4.x - // well, doh ... - - if((ProcFamily == 5) && (ProcModel >= 4)) - { // Cyrix MediaGXm - canExtended = true; - } - - } else if(VendorString.as_string() == "Geode by NSC") - { // National Semiconductor - - if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 5))) - { // >= Geode GX2 - canExtended = true; - } - - } else - { // unknown, which nowadays most likely means some virtualized CPU - - // we assume extended flags present in this case - canExtended = true; - - } - - if(canExtended) + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) { - cpuid_result ExtendedVendorString = cpuid(0x80000000u); - if(ExtendedVendorString.a >= 0x80000001u) + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<<29)) ProcSupport |= PROCSUPPORT_LM; + } + if(ExtendedVendorString.a >= 0x80000004u) + { + mpt::String::WriteAutoBuf(ProcBrandID) = cpuid(0x80000002u).as_string4() + cpuid(0x80000003u).as_string4() + cpuid(0x80000004u).as_string4(); + if(ExtendedVendorString.a >= 0x80000007u) { - 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(); + cpuid_result ExtendedFeatures = cpuid(0x80000007u); + if(ExtendedFeatures.b & (1<< 5)) ProcSupport |= PROCSUPPORT_AVX2; } } } - // We do not have to check if SSE got enabled by the OS because we only do - // support Windows >= XP. Windows will always enable SSE since Windows 98 SE. + RealProcSupport = ProcSupport; + +} + + +#elif MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64)) + + +void InitProcSupport() +{ + + RealProcSupport = 0; + ProcSupport = 0; + mpt::String::WriteAutoBuf(ProcVendorID) = ""; + mpt::String::WriteAutoBuf(ProcBrandID) = ""; + ProcFamily = 0; + ProcModel = 0; + ProcStepping = 0; + + ProcSupport |= PROCSUPPORT_ASM_INTRIN; + + { + + if(IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_MMX; + if(IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_SSE; + if(IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_SSE2; + if(IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_SSE3; + + } RealProcSupport = ProcSupport; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h index e70dc1f17..5b3eb3a24 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h @@ -16,26 +16,30 @@ OPENMPT_NAMESPACE_BEGIN -#ifdef ENABLE_ASM +#ifdef MODPLUG_TRACKER -#define PROCSUPPORT_LM 0x00001 // Processor supports long mode (amd64) -#define PROCSUPPORT_CMOV 0x00004 // Processor supports conditional move instructions (i686) +#define PROCSUPPORT_ASM_INTRIN 0x00001 // assembly and intrinsics are enabled at runtime +#define PROCSUPPORT_CPUID 0x00002 // Processor supports modern cpuid +#define PROCSUPPORT_LM 0x00004 // Processor supports long mode (amd64) #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 -#define PROCSUPPORT_AMD_3DNOWEXT 0x00080 // Processor supports AMD 3DNow!2 instructions #define PROCSUPPORT_SSE 0x00100 // Processor supports SSE instructions #define PROCSUPPORT_SSE2 0x00200 // Processor supports SSE2 instructions #define PROCSUPPORT_SSE3 0x00400 // Processor supports SSE3 instructions #define PROCSUPPORT_SSSE3 0x00800 // Processor supports SSSE3 instructions #define PROCSUPPORT_SSE4_1 0x01000 // Processor supports SSE4.1 instructions #define PROCSUPPORT_SSE4_2 0x02000 // Processor supports SSE4.2 instructions +#define PROCSUPPORT_AVX 0x10000 // Processor supports AVX instructions +#define PROCSUPPORT_AVX2 0x20000 // Processor supports AVX2 instructions -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; +static constexpr uint32 PROCSUPPORT_i586 = 0u ; +static constexpr uint32 PROCSUPPORT_x86_SSE = 0u | PROCSUPPORT_SSE ; +static constexpr uint32 PROCSUPPORT_x86_SSE2 = 0u | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 ; +static constexpr uint32 PROCSUPPORT_AMD64 = 0u | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 | PROCSUPPORT_LM; + +#endif + + +#ifdef ENABLE_ASM extern uint32 RealProcSupport; extern uint32 ProcSupport; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h index 498015b14..de5a96f67 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h @@ -113,7 +113,7 @@ public: inline void processByte(byte_type byte) { - MPT_CONSTANT_IF(reverseData) + if constexpr(reverseData) { value = (value >> 8) ^ read_table(static_cast((value & 0xff) ^ byte)); } else @@ -152,13 +152,11 @@ public: return *this; } -#if MPT_BYTE_IS_STD_BYTE - inline crc & process(mpt::byte c) + inline crc & process(std::byte c) { processByte(mpt::byte_cast(c)); return *this; } -#endif template crc & process(InputIt beg, InputIt end) @@ -196,13 +194,11 @@ public: return *this; } -#if MPT_BYTE_IS_STD_BYTE - inline crc & operator () (mpt::byte c) + inline crc & operator () (std::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 index 59bf21139..1e2a44687 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptException.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptException.h @@ -17,14 +17,14 @@ #include "mptBaseMacros.h" #include -#if !defined(_MFC_VER) +#if !defined(MPT_WITH_MFC) #include -#endif // !_MFC_VER +#endif // !MPT_WITH_MFC -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) // cppcheck-suppress missingInclude #include -#endif // _MFC_VER +#endif // MPT_WITH_MFC @@ -35,21 +35,21 @@ 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) +#if defined(MPT_WITH_MFC) #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 +#else // !MPT_WITH_MFC #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 +#endif // MPT_WITH_MFC diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h b/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h index 67fc443d9..571fac2b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h @@ -53,19 +53,19 @@ template <> inline std::string get_exception_text(const std::except #if defined(MPT_ENABLE_CHARSET_LOCALE) template <> inline mpt::lstring get_exception_text(const std::exception & e) { - return mpt::ToLocale(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); + return mpt::ToLocale(mpt::CharsetException, mpt::get_exception_text_impl(e)); } #endif #if MPT_WSTRING_FORMAT template <> inline std::wstring get_exception_text(const std::exception & e) { - return mpt::ToWide(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); + return mpt::ToWide(mpt::CharsetException, mpt::get_exception_text_impl(e)); } #endif #if MPT_USTRING_MODE_UTF8 template <> inline mpt::ustring get_exception_text(const std::exception & e) { - return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); + return mpt::ToUnicode(mpt::CharsetException, mpt::get_exception_text_impl(e)); } #endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp index eae979c7a..c5119f6fb 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp @@ -99,12 +99,12 @@ mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMod fopen_mode = _T("r"); break; case std::ios_base::out: - MPT_FALLTHROUGH; + [[fallthrough]]; case std::ios_base::out | std::ios_base::trunc: fopen_mode = _T("w"); break; case std::ios_base::app: - MPT_FALLTHROUGH; + [[fallthrough]]; case std::ios_base::out | std::ios_base::app: fopen_mode = _T("a"); break; @@ -115,7 +115,7 @@ mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMod fopen_mode = _T("w+"); break; case std::ios_base::out | std::ios_base::in | std::ios_base::app: - MPT_FALLTHROUGH; + [[fallthrough]]; case std::ios_base::in | std::ios_base::app: fopen_mode = _T("a+"); break; @@ -171,12 +171,25 @@ FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_ // cppcheck-suppress exceptThrowInDestructor SafeOutputFile::~SafeOutputFile() noexcept(false) { + const bool mayThrow = (std::uncaught_exceptions() == 0); if(!stream()) { + #if MPT_COMPILER_MSVC + if(m_f) + { + fclose(m_f); + } + #endif // MPT_COMPILER_MSVC return; } if(!stream().rdbuf()) { + #if MPT_COMPILER_MSVC + if(m_f) + { + fclose(m_f); + } + #endif // MPT_COMPILER_MSVC return; } #if MPT_COMPILER_MSVC @@ -210,8 +223,12 @@ SafeOutputFile::~SafeOutputFile() noexcept(false) errorOnFlush = true; } #endif // MPT_COMPILER_MSVC - // ignore errorOnFlush here, and re-throw the earlier exception - throw; + if(mayThrow) + { + // ignore errorOnFlush here, and re-throw the earlier exception + // cppcheck-suppress exceptThrowInDestructor + throw; + } } } #if MPT_COMPILER_MSVC @@ -227,8 +244,9 @@ SafeOutputFile::~SafeOutputFile() noexcept(false) errorOnFlush = true; } #endif // MPT_COMPILER_MSVC - if(errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) + if(mayThrow && errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) { + // cppcheck-suppress exceptThrowInDestructor throw std::ios_base::failure(std::string("Error flushing file buffers.")); } } @@ -243,11 +261,11 @@ SafeOutputFile::~SafeOutputFile() noexcept(false) namespace mpt { -LazyFileRef & LazyFileRef::operator = (const std::vector &data) +LazyFileRef & LazyFileRef::operator = (const std::vector &data) { mpt::ofstream file(m_Filename, std::ios::binary); file.exceptions(std::ios_base::failbit | std::ios_base::badbit); - mpt::IO::WriteRaw(file, data.data(), data.size()); + mpt::IO::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } @@ -256,7 +274,7 @@ LazyFileRef & LazyFileRef::operator = (const std::vector &data) { mpt::ofstream file(m_Filename, std::ios::binary); file.exceptions(std::ios_base::failbit | std::ios_base::badbit); - mpt::IO::WriteRaw(file, data.data(), data.size()); + mpt::IO::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } @@ -265,23 +283,23 @@ LazyFileRef & LazyFileRef::operator = (const std::string &data) { mpt::ofstream file(m_Filename, std::ios::binary); file.exceptions(std::ios_base::failbit | std::ios_base::badbit); - mpt::IO::WriteRaw(file, data.data(), data.size()); + mpt::IO::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } -LazyFileRef::operator std::vector () const +LazyFileRef::operator std::vector () const { mpt::ifstream file(m_Filename, std::ios::binary); if(!mpt::IO::IsValid(file)) { - return std::vector(); + return std::vector(); } file.exceptions(std::ios_base::failbit | std::ios_base::badbit); mpt::IO::SeekEnd(file); - std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); mpt::IO::SeekBegin(file); - mpt::IO::ReadRaw(file, buf.data(), buf.size()); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); return buf; } @@ -296,7 +314,7 @@ LazyFileRef::operator std::vector () const mpt::IO::SeekEnd(file); std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); mpt::IO::SeekBegin(file); - mpt::IO::ReadRaw(file, buf.data(), buf.size()); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); return buf; } @@ -311,7 +329,7 @@ LazyFileRef::operator std::string () const mpt::IO::SeekEnd(file); std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); mpt::IO::SeekBegin(file); - mpt::IO::ReadRaw(file, buf.data(), buf.size()); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); return std::string(buf.begin(), buf.end()); } @@ -320,149 +338,22 @@ LazyFileRef::operator std::string () const #endif // MODPLUG_TRACKER - -#ifdef MODPLUG_TRACKER - -#if MPT_OS_WINDOWS - -CMappedFile::~CMappedFile() +bool InputFile::DefaultToLargeAddressSpaceUsage() { - Close(); + return false; } -bool CMappedFile::Open(const mpt::PathString &filename) -{ - m_hFile = CreateFile( - filename.AsNativePrefixed().c_str(), - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if(m_hFile == INVALID_HANDLE_VALUE) - { - m_hFile = nullptr; - return false; - } - m_FileName = filename; - return true; -} - - -void CMappedFile::Close() -{ - m_FileName = mpt::PathString(); - // Unlock file - if(m_hFMap) - { - if(m_pData) - { - UnmapViewOfFile(m_pData); - m_pData = nullptr; - } - CloseHandle(m_hFMap); - m_hFMap = nullptr; - } else if(m_pData) - { - free(m_pData); - m_pData = nullptr; - } - - // Close file handle - if(m_hFile) - { - CloseHandle(m_hFile); - m_hFile = nullptr; - } -} - - -size_t CMappedFile::GetLength() -{ - LARGE_INTEGER size; - if(GetFileSizeEx(m_hFile, &size) == FALSE) - { - return 0; - } - return mpt::saturate_cast(size.QuadPart); -} - - -const mpt::byte *CMappedFile::Lock() -{ - size_t length = GetLength(); - if(!length) return nullptr; - - void *lpStream; - - HANDLE hmf = CreateFileMapping( - m_hFile, - NULL, - PAGE_READONLY, - 0, 0, - NULL); - - // Try memory-mapping first - if(hmf) - { - lpStream = MapViewOfFile( - hmf, - FILE_MAP_READ, - 0, 0, - length); - if(lpStream) - { - m_hFMap = hmf; - m_pData = lpStream; - return mpt::void_cast(lpStream); - } - CloseHandle(hmf); - hmf = nullptr; - } - - // Fallback if memory-mapping fails for some weird reason - if((lpStream = malloc(length)) == nullptr) return nullptr; - memset(lpStream, 0, length); - size_t bytesToRead = length; - size_t bytesRead = 0; - while(bytesToRead > 0) - { - DWORD chunkToRead = mpt::saturate_cast(length); - DWORD chunkRead = 0; - if(ReadFile(m_hFile, mpt::void_cast(lpStream) + bytesRead, chunkToRead, &chunkRead, NULL) == FALSE) - { - // error - free(lpStream); - return nullptr; - } - bytesRead += chunkRead; - bytesToRead -= chunkRead; - } - m_pData = lpStream; - return mpt::void_cast(lpStream); -} - -#endif // MPT_OS_WINDOWS - -#endif // MODPLUG_TRACKER - - - InputFile::InputFile() + : m_IsCached(false) { return; } -InputFile::InputFile(const mpt::PathString &filename) - : m_Filename(filename) +InputFile::InputFile(const mpt::PathString &filename, bool allowWholeFileCaching) + : m_IsCached(false) { -#if defined(MPT_FILEREADER_STD_ISTREAM) - m_File.open(m_Filename, std::ios::binary | std::ios::in); -#else - m_File.Open(m_Filename); -#endif + Open(filename, allowWholeFileCaching); } InputFile::~InputFile() @@ -471,57 +362,82 @@ InputFile::~InputFile() } -bool InputFile::Open(const mpt::PathString &filename) +bool InputFile::Open(const mpt::PathString &filename, bool allowWholeFileCaching) { + m_IsCached = false; + m_Cache.resize(0); + m_Cache.shrink_to_fit(); m_Filename = filename; -#if defined(MPT_FILEREADER_STD_ISTREAM) m_File.open(m_Filename, std::ios::binary | std::ios::in); + if(allowWholeFileCaching) + { + if(mpt::IO::IsReadSeekable(m_File)) + { + if(!mpt::IO::SeekEnd(m_File)) + { + m_File.close(); + return false; + } + mpt::IO::Offset filesize = mpt::IO::TellRead(m_File); + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + if(Util::TypeCanHoldValue(filesize)) + { + std::size_t buffersize = mpt::saturate_cast(filesize); + m_Cache.resize(buffersize); + if(mpt::IO::ReadRaw(m_File, mpt::as_span(m_Cache)) != filesize) + { + m_File.close(); + return false; + } + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + m_IsCached = true; + return true; + } + } + } return m_File.good(); -#else - return m_File.Open(m_Filename); -#endif } bool InputFile::IsValid() const { -#if defined(MPT_FILEREADER_STD_ISTREAM) return m_File.good(); -#else - return m_File.IsOpen(); -#endif } -#if defined(MPT_FILEREADER_STD_ISTREAM) -InputFile::ContentsRef InputFile::Get() +bool InputFile::IsCached() const { - InputFile::ContentsRef result; - result.first = &m_File; - result.second = m_File.good() ? &m_Filename : nullptr; - return result; + return m_IsCached; } -#else -InputFile::ContentsRef InputFile::Get() +const mpt::PathString& InputFile::GetFilenameRef() const { - InputFile::ContentsRef result; - result.first.data = nullptr; - result.first.size = 0; - result.second = nullptr; - if(!m_File.IsOpen()) - { - return result; - } - result.first.data = m_File.Lock(); - if(result.first.data) - result.first.size = m_File.GetLength(); - result.second = &m_Filename; - return result; + return m_Filename; +} + + +std::istream* InputFile::GetStream() +{ + MPT_ASSERT(!m_IsCached); + return &m_File; +} + + +mpt::const_byte_span InputFile::GetCache() +{ + MPT_ASSERT(m_IsCached); + return mpt::as_span(m_Cache); } -#endif #else // !MPT_ENABLE_FILEIO diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h index 53fec7e07..1c8ad5099 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h @@ -68,7 +68,7 @@ template inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::ios_base::openmode mode) { #if defined(MPT_FSTREAM_DO_CONVERSIONS_ANSI) - base.open(mpt::ToCharset(mpt::CharsetLocale, filename.AsNative()).c_str(), mode); + base.open(mpt::ToCharset(mpt::Charset::Locale, filename.AsNative()).c_str(), mode); #else base.open(filename.AsNativePrefixed().c_str(), mode); #endif @@ -181,7 +181,7 @@ class SafeOutputFile private: FlushMode m_FlushMode; #if MPT_COMPILER_MSVC - FILE *m_f; + FILE *m_f = nullptr; #endif // MPT_COMPILER_MSVC mpt::ofstream m_s; #if MPT_COMPILER_MSVC @@ -198,6 +198,8 @@ public: , m_s(filename, mode) #endif // MPT_COMPILER_MSVC { + if(!stream().is_open()) + stream().setstate(mpt::ofstream::failbit); } mpt::ofstream& stream() { @@ -245,10 +247,10 @@ public: return; } public: - LazyFileRef & operator = (const std::vector &data); + LazyFileRef & operator = (const std::vector &data); LazyFileRef & operator = (const std::vector &data); LazyFileRef & operator = (const std::string &data); - operator std::vector () const; + operator std::vector () const; operator std::vector () const; operator std::string () const; }; @@ -259,59 +261,25 @@ public: } // namespace mpt - -#ifdef MODPLUG_TRACKER -#if MPT_OS_WINDOWS -class CMappedFile -{ -protected: - HANDLE m_hFile; - HANDLE m_hFMap; - void *m_pData; - mpt::PathString m_FileName; - -public: - CMappedFile() : m_hFile(nullptr), m_hFMap(nullptr), m_pData(nullptr) { } - ~CMappedFile(); - -public: - bool Open(const mpt::PathString &filename); - bool IsOpen() const { return m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE; } - const mpt::PathString * GetpFilename() const { return &m_FileName; } - void Close(); - size_t GetLength(); - const mpt::byte *Lock(); -}; -#endif // MPT_OS_WINDOWS -#endif // MODPLUG_TRACKER - - class InputFile { private: mpt::PathString m_Filename; - #ifdef MPT_FILEREADER_STD_ISTREAM - mpt::ifstream m_File; - #else - CMappedFile m_File; - #endif + mpt::ifstream m_File; + bool m_IsCached; + std::vector m_Cache; +public: + static bool DefaultToLargeAddressSpaceUsage(); public: InputFile(); - InputFile(const mpt::PathString &filename); + InputFile(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); ~InputFile(); - bool Open(const mpt::PathString &filename); + bool Open(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); bool IsValid() const; -#if defined(MPT_FILEREADER_STD_ISTREAM) - typedef std::pair ContentsRef; -#else - struct Data - { - const mpt::byte *data; - std::size_t size; - }; - typedef std::pair ContentsRef; -#endif - InputFile::ContentsRef Get(); + bool IsCached() const; + const mpt::PathString& GetFilenameRef() const; + std::istream* GetStream(); + mpt::const_byte_span GetCache(); }; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp index e1c3e3de3..41d06903c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp @@ -17,9 +17,6 @@ #include #include #include -#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM -#include -#endif // MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM OPENMPT_NAMESPACE_BEGIN @@ -30,100 +27,84 @@ namespace mpt { namespace IO { -#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. -// seekoff() can get called via tell*() or seek*() iostream members. tell*() has -// been special cased from VS2010 onwards to handle this specific case and -// changed to not fail when the stringbuf is empty. -// In addition to using out own wrapper around std::stringstream and -// std::stringbuf, we also work-around the plain native type's problem in case -// we get handed such an object from third party code. This mitigation of course -// requires using our consolidated and normalized IO functions. -// We use the following work-around strategy: -// * If the stream is already in failed state, we do not do any work-around -// and bail out early. -// * If the underlying streambuf is not a std::stringbuf, the work-around is -// not necessary and we skip it. -// * If querying the current position does not fail and returns a -// position > 0, the underlying stringbuf is not empty and we also bail out. -// * Otherwise, we actually query the string contained in the stringbuf to be -// empty. This operation is slow as it has to copy the string into a -// temporary. -// Note, however, that this is only ever necessary if the current position -// is 0. If it always has been 0, the stringbuf will be empty anyway and the -// copy does not cost anything measurable. If it got seeked to position 0, -// we have to pay the price. However, this should be relatively uncommmon in -// pratice. -// * The actual work-around consists of performing or emulating the requested -// operation and resetting the failed state afterwards. - -static bool StreamIsStringStreamAndValidAndEmpty(std::ostream & f) -{ - if(f.fail() || !f.rdbuf()) - { // failed - return false; - } - if(!dynamic_cast(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf))) - { // no stringbuf - return false; - } - std::streampos pos = f.tellp(); - f.clear(f.rdstate() & ~std::ios::failbit); - if(pos != std::streampos(-1) && pos > 0) - { // if the position is not 0, the streambuf is not empty - return false; - } - return dynamic_cast(f.rdbuf())->str().empty(); // slow -} - -static bool StreamIsStringStreamAndValidAndEmpty(std::istream & f) -{ - if(f.fail() || !f.rdbuf()) - { // failed - return false; - } - if(!dynamic_cast(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf))) - { // no stringbuf - return false; - } - std::streampos pos = f.tellg(); - f.clear(f.rdstate() & ~std::ios::failbit); - if(pos != std::streampos(-1) && pos > 0) - { // if the position is not 0, the streambuf is not empty - return false; - } - return dynamic_cast(f.rdbuf())->str().empty(); // slow -} - -static bool StreamIsStringStreamAndValidAndEmpty(std::iostream & f) -{ - if(f.fail() || !f.rdbuf()) - { // failed - return false; - } - if(!dynamic_cast(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf))) - { // no stringbuf - return false; - } - std::streampos ipos = f.tellg(); - f.clear(f.rdstate() & ~std::ios::failbit); - std::streampos opos = f.tellp(); - f.clear(f.rdstate() & ~std::ios::failbit); - if((ipos != std::streampos(-1) && ipos > 0) || (opos != std::streampos(-1) && opos > 0)) - { // if the position is not 0, the streambuf is not empty - return false; - } - return dynamic_cast(f.rdbuf())->str().empty(); // slow -} - -#endif // MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - -//STATIC_ASSERT(sizeof(std::streamoff) == 8); // Assert 64bit file support. +//static_assert(sizeof(std::streamoff) == 8); // Assert 64bit file support. bool IsValid(std::ostream & f) { return !f.fail(); } bool IsValid(std::istream & f) { return !f.fail(); } bool IsValid(std::iostream & f) { return !f.fail(); } +bool IsReadSeekable(std::istream & f) +{ + f.clear(); + std::streampos oldpos = f.tellg(); + if(f.fail() || oldpos == std::streampos(-1)) + { + f.clear(); + return false; + } + f.seekg(0, std::ios::beg); + if(f.fail()) + { + f.clear(); + f.seekg(oldpos); + f.clear(); + return false; + } + f.seekg(0, std::ios::end); + if(f.fail()) + { + f.clear(); + f.seekg(oldpos); + f.clear(); + return false; + } + std::streampos length = f.tellg(); + if(f.fail() || length == std::streampos(-1)) + { + f.clear(); + f.seekg(oldpos); + f.clear(); + return false; + } + f.seekg(oldpos); + f.clear(); + return true; +} +bool IsWriteSeekable(std::ostream & f) +{ + f.clear(); + std::streampos oldpos = f.tellp(); + if(f.fail() || oldpos == std::streampos(-1)) + { + f.clear(); + return false; + } + f.seekp(0, std::ios::beg); + if(f.fail()) + { + f.clear(); + f.seekp(oldpos); + f.clear(); + return false; + } + f.seekp(0, std::ios::end); + if(f.fail()) + { + f.clear(); + f.seekp(oldpos); + f.clear(); + return false; + } + std::streampos length = f.tellp(); + if(f.fail() || length == std::streampos(-1)) + { + f.clear(); + f.seekp(oldpos); + f.clear(); + return false; + } + f.seekp(oldpos); + f.clear(); + return true; +} IO::Offset TellRead(std::istream & f) { return f.tellg(); @@ -134,150 +115,60 @@ IO::Offset TellWrite(std::ostream & f) } bool SeekBegin(std::ostream & f) { - #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; - } - #endif f.seekp(0); return !f.fail(); } bool SeekBegin(std::istream & f) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - #endif f.seekg(0); return !f.fail(); } bool SeekBegin(std::iostream & f) { - #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; - } - #endif f.seekg(0); f.seekp(0); return !f.fail(); } bool SeekEnd(std::ostream & f) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - #endif f.seekp(0, std::ios::end); return !f.fail(); } bool SeekEnd(std::istream & f) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - #endif f.seekg(0, std::ios::end); return !f.fail(); } bool SeekEnd(std::iostream & f) { - #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; - } - #endif f.seekg(0, std::ios::end); f.seekp(0, std::ios::end); return !f.fail(); } bool SeekAbsolute(std::ostream & f, IO::Offset pos) { if(!OffsetFits(pos)) { return false; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - if(pos == 0) - { - f.seekp(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - } - #endif f.seekp(static_cast(pos), std::ios::beg); return !f.fail(); } bool SeekAbsolute(std::istream & f, IO::Offset pos) { if(!OffsetFits(pos)) { return false; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - if(pos == 0) - { - f.seekg(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - } - #endif f.seekg(static_cast(pos), std::ios::beg); return !f.fail(); } bool SeekAbsolute(std::iostream & f, IO::Offset pos) { if(!OffsetFits(pos)) { return false; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - if(pos == 0) - { - f.seekg(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(static_cast(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - } - #endif f.seekg(static_cast(pos), std::ios::beg); f.seekp(static_cast(pos), std::ios::beg); return !f.fail(); } bool SeekRelative(std::ostream & f, IO::Offset off) { if(!OffsetFits(off)) { return false; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - if(off == 0) - { - f.seekp(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - } - #endif f.seekp(static_cast(off), std::ios::cur); return !f.fail(); } bool SeekRelative(std::istream & f, IO::Offset off) { if(!OffsetFits(off)) { return false; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - if(off == 0) - { - f.seekg(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - } - #endif f.seekg(static_cast(off), std::ios::cur); return !f.fail(); } bool SeekRelative(std::iostream & f, IO::Offset off) { if(!OffsetFits(off)) { return false; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - if(StreamIsStringStreamAndValidAndEmpty(f)) - { - if(off == 0) - { - f.seekg(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(static_cast(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true; - } - } - #endif f.seekg(static_cast(off), std::ios::cur); f.seekp(static_cast(off), std::ios::cur); return !f.fail(); } -IO::Offset ReadRawImpl(std::istream & f, mpt::byte * data, std::size_t size) { return f.read(mpt::byte_cast(data), size) ? f.gcount() : std::streamsize(0); } -bool WriteRawImpl(std::ostream & f, const mpt::byte * data, std::size_t size) { f.write(mpt::byte_cast(data), size); return !f.fail(); } +IO::Offset ReadRawImpl(std::istream & f, mpt::byte_span data) { f.read(mpt::byte_cast(data.data()), data.size()); return f.gcount(); } +bool WriteRawImpl(std::ostream & f, mpt::const_byte_span data) { f.write(mpt::byte_cast(data.data()), data.size()); return !f.fail(); } bool IsEof(std::istream & f) { return f.eof(); } bool Flush(std::ostream & f) { f.flush(); return !f.fail(); } @@ -289,15 +180,19 @@ bool Flush(std::ostream & f) { f.flush(); return !f.fail(); } -#if defined(MPT_FILEREADER_STD_ISTREAM) - - - -FileDataContainerSeekable::FileDataContainerSeekable(off_t streamLength) +FileDataContainerSeekable::FileDataContainerSeekable(off_t streamLength, bool buffered) : streamLength(streamLength) , cached(false) + , m_Buffered(buffered) + , m_Buffer(m_Buffered ? static_cast(BUFFER_SIZE) : 0) { - return; + if(m_Buffered) + { + for(std::size_t chunkIndex = 0; chunkIndex < NUM_CHUNKS; ++chunkIndex) + { + m_ChunkIndexLRU[chunkIndex] = chunkIndex; + } + } } void FileDataContainerSeekable::CacheStream() const @@ -306,11 +201,48 @@ void FileDataContainerSeekable::CacheStream() const { return; } + if(m_Buffered) + { + m_Buffered = false; + for (std::size_t chunkIndex = 0; chunkIndex < NUM_CHUNKS; ++chunkIndex) + { + m_ChunkInfo[chunkIndex].ChunkValid = false; + } + m_Buffer.resize(0); + m_Buffer.shrink_to_fit(); + } cache.resize(streamLength); InternalRead(cache.data(), 0, streamLength); cached = true; } +std::size_t FileDataContainerSeekable::InternalFillPageAndReturnIndex(off_t pos) const +{ + pos = Util::AlignDown(pos, static_cast(CHUNK_SIZE)); + for(std::size_t chunkLRUIndex = 0; chunkLRUIndex < NUM_CHUNKS; ++chunkLRUIndex) + { + std::size_t chunkIndex = m_ChunkIndexLRU[chunkLRUIndex]; + if(m_ChunkInfo[chunkIndex].ChunkValid && (m_ChunkInfo[chunkIndex].ChunkOffset == pos)) + { + std::size_t chunk = std::move(m_ChunkIndexLRU[chunkLRUIndex]); + std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + chunkLRUIndex, m_ChunkIndexLRU.begin() + (chunkLRUIndex + 1)); + m_ChunkIndexLRU[0] = std::move(chunk); + return chunkIndex; + } + } + { + std::size_t chunk = std::move(m_ChunkIndexLRU[NUM_CHUNKS - 1]); + std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + (NUM_CHUNKS - 1), m_ChunkIndexLRU.begin() + NUM_CHUNKS); + m_ChunkIndexLRU[0] = std::move(chunk); + } + std::size_t chunkIndex = m_ChunkIndexLRU[0]; + chunk_info& chunk = m_ChunkInfo[chunkIndex]; + chunk.ChunkOffset = pos; + chunk.ChunkLength = InternalRead(chunk_data(chunkIndex).data(), pos, CHUNK_SIZE); + chunk.ChunkValid = true; + return chunkIndex; +} + bool FileDataContainerSeekable::IsValid() const { return true; @@ -326,7 +258,7 @@ bool FileDataContainerSeekable::HasPinnedView() const return cached; } -const mpt::byte *FileDataContainerSeekable::GetRawData() const +const std::byte *FileDataContainerSeekable::GetRawData() const { CacheStream(); return cache.data(); @@ -337,57 +269,51 @@ IFileDataContainer::off_t FileDataContainerSeekable::GetLength() const return streamLength; } -IFileDataContainer::off_t FileDataContainerSeekable::Read(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +IFileDataContainer::off_t FileDataContainerSeekable::Read(std::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const { if(cached) { - IFileDataContainer::off_t cache_avail = std::min(IFileDataContainer::off_t(cache.size()) - pos, count); + IFileDataContainer::off_t cache_avail = std::min(IFileDataContainer::off_t(cache.size()) - pos, count); std::copy(cache.begin() + pos, cache.begin() + pos + cache_avail, dst); return cache_avail; } else + { + return InternalReadBuffered(dst, pos, count); + } +} + +IFileDataContainer::off_t FileDataContainerSeekable::InternalReadBuffered(std::byte* dst, off_t pos, off_t count) const +{ + if(!m_Buffered) { return InternalRead(dst, pos, count); } + off_t totalRead = 0; + while (count > 0) + { + std::size_t chunkIndex = InternalFillPageAndReturnIndex(pos); + off_t pageSkip = pos - m_ChunkInfo[chunkIndex].ChunkOffset; + off_t chunkWanted = std::min(static_cast(CHUNK_SIZE) - pageSkip, count); + off_t chunkGot = (m_ChunkInfo[chunkIndex].ChunkLength > pageSkip) ? (m_ChunkInfo[chunkIndex].ChunkLength - pageSkip) : 0; + off_t chunk = std::min(chunkWanted, chunkGot); + std::copy(chunk_data(chunkIndex).data() + pageSkip, chunk_data(chunkIndex).data() + pageSkip + chunk, dst); + pos += chunk; + dst += chunk; + totalRead += chunk; + count -= chunk; + if (chunkWanted > chunk) + { + return totalRead; + } + } + return totalRead; } bool FileDataContainerStdStreamSeekable::IsSeekable(std::istream *stream) { - stream->clear(); - std::streampos oldpos = stream->tellg(); - if(stream->fail() || oldpos == std::streampos(-1)) - { - stream->clear(); - return false; - } - stream->seekg(0, std::ios::beg); - if(stream->fail()) - { - stream->clear(); - stream->seekg(oldpos); - stream->clear(); - return false; - } - stream->seekg(0, std::ios::end); - if(stream->fail()) - { - stream->clear(); - stream->seekg(oldpos); - stream->clear(); - return false; - } - std::streampos length = stream->tellg(); - if(stream->fail() || length == std::streampos(-1)) - { - stream->clear(); - stream->seekg(oldpos); - stream->clear(); - return false; - } - stream->seekg(oldpos); - stream->clear(); - return true; + return mpt::IO::IsReadSeekable(*stream); } IFileDataContainer::off_t FileDataContainerStdStreamSeekable::GetLength(std::istream *stream) @@ -401,13 +327,13 @@ IFileDataContainer::off_t FileDataContainerStdStreamSeekable::GetLength(std::ist } FileDataContainerStdStreamSeekable::FileDataContainerStdStreamSeekable(std::istream *s) - : FileDataContainerSeekable(GetLength(s)) + : FileDataContainerSeekable(GetLength(s), true) , stream(s) { return; } -IFileDataContainer::off_t FileDataContainerStdStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const +IFileDataContainer::off_t FileDataContainerStdStreamSeekable::InternalRead(std::byte *dst, off_t pos, off_t count) const { stream->clear(); // tellg needs eof and fail bits unset std::streampos currentpos = stream->tellg(); @@ -487,7 +413,7 @@ void FileDataContainerUnseekable::CacheStreamUpTo(off_t pos, off_t length) const streamFullyCached = true; } -void FileDataContainerUnseekable::ReadCached(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +void FileDataContainerUnseekable::ReadCached(std::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const { std::copy(cache.begin() + pos, cache.begin() + pos + count, dst); } @@ -507,7 +433,7 @@ bool FileDataContainerUnseekable::HasPinnedView() const return true; // we have the cache which is required for seeking anyway } -const mpt::byte *FileDataContainerUnseekable::GetRawData() const +const std::byte *FileDataContainerUnseekable::GetRawData() const { CacheStream(); return cache.data(); @@ -519,14 +445,14 @@ IFileDataContainer::off_t FileDataContainerUnseekable::GetLength() const return cachesize; } -IFileDataContainer::off_t FileDataContainerUnseekable::Read(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +IFileDataContainer::off_t FileDataContainerUnseekable::Read(std::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const { CacheStreamUpTo(pos, count); if(pos >= IFileDataContainer::off_t(cachesize)) { return 0; } - IFileDataContainer::off_t cache_avail = std::min(IFileDataContainer::off_t(cachesize) - pos, count); + IFileDataContainer::off_t cache_avail = std::min(IFileDataContainer::off_t(cachesize) - pos, count); ReadCached(dst, pos, cache_avail); return cache_avail; } @@ -552,7 +478,7 @@ IFileDataContainer::off_t FileDataContainerUnseekable::GetReadableLength(IFileDa { return 0; } - return std::min(cachesize - pos, length); + return std::min(static_cast(cachesize) - pos, length); } @@ -574,7 +500,7 @@ bool FileDataContainerStdStream::InternalEof() const } } -IFileDataContainer::off_t FileDataContainerStdStream::InternalRead(mpt::byte *dst, off_t count) const +IFileDataContainer::off_t FileDataContainerStdStream::InternalRead(std::byte *dst, off_t count) const { stream->read(mpt::byte_cast(dst), count); return static_cast(stream->gcount()); @@ -664,13 +590,13 @@ IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::GetLength(Cal } FileDataContainerCallbackStreamSeekable::FileDataContainerCallbackStreamSeekable(CallbackStream s) - : FileDataContainerSeekable(GetLength(s)) + : FileDataContainerSeekable(GetLength(s), false) , stream(s) { return; } -IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const +IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::InternalRead(std::byte *dst, off_t pos, off_t count) const { if(!stream.read) { @@ -710,7 +636,7 @@ bool FileDataContainerCallbackStream::InternalEof() const return eof_reached; } -IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(mpt::byte *dst, off_t count) const +IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(std::byte *dst, off_t count) const { if(eof_reached) { @@ -741,8 +667,5 @@ IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(mpt::byt #endif // MPT_FILEREADER_CALLBACK_STREAM -#endif - - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptIO.h index 24825bd33..19a28c108 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptIO.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.h @@ -50,6 +50,8 @@ inline bool OffsetFits(IO::Offset off) bool IsValid(std::ostream & f); bool IsValid(std::istream & f); bool IsValid(std::iostream & f); +bool IsReadSeekable(std::istream& f); +bool IsWriteSeekable(std::ostream& f); IO::Offset TellRead(std::istream & f); IO::Offset TellWrite(std::ostream & f); bool SeekBegin(std::ostream & f); @@ -64,8 +66,8 @@ bool SeekAbsolute(std::iostream & f, IO::Offset pos); bool SeekRelative(std::ostream & f, IO::Offset off); bool SeekRelative(std::istream & f, IO::Offset off); bool SeekRelative(std::iostream & f, IO::Offset off); -IO::Offset ReadRawImpl(std::istream & f, mpt::byte * data, std::size_t size); -bool WriteRawImpl(std::ostream & f, const mpt::byte * data, std::size_t size); +IO::Offset ReadRawImpl(std::istream & f, mpt::byte_span data); +bool WriteRawImpl(std::ostream & f, mpt::const_byte_span data); bool IsEof(std::istream & f); bool Flush(std::ostream & f); @@ -74,14 +76,16 @@ bool Flush(std::ostream & f); template class WriteBuffer; template bool IsValid(WriteBuffer & f) { return IsValid(f.file()); } +template bool IsReadSeekable(WriteBuffer & f) { return IsReadSeekable(f.file()); } +template bool IsWriteSeekable(WriteBuffer & f) { return IsWriteSeekable(f.file()); } template IO::Offset TellRead(WriteBuffer & f) { f.FlushLocal(); return TellRead(f.file()); } template IO::Offset TellWrite(WriteBuffer & f) { return TellWrite(f.file()) + f.GetCurrentSize(); } template bool SeekBegin(WriteBuffer & f) { f.FlushLocal(); return SeekBegin(f.file()); } template bool SeekEnd(WriteBuffer & f) { f.FlushLocal(); return SeekEnd(f.file()); } -template bool SeekAbsolute(WriteBuffer & f, IO::Offset pos) { return f.FlushLocal(); SeekAbsolute(f.file(), pos); } -template bool SeekRelative(WriteBuffer & f, IO::Offset off) { return f.FlushLocal(); SeekRelative(f.file(), off); } -template IO::Offset ReadRawImpl(WriteBuffer & f, mpt::byte * data, std::size_t size) { f.FlushLocal(); 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 SeekAbsolute(WriteBuffer & f, IO::Offset pos) { f.FlushLocal(); return SeekAbsolute(f.file(), pos); } +template bool SeekRelative(WriteBuffer & f, IO::Offset off) { f.FlushLocal(); return SeekRelative(f.file(), off); } +template IO::Offset ReadRawImpl(WriteBuffer & f, mpt::byte_span data) { f.FlushLocal(); return ReadRawImpl(f.file(), data); } +template bool WriteRawImpl(WriteBuffer & f, mpt::const_byte_span data) { return f.Write(data); } template bool IsEof(WriteBuffer & f) { f.FlushLocal(); return IsEof(f.file()); } template bool Flush(WriteBuffer & f) { f.FlushLocal(); return Flush(f.file()); } @@ -125,7 +129,7 @@ template bool SeekRelative(std::pair, IO::Offs f.second += off; return true; } -template IO::Offset ReadRawImpl(std::pair, IO::Offset> & f, mpt::byte * data, std::size_t size) +template IO::Offset ReadRawImpl(std::pair, IO::Offset> & f, mpt::byte_span data) { if(f.second < 0) { @@ -135,27 +139,27 @@ template IO::Offset ReadRawImpl(std::pair, IO: { return 0; } - std::size_t num = mpt::saturate_cast(std::min(f.first.size() - f.second, size)); - std::copy(mpt::byte_cast(f.first.data() + f.second), mpt::byte_cast(f.first.data() + f.second + num), data); + std::size_t num = mpt::saturate_cast(std::min(static_cast(f.first.size()) - f.second, static_cast(data.size()))); + std::copy(mpt::byte_cast(f.first.data() + f.second), mpt::byte_cast(f.first.data() + f.second + num), data.data()); f.second += num; return num; } -template bool WriteRawImpl(std::pair, IO::Offset> & f, const mpt::byte * data, std::size_t size) +template bool WriteRawImpl(std::pair, IO::Offset> & f, mpt::const_byte_span data) { if(f.second < 0) { return false; } - if(f.second >= static_cast(f.first.size())) + if(f.second > static_cast(f.first.size())) { return false; } - std::size_t num = mpt::saturate_cast(std::min(f.first.size() - f.second, size)); - if(num != size) + std::size_t num = mpt::saturate_cast(std::min(static_cast(f.first.size()) - f.second, static_cast(data.size()))); + if(num != data.size()) { return false; } - std::copy(data, data + num, mpt::byte_cast(f.first.data() + f.second)); + std::copy(data.data(), data.data() + num, mpt::byte_cast(f.first.data() + f.second)); f.second += num; return true; } @@ -165,7 +169,7 @@ template bool IsEof(std::pair, IO::Offset> & f } template bool Flush(std::pair, IO::Offset> & f) { - MPT_UNREFERENCED_PARAMTER(f); + MPT_UNREFERENCED_PARAMETER(f); return true; } @@ -174,25 +178,25 @@ template bool Flush(std::pair, IO::Offset> & f template inline IO::Offset ReadRaw(Tfile & f, Tbyte * data, std::size_t size) { - return IO::ReadRawImpl(f, mpt::byte_cast(data), size); + return IO::ReadRawImpl(f, mpt::as_span(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()); + return IO::ReadRawImpl(f, mpt::byte_cast(data)); } template inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) { - return IO::WriteRawImpl(f, mpt::byte_cast(data), size); + return IO::WriteRawImpl(f, mpt::as_span(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()); + return IO::WriteRawImpl(f, mpt::byte_cast(data)); } template @@ -210,7 +214,7 @@ inline bool Write(Tfile & f, const Tbinary & v) template inline bool Write(Tfile & f, const std::vector & v) { - STATIC_ASSERT(mpt::is_binary_safe::value); + static_assert(mpt::is_binary_safe::value); return IO::WriteRaw(f, mpt::as_raw_memory(v)); } @@ -222,17 +226,17 @@ inline bool WritePartial(Tfile & f, const T & v, size_t size = sizeof(T)) } template -inline bool ReadByte(Tfile & f, mpt::byte & v) +inline bool ReadByte(Tfile & f, std::byte & v) { bool result = false; - mpt::byte byte = mpt::as_byte(0); - const IO::Offset readResult = IO::ReadRaw(f, &byte, sizeof(mpt::byte)); + std::byte byte = mpt::as_byte(0); + const IO::Offset readResult = IO::ReadRaw(f, &byte, sizeof(std::byte)); if(readResult < 0) { result = false; } else { - result = (static_cast(readResult) == sizeof(mpt::byte)); + result = (static_cast(readResult) == sizeof(std::byte)); } v = byte; return result; @@ -242,7 +246,7 @@ template inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) { bool result = false; - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, std::min(size, sizeof(T))); @@ -263,7 +267,7 @@ template inline bool ReadIntLE(Tfile & f, T & v) { bool result = false; - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); @@ -284,7 +288,7 @@ template inline bool ReadIntBE(Tfile & f, T & v) { bool result = false; - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); @@ -364,7 +368,7 @@ inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) template inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits::max()) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); str.clear(); Tsize size = 0; if(!mpt::IO::ReadIntLE(f, size)) @@ -391,14 +395,14 @@ inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std: template inline bool WriteIntLE(Tfile & f, const T v) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return IO::Write(f, mpt::as_le(v)); } template inline bool WriteIntBE(Tfile & f, const T v) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return IO::Write(f, mpt::as_be(v)); } @@ -448,10 +452,10 @@ inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSiz } else if(v < 0x400000 && minSize <= 3 && 3 <= maxSize) { uint32 value = static_cast(v << 2) | 0x02; - mpt::byte bytes[3]; - bytes[0] = static_cast(value >> 0); - bytes[1] = static_cast(value >> 8); - bytes[2] = static_cast(value >> 16); + std::byte bytes[3]; + bytes[0] = static_cast(value >> 0); + 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) { @@ -498,19 +502,19 @@ inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSiz template bool WriteVarInt(Tfile & f, const T v, size_t *bytesWritten = nullptr) { - STATIC_ASSERT(std::numeric_limits::is_integer); - STATIC_ASSERT(!std::numeric_limits::is_signed); - mpt::byte out[(sizeof(T) * 8 + 6) / 7]; + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + std::byte out[(sizeof(T) * 8 + 6) / 7]; size_t numBytes = 0; for(uint32 n = (sizeof(T) * 8) / 7; n > 0; n--) { if(v >= (static_cast(1) << (n * 7u))) { - out[numBytes++] = static_cast(((v >> (n * 7u)) & 0x7F) | 0x80); + out[numBytes++] = static_cast(((v >> (n * 7u)) & 0x7F) | 0x80); } } - out[numBytes++] = static_cast(v & 0x7F); - MPT_ASSERT(numBytes <= mpt::size(out)); + out[numBytes++] = static_cast(v & 0x7F); + MPT_ASSERT(numBytes <= std::size(out)); if(bytesWritten != nullptr) *bytesWritten = numBytes; return mpt::IO::WriteRaw(f, out, numBytes); } @@ -518,7 +522,7 @@ bool WriteVarInt(Tfile & f, const T v, size_t *bytesWritten = nullptr) template inline bool WriteSizedStringLE(Tfile & f, const std::string & str) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); if(str.size() > std::numeric_limits::max()) { return false; @@ -675,8 +679,6 @@ public: -#if defined(MPT_FILEREADER_STD_ISTREAM) - class IFileDataContainer { public: typedef std::size_t off_t; @@ -690,9 +692,9 @@ public: virtual bool IsValid() const = 0; virtual bool HasFastGetLength() const = 0; virtual bool HasPinnedView() const = 0; - virtual const mpt::byte *GetRawData() const = 0; + virtual const std::byte *GetRawData() const = 0; 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(std::byte *dst, off_t pos, off_t count) const = 0; virtual off_t Read(off_t pos, mpt::byte_span dst) const { @@ -720,7 +722,7 @@ public: { return 0; } - return std::min(length, dataLength - pos); + return std::min(length, dataLength - pos); } }; @@ -744,7 +746,7 @@ public: return true; } - const mpt::byte *GetRawData() const override + const std::byte *GetRawData() const override { return nullptr; } @@ -753,7 +755,7 @@ public: { return 0; } - off_t Read(mpt::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const override + off_t Read(std::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const override { return 0; } @@ -781,7 +783,7 @@ public: { return data->HasPinnedView(); } - const mpt::byte *GetRawData() const override + const std::byte *GetRawData() const override { return data->GetRawData() + dataOffset; } @@ -789,7 +791,7 @@ public: { return dataLength; } - off_t Read(mpt::byte *dst, off_t pos, off_t count) const override + off_t Read(std::byte *dst, off_t pos, off_t count) const override { if(pos >= dataLength) { @@ -827,11 +829,36 @@ private: off_t streamLength; mutable bool cached; - mutable std::vector cache; + mutable std::vector cache; + +private: + + mutable bool m_Buffered; + enum : std::size_t { + CHUNK_SIZE = mpt::IO::BUFFERSIZE_SMALL, + BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL + }; + enum : std::size_t { + NUM_CHUNKS = BUFFER_SIZE / CHUNK_SIZE + }; + struct chunk_info { + off_t ChunkOffset = 0; + off_t ChunkLength = 0; + bool ChunkValid = false; + }; + mutable std::vector m_Buffer; + mpt::byte_span chunk_data(std::size_t chunkIndex) const + { + return mpt::byte_span(m_Buffer.data() + (chunkIndex * CHUNK_SIZE), CHUNK_SIZE); + } + mutable std::array m_ChunkInfo; + mutable std::array m_ChunkIndexLRU; + + std::size_t InternalFillPageAndReturnIndex(off_t pos) const; protected: - FileDataContainerSeekable(off_t length); + FileDataContainerSeekable(off_t length, bool buffered); private: @@ -842,13 +869,15 @@ public: bool IsValid() const override; bool HasFastGetLength() const override; bool HasPinnedView() const override; - const mpt::byte *GetRawData() const override; + const std::byte *GetRawData() const override; off_t GetLength() const override; - off_t Read(mpt::byte *dst, off_t pos, off_t count) const override; + off_t Read(std::byte *dst, off_t pos, off_t count) const override; private: - virtual off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const = 0; + off_t InternalReadBuffered(std::byte* dst, off_t pos, off_t count) const; + + virtual off_t InternalRead(std::byte *dst, off_t pos, off_t count) const = 0; }; @@ -868,7 +897,7 @@ public: private: - off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t pos, off_t count) const override; }; @@ -877,7 +906,7 @@ class FileDataContainerUnseekable : public IFileDataContainer { private: - mutable std::vector cache; + mutable std::vector cache; mutable std::size_t cachesize; mutable bool streamFullyCached; @@ -898,23 +927,23 @@ private: private: - void ReadCached(mpt::byte *dst, off_t pos, off_t count) const; + void ReadCached(std::byte *dst, off_t pos, off_t count) const; public: bool IsValid() const override; bool HasFastGetLength() const override; bool HasPinnedView() const override; - const mpt::byte *GetRawData() const override; + const std::byte *GetRawData() const override; off_t GetLength() const override; - off_t Read(mpt::byte *dst, off_t pos, off_t count) const override; + off_t Read(std::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: virtual bool InternalEof() const = 0; - virtual off_t InternalRead(mpt::byte *dst, off_t count) const = 0; + virtual off_t InternalRead(std::byte *dst, off_t count) const = 0; }; @@ -932,7 +961,7 @@ public: private: bool InternalEof() const override; - off_t InternalRead(mpt::byte *dst, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t count) const override; }; @@ -963,7 +992,7 @@ public: static off_t GetLength(CallbackStream stream); FileDataContainerCallbackStreamSeekable(CallbackStream s); private: - off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t pos, off_t count) const override; }; @@ -976,36 +1005,20 @@ public: FileDataContainerCallbackStream(CallbackStream s); private: bool InternalEof() const override; - off_t InternalRead(mpt::byte *dst, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t count) const override; }; #endif // MPT_FILEREADER_CALLBACK_STREAM -#endif - - class FileDataContainerMemory -#if defined(MPT_FILEREADER_STD_ISTREAM) : public IFileDataContainer -#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; -#endif - private: - const mpt::byte *streamData; // Pointer to memory-mapped file + const std::byte *streamData; // Pointer to memory-mapped file off_t streamLength; // Size of memory-mapped file in bytes public: @@ -1014,48 +1027,48 @@ public: public: - bool IsValid() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool IsValid() const override { return streamData != nullptr; } - bool HasFastGetLength() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool HasFastGetLength() const override { return true; } - bool HasPinnedView() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool HasPinnedView() const override { return true; } - const mpt::byte *GetRawData() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + const std::byte *GetRawData() const override { return streamData; } - off_t GetLength() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t GetLength() const override { return streamLength; } - off_t Read(mpt::byte *dst, off_t pos, off_t count) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t Read(std::byte *dst, off_t pos, off_t count) const override { if(pos >= streamLength) { return 0; } - off_t avail = std::min(streamLength - pos, count); + off_t avail = std::min(streamLength - pos, count); std::copy(streamData + pos, streamData + pos + avail, dst); return avail; } - off_t Read(off_t pos, mpt::byte_span dst) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t Read(off_t pos, mpt::byte_span dst) const override { return Read(dst.data(), pos, dst.size()); } - bool CanRead(off_t pos, off_t length) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool CanRead(off_t pos, off_t length) const override { if((pos == streamLength) && (length == 0)) { @@ -1068,13 +1081,13 @@ public: return (length <= streamLength - pos); } - off_t GetReadableLength(off_t pos, off_t length) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t GetReadableLength(off_t pos, off_t length) const override { if(pos >= streamLength) { return 0; } - return std::min(length, streamLength - pos); + return std::min(length, streamLength - pos); } }; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp index 41b422782..6073a8666 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp @@ -100,7 +100,7 @@ public: if(WindowsVersion.IsAtLeast(mpt::Windows::Version::Win8)) { hasKB2533623 = true; - } else if(WindowsVersion.IsAtLeast(mpt::Windows::Version::WinVista)) + } else { HMODULE hKernel32DLL = LoadLibraryW(L"kernel32.dll"); if(hKernel32DLL) @@ -133,7 +133,7 @@ public: // Just rely on the default search path here. case mpt::LibrarySearchPathApplication: { - const mpt::PathString dllPath = mpt::GetAppPath(); + const mpt::PathString dllPath = mpt::GetExecutablePath(); if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) { hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str()); @@ -165,7 +165,7 @@ public: break; case mpt::LibrarySearchPathApplication: { - const mpt::PathString dllPath = mpt::GetAppPath(); + const mpt::PathString dllPath = mpt::GetExecutablePath(); if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) { hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str()); @@ -194,6 +194,10 @@ public: } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { if(IsValid()) @@ -237,6 +241,10 @@ public: return; } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { return; @@ -287,6 +295,10 @@ public: handle = lt_dlopenext(path.GetFileName().AsNative().c_str()); } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { if(IsValid()) @@ -338,6 +350,10 @@ public: handle = dlopen(path.GetFileName().AsNative().c_str(), RTLD_NOW); } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { if(IsValid()) @@ -381,6 +397,10 @@ public: return; } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { return; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h index d182b910b..b865f3e54 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h @@ -104,7 +104,7 @@ public: #if !(MPT_OS_WINDOWS && MPT_COMPILER_GCC) // MinGW64 std::is_function is always false for non __cdecl functions. // See https://connect.microsoft.com/VisualStudio/feedback/details/774720/stl-is-function-bug . - STATIC_ASSERT(std::is_function::value); + static_assert(std::is_function::value); #endif const FuncPtr addr = GetProcAddress(symbol); f = reinterpret_cast(addr); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h b/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h index 539c58dec..e788dc118 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h @@ -21,6 +21,7 @@ #include #endif #include +#include #include @@ -36,8 +37,8 @@ namespace mpt { -typedef mpt::span byte_span; -typedef mpt::span const_byte_span; +typedef mpt::span byte_span; +typedef mpt::span const_byte_span; @@ -49,19 +50,15 @@ typedef mpt::span const_byte_span; 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 { }; 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 : public std::false_type { }; -template <> struct is_byte : public std::true_type { }; -template <> struct is_byte : public std::true_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. @@ -74,9 +71,7 @@ template struct is_binary_safe : public std::false_type { }; 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 +template <> struct is_binary_safe : public std::true_type { }; // Generic Specialization for arrays. template struct is_binary_safe : public is_binary_safe { }; @@ -88,9 +83,9 @@ template struct is_binary_safe::value); \ + static_assert(sizeof( type ) == (size) ); \ + static_assert(alignof( type ) == 1); \ + static_assert(std::is_standard_layout< type >::value); \ namespace mpt { \ template <> struct is_binary_safe< type > : public std::true_type { }; \ } \ @@ -122,7 +117,7 @@ struct value_initializer template inline void Clear(T & x) { - MPT_STATIC_ASSERT(!std::is_pointer::value); + static_assert(!std::is_pointer::value); value_initializer()(x); } @@ -132,13 +127,8 @@ 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 + static_assert(std::is_standard_layout::value); + static_assert((std::is_trivially_default_constructible::value && std::is_trivially_copyable::value) || mpt::is_binary_safe::value); std::memset(&a, 0, sizeof(T)); } @@ -152,39 +142,13 @@ namespace mpt { using std::bit_cast; #else // C++2a compatible bit_cast. -// See . -// Not implementing constexpr because this is not easily possible pre C++2a. +// Not implementing constexpr because this is not easily possible pre C++20. template -MPT_FORCEINLINE Tdst bit_cast(const Tsrc & src) noexcept +MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::is_trivially_copyable::value && std::is_trivially_copyable::value, Tdst>::type bit_cast(const Tsrc & src) noexcept { - 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 + Tdst dst{}; + std::memcpy(&dst, &src, sizeof(Tdst)); + return dst; } #endif @@ -195,12 +159,12 @@ 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(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(sizeof(Tdst) == sizeof(std::byte)); // not checking is_byte_castable here because we are actually // doing a static_cast and converting the value - STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); - STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return static_cast(src); } }; @@ -209,13 +173,13 @@ 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())); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + return mpt::as_span(mpt::byte_cast_impl()(src.data()), mpt::byte_cast_impl()(src.data() + src.size())); } }; template @@ -223,12 +187,12 @@ 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); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -241,9 +205,9 @@ 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); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -252,9 +216,9 @@ 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); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -263,9 +227,9 @@ 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); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -274,9 +238,9 @@ 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); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -298,10 +262,10 @@ inline Tdst void_cast(Tsrc src) template -MPT_CONSTEXPR14_FUN mpt::byte as_byte(T src) noexcept +MPT_CONSTEXPR14_FUN std::byte as_byte(T src) noexcept { - MPT_STATIC_ASSERT(std::is_integral::value); - return static_cast(static_cast(src)); + static_assert(std::is_integral::value); + return static_cast(static_cast(src)); } @@ -311,13 +275,13 @@ 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)); + 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)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(&v), sizeof(T)); } }; @@ -326,13 +290,13 @@ 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)); + 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)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } }; @@ -341,15 +305,15 @@ 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)); + 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 *), +// Using a wrapper (by default just around a cast to const std::byte *), // allows for implementing raw memory access // via on-demand generating a cached serialized representation. template inline mpt::const_byte_span as_raw_memory(const T & v) diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h index 669a0c350..8b021aa6f 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h @@ -174,7 +174,7 @@ public: #if MPT_MUTEX_STD -#define MPT_LOCK_GUARD std::lock_guard +template using lock_guard = std::lock_guard; #else // !MPT_MUTEX_STD @@ -188,8 +188,6 @@ public: ~lock_guard() { mutex.unlock(); } }; -#define MPT_LOCK_GUARD mpt::lock_guard - #endif // MPT_MUTEX_STD #ifdef MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp index 9d75ab755..f4d244998 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp @@ -15,10 +15,71 @@ #include #endif +#if defined(MODPLUG_TRACKER) +#if !MPT_OS_WINDOWS +#include +#endif // !MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + OPENMPT_NAMESPACE_BEGIN +#if defined(MODPLUG_TRACKER) + +namespace mpt +{ +namespace OS +{ + +mpt::OS::Class GetClassFromSysname(mpt::ustring sysname) +{ + mpt::OS::Class result = mpt::OS::Class::Unknown; + if(sysname == U_("")) + { + result = mpt::OS::Class::Unknown; + } else if(sysname == U_("Windows") || sysname == U_("WindowsNT") || sysname == U_("Windows_NT")) + { + result = mpt::OS::Class::Windows; + } else if(sysname == U_("Linux")) + { + result = mpt::OS::Class::Linux; + } else if(sysname == U_("Darwin")) + { + result = mpt::OS::Class::Darwin; + } else if(sysname == U_("FreeBSD") || sysname == U_("DragonFly") || sysname == U_("NetBSD") || sysname == U_("OpenBSD") || sysname == U_("MidnightBSD")) + { + result = mpt::OS::Class::BSD; + } else if(sysname == U_("Haiku")) + { + result = mpt::OS::Class::Haiku; + } else if(sysname == U_("MS-DOS")) + { + result = mpt::OS::Class::DOS; + } + return result; +} + +mpt::OS::Class GetClass() +{ + #if MPT_OS_WINDOWS + return mpt::OS::Class::Windows; + #else // !MPT_OS_WINDOWS + utsname uname_result; + if(uname(&uname_result) != 0) + { + return mpt::OS::Class::Unknown; + } + return mpt::OS::GetClassFromSysname(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(uname_result.sysname))); + #endif // MPT_OS_WINDOWS +} + +} // namespace OS +} // namespace mpt + +#endif // MODPLUG_TRACKER + + namespace mpt { namespace Windows @@ -52,7 +113,7 @@ static mpt::Windows::Version VersionFromNTDDI_VERSION() noexcept mpt::Windows::Version::WinNT4 #endif ; - return mpt::Windows::Version(System, mpt::Windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0); + return mpt::Windows::Version(System, mpt::Windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0); } @@ -77,6 +138,7 @@ static mpt::Windows::Version GatherWindowsVersion() noexcept #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4996) // 'GetVersionExW': was declared deprecated +#pragma warning(disable:28159) // Consider using 'IsWindows*' instead of 'GetVersionExW'. Reason: Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers. #endif // MPT_COMPILER_MSVC #if MPT_COMPILER_CLANG #pragma clang diagnostic push @@ -96,10 +158,17 @@ static mpt::Windows::Version GatherWindowsVersion() noexcept { return VersionFromNTDDI_VERSION(); } + DWORD dwProductType = 0; + dwProductType = PRODUCT_UNDEFINED; + if(GetProductInfo(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion, versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor, &dwProductType) == FALSE) + { + dwProductType = PRODUCT_UNDEFINED; + } return mpt::Windows::Version( mpt::Windows::Version::System(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion), mpt::Windows::Version::ServicePack(versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor), - versioninfoex.dwBuildNumber + versioninfoex.dwBuildNumber, + dwProductType ); #endif // MPT_OS_WINDOWS_WINRT } @@ -135,6 +204,7 @@ Version::Version() noexcept , m_System() , m_ServicePack() , m_Build() + , m_Type() { } @@ -145,11 +215,12 @@ Version Version::NoWindows() noexcept } -Version::Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build) noexcept +Version::Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build, mpt::Windows::Version::TypeId type) noexcept : m_SystemIsWindows(true) , m_System(system) , m_ServicePack(servicePack) , m_Build(build) + , m_Type(type) { } @@ -284,7 +355,13 @@ mpt::Windows::Version::Build Version::GetBuild() const noexcept } -static MPT_CONSTEXPR11_VAR struct { Version::System version; const MPT_UCHAR_TYPE * name; bool showDetails; } versionMap[] = +mpt::Windows::Version::TypeId Version::GetTypeId() const noexcept +{ + return m_Type; +} + + +static constexpr struct { Version::System version; const mpt::uchar * name; bool showDetails; } versionMap[] = { { mpt::Windows::Version::WinNewer, UL_("Windows 10 (or newer)"), false }, { mpt::Windows::Version::Win10, UL_("Windows 10"), true }, @@ -369,7 +446,7 @@ mpt::ustring Version::GetName() const } else { result = mpt::format(U_("Wine (unknown version: '%1') (%2)"))( - mpt::ToUnicode(mpt::CharsetUTF8, v.RawVersion()) + mpt::ToUnicode(mpt::Charset::UTF8, v.RawVersion()) , name ); } @@ -410,11 +487,7 @@ mpt::Windows::Version::System Version::GetMinimumKernelLevel() noexcept { uint64 minimumKernelVersion = 0; #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC - #if !defined(MPT_BUILD_TARGET_XP) - minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinVista); - #else - minimumKernelVersion = std::max(minimumKernelVersion, mpt::Windows::Version::WinXP); - #endif + minimumKernelVersion = std::max(minimumKernelVersion, static_cast(mpt::Windows::Version::WinVista)); #endif return mpt::Windows::Version::System(minimumKernelVersion); } @@ -502,7 +575,7 @@ struct ArchitectureInfo { Architecture Arch; int Bitness; - const MPT_UCHAR_TYPE * Name; + const mpt::uchar * Name; }; static constexpr ArchitectureInfo architectureInfo [] = { { Architecture::x86 , 32, UL_("x86") }, @@ -836,8 +909,7 @@ mpt::Wine::Version GetMinimumWineVersion() VersionContext::VersionContext() : m_IsWine(false) - , m_HostIsLinux(false) - , m_HostIsBSD(false) + , m_HostClass(mpt::OS::Class::Unknown) { #if MPT_OS_WINDOWS m_IsWine = mpt::Windows::IsWine(); @@ -869,9 +941,8 @@ VersionContext::VersionContext() m_RawHostSysName = wine_host_sysname ? wine_host_sysname : ""; m_RawHostRelease = wine_host_release ? wine_host_release : ""; } - m_Version = mpt::Wine::Version(mpt::ToUnicode(mpt::CharsetUTF8, m_RawVersion)); - m_HostIsLinux = (m_RawHostSysName == "Linux"); - m_HostIsBSD = (m_RawHostSysName == "FreeBSD" || m_RawHostSysName == "DragonFly" || m_RawHostSysName == "NetBSD" || m_RawHostSysName == "OpenBSD"); + m_Version = mpt::Wine::Version(mpt::ToUnicode(mpt::Charset::UTF8, m_RawVersion)); + m_HostClass = mpt::OS::GetClassFromSysname(mpt::ToUnicode(mpt::Charset::UTF8, m_RawHostSysName)); #endif // MPT_OS_WINDOWS } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h index 851ca9699..5888f71f8 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h @@ -19,6 +19,34 @@ OPENMPT_NAMESPACE_BEGIN +#if defined(MODPLUG_TRACKER) + +namespace mpt +{ +namespace OS +{ + +enum class Class +{ + Unknown, + Windows, + Linux, + Darwin, + BSD, + Haiku, + DOS, +}; + +mpt::OS::Class GetClassFromSysname(mpt::ustring sysname); + +mpt::OS::Class GetClass(); + +} // namespace OS +} // namespace mpt + +#endif // MODPLUG_TRACKER + + namespace mpt { namespace Windows @@ -92,6 +120,8 @@ public: typedef uint32 Build; + typedef uint32 TypeId; + static mpt::ustring VersionToString(mpt::Windows::Version::System version); private: @@ -101,6 +131,7 @@ private: System m_System; ServicePack m_ServicePack; Build m_Build; + TypeId m_Type; private: @@ -110,7 +141,7 @@ public: static Version NoWindows() noexcept; - Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build) noexcept; + Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build, mpt::Windows::Version::TypeId type) noexcept; public: @@ -131,6 +162,7 @@ public: mpt::Windows::Version::System GetSystem() const noexcept; mpt::Windows::Version::ServicePack GetServicePack() const noexcept; mpt::Windows::Version::Build GetBuild() const noexcept; + mpt::Windows::Version::TypeId GetTypeId() const noexcept; mpt::ustring GetName() const; #ifdef MODPLUG_TRACKER @@ -248,8 +280,7 @@ protected: std::string m_RawHostSysName; std::string m_RawHostRelease; mpt::Wine::Version m_Version; - bool m_HostIsLinux; - bool m_HostIsBSD; + mpt::OS::Class m_HostClass; public: VersionContext(); public: @@ -260,8 +291,7 @@ public: std::string RawHostSysName() const { return m_RawHostSysName; } std::string RawHostRelease() const { return m_RawHostRelease; } mpt::Wine::Version Version() const { return m_Version; } - bool HostIsLinux() const { return m_HostIsLinux; } - bool HostIsBSD() const { return m_HostIsBSD; } + mpt::OS::Class HostClass() const { return m_HostClass; } }; } // namespace Wine diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOSError.h b/Frameworks/OpenMPT/OpenMPT/common/mptOSError.h new file mode 100644 index 000000000..99f999be1 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOSError.h @@ -0,0 +1,99 @@ +/* + * mptOSError.h + * ------------ + * Purpose: OS-specific error message handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptBaseMacros.h" +#include "mptException.h" +#include "mptString.h" +#include "mptStringFormat.h" + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS + +namespace Windows +{ + + +inline mpt::ustring GetErrorMessage(DWORD errorCode, HANDLE hModule = NULL) +{ + mpt::ustring message; + void *lpMsgBuf = nullptr; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0) | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + hModule, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, + NULL); + if(!lpMsgBuf) + { + DWORD e = GetLastError(); + if((e == ERROR_NOT_ENOUGH_MEMORY) || (e == ERROR_OUTOFMEMORY)) + { + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + } + return {}; + } + try + { + message = mpt::ToUnicode(mpt::winstring((LPTSTR)lpMsgBuf)); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + LocalFree(lpMsgBuf); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } + LocalFree(lpMsgBuf); + return message; +} + + +class Error + : public std::runtime_error +{ +public: + Error(DWORD errorCode, HANDLE hModule = NULL) + : std::runtime_error(mpt::ToCharset(mpt::CharsetException, mpt::format(U_("Windows Error: 0x%1: %2"))(mpt::ufmt::hex0<8>(errorCode), GetErrorMessage(errorCode, hModule)))) + { + return; + } +}; + + +} // namespace Windows + +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOSException.h b/Frameworks/OpenMPT/OpenMPT/common/mptOSException.h new file mode 100644 index 000000000..6e5eed117 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOSException.h @@ -0,0 +1,188 @@ +/* + * mptOSException.h + * ---------------- + * Purpose: platform-specific exception/signal handling + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "mptBaseMacros.h" +#include "mptBaseTypes.h" + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) + + +#if MPT_OS_WINDOWS + + +namespace Windows +{ + + +namespace SEH +{ + + +struct Code +{ +private: + DWORD m_Code; +public: + constexpr Code(DWORD code) noexcept + : m_Code(code) + { + return; + } +public: + constexpr DWORD code() const noexcept + { + return m_Code; + } +}; + + +template +auto TryFilterHandleThrow(const Tfn &fn, const Tfilter &filter, const Thandler &handler) -> decltype(fn()) +{ + static_assert(std::is_trivially_copy_assignable::value); + static_assert(std::is_trivially_copy_constructible::value); + static_assert(std::is_trivially_move_assignable::value); + static_assert(std::is_trivially_move_constructible::value); + DWORD code = 0; + __try + { + return fn(); + } __except(filter(GetExceptionCode(), GetExceptionInformation())) + { + code = GetExceptionCode(); + } + throw Windows::SEH::Code(code); +} + + +template +void TryFilterHandleVoid(const Tfn &fn, const Tfilter &filter, const Thandler &handler) +{ + static_assert(std::is_same::value || std::is_trivially_copy_assignable::value); + static_assert(std::is_same::value || std::is_trivially_copy_constructible::value); + static_assert(std::is_same::value || std::is_trivially_move_assignable::value); + static_assert(std::is_same::value || std::is_trivially_move_constructible::value); + __try + { + fn(); + return; + } __except(filter(GetExceptionCode(), GetExceptionInformation())) + { + DWORD code = GetExceptionCode(); + handler(code); + } + return; +} + + +template +auto TryFilterHandleDefault(const Tfn &fn, const Tfilter &filter, const Thandler &handler, decltype(fn()) def = decltype(fn()){}) -> decltype(fn()) +{ + static_assert(std::is_trivially_copy_assignable::value); + static_assert(std::is_trivially_copy_constructible::value); + static_assert(std::is_trivially_move_assignable::value); + static_assert(std::is_trivially_move_constructible::value); + auto result = def; + __try + { + result = fn(); + } __except(filter(GetExceptionCode(), GetExceptionInformation())) + { + DWORD code = GetExceptionCode(); + result = handler(code); + } + return result; +} + + +template +auto TryReturnOrThrow(const Tfn &fn) -> decltype(fn()) +{ + return TryFilterHandleThrow( + fn, + [](auto code, auto eptr) + { + MPT_UNREFERENCED_PARAMETER(code); + MPT_UNREFERENCED_PARAMETER(eptr); + return EXCEPTION_EXECUTE_HANDLER; + }, + [](auto code) + { + throw Windows::SEH::Code(code); + }); +} + + +template +DWORD TryOrError(const Tfn &fn) +{ + DWORD result = DWORD{0}; + TryFilterHandleVoid( + fn, + [](auto code, auto eptr) + { + MPT_UNREFERENCED_PARAMETER(code); + MPT_UNREFERENCED_PARAMETER(eptr); + return EXCEPTION_EXECUTE_HANDLER; + }, + [&result](auto code) + { + result = code; + }); + return result; +} + + +template +auto TryReturnOrDefault(const Tfn &fn, decltype(fn()) def = decltype(fn()){}) -> decltype(fn()) +{ + return TryFilterHandleDefault( + fn, + [](auto code, auto eptr) + { + MPT_UNREFERENCED_PARAMETER(code); + MPT_UNREFERENCED_PARAMETER(eptr); + return EXCEPTION_EXECUTE_HANDLER; + }, + [def](auto code) + { + MPT_UNREFERENCED_PARAMETER(code); + return def; + }); +} + + +} // namspace SEH + + +} // namespace Windows + + +#endif // MPT_OS_WINDOWS + + +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp index 09876dd48..fabc7bca5 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp @@ -524,7 +524,7 @@ bool DeleteWholeDirectoryTree(mpt::PathString path) #if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE) -mpt::PathString GetAppPath() +mpt::PathString GetExecutablePath() { std::vector exeFileName(MAX_PATH); while(GetModuleFileName(0, exeFileName.data(), mpt::saturate_cast(exeFileName.size())) >= exeFileName.size()) @@ -578,8 +578,8 @@ mpt::PathString GetTempDirectory() return mpt::PathString::FromNative(tempPath.data()); } } - // use app directory as fallback - return mpt::GetAppPath(); + // use exe directory as fallback + return mpt::GetExecutablePath(); } mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension) @@ -735,7 +735,7 @@ void SanitizeFilename(mpt::u8string &str) } #endif // MPT_USTRING_MODE_UTF8 -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) void SanitizeFilename(CString &str) { for(int i = 0; i < str.GetLength(); i++) @@ -743,7 +743,7 @@ void SanitizeFilename(CString &str) str.SetAt(i, SanitizeFilenameChar(str.GetAt(i))); } } -#endif // MFC +#endif // MPT_WITH_MFC #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h index 8a5a6ec20..169dc5e0f 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN #define MPT_DEPRECATED_PATH -//#define MPT_DEPRECATED_PATH MPT_DEPRECATED +//#define MPT_DEPRECATED_PATH [[deprecated]] @@ -120,9 +120,9 @@ public: PathString GetDrive() const; // Drive letter + colon, e.g. "C:" or \\server\\share PathString GetDir() const; // Directory, e.g. "\OpenMPT\" PathString GetPath() const; // Drive + Dir, e.g. "C:\OpenMPT\" - PathString GetFileName() const; // File name without extension, e.g. "mptrack" + PathString GetFileName() const; // File name without extension, e.g. "OpenMPT" PathString GetFileExt() const; // Extension including dot, e.g. ".exe" - PathString GetFullFileName() const; // File name + extension, e.g. "mptrack.exe" + PathString GetFullFileName() const; // File name + extension, e.g. "OpenMPT.exe" // Verify if this path represents a valid directory on the file system. bool IsDirectory() const; @@ -204,27 +204,27 @@ public: #endif // conversions #if defined(MPT_ENABLE_CHARSET_LOCALE) - MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::CharsetLocale, path); } + MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::Charset::Locale, path); } #endif - std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, path); } + std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, path); } std::wstring ToWide() const { return mpt::ToWide(path); } mpt::ustring ToUnicode() const { return mpt::ToUnicode(path); } #if defined(MPT_ENABLE_CHARSET_LOCALE) - MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetLocale, path)); } - static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetLocale, path)); } + MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } + static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } #endif - static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetUTF8, path)); } + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::UTF8, path)); } static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToWin(path)); } static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWin(path)); } RawPathString AsNative() const { return path; } // Return native string, with possible \\?\ prefix if it exceeds MAX_PATH characters. RawPathString AsNativePrefixed() const; static PathString FromNative(const RawPathString &path) { return PathString(path); } -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) // CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE CString ToCString() const { return mpt::ToCString(path); } static PathString FromCString(const CString &path) { return PathString(mpt::ToWin(path)); } -#endif +#endif // MPT_WITH_MFC // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries mpt::PathString Simplify() const; @@ -234,39 +234,39 @@ public: // conversions #if defined(MPT_ENABLE_CHARSET_LOCALE) std::string ToLocale() const { return path; } - std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, path); } + std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, path); } #if MPT_WSTRING_CONVERT - std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetLocale, path); } + std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::Locale, path); } #endif - mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetLocale, path); } + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::Locale, path); } static PathString FromLocale(const std::string &path) { return PathString(path); } static PathString FromLocaleSilent(const std::string &path) { return PathString(path); } - static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, mpt::CharsetUTF8, path)); } + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, mpt::Charset::UTF8, path)); } #if MPT_WSTRING_CONVERT - static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); } + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); } #endif - static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); } + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); } RawPathString AsNative() const { return path; } RawPathString AsNativePrefixed() const { return path; } static PathString FromNative(const RawPathString &path) { return PathString(path); } #else // !MPT_ENABLE_CHARSET_LOCALE std::string ToUTF8() const { return path; } #if MPT_WSTRING_CONVERT - std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetUTF8, path); } + std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::UTF8, path); } #endif - mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetUTF8, path); } + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::UTF8, path); } static PathString FromUTF8(const std::string &path) { return PathString(path); } #if MPT_WSTRING_CONVERT - static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); } + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); } #endif - static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); } + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); } RawPathString AsNative() const { return path; } RawPathString AsNativePrefixed() const { return path; } static PathString FromNative(const RawPathString &path) { return PathString(path); } #endif // MPT_ENABLE_CHARSET_LOCALE // Convert a path to its simplified form (currently only implemented on Windows) - MPT_DEPRECATED mpt::PathString Simplify() const { return PathString(path); } + [[deprecated]] mpt::PathString Simplify() const { return PathString(path); } #endif // MPT_OS_WINDOWS @@ -277,12 +277,12 @@ 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()); } +[[deprecated]] static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } #else -MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.AsNative()); } +MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); } #endif #else -MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.ToUnicode()); } +MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } #endif #endif static inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); } @@ -339,8 +339,8 @@ bool DeleteWholeDirectoryTree(mpt::PathString path); #if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE) -// Returns the application path or an empty string (if unknown), e.g. "C:\mptrack\" -mpt::PathString GetAppPath(); +// Returns the application executable path or an empty string (if unknown), e.g. "C:\mptrack\" +mpt::PathString GetExecutablePath(); #endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE @@ -418,20 +418,20 @@ void SanitizeFilename(mpt::u8string &str); template void SanitizeFilename(char (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); SanitizeFilename(buffer, buffer + size); } template void SanitizeFilename(wchar_t (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); SanitizeFilename(buffer, buffer + size); } -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) void SanitizeFilename(CString &str); -#endif +#endif // MPT_WITH_MFC #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp index e4ba915e2..84e0db941 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp @@ -34,16 +34,16 @@ static T log2(T x) } -static MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x) +static MPT_CONSTEXPR14_FUN int lower_bound_entropy_bits(unsigned int x) { return detail::lower_bound_entropy_bits(x); } template -static inline bool is_mask(T x) +static MPT_CONSTEXPR14_FUN bool is_mask(T x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; unsigned_T ux = static_cast(x); unsigned_T mask = 0; @@ -86,7 +86,7 @@ static T generate_timeseed() { uint64be time; time = std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count(); - mpt::byte bytes[sizeof(time)]; + std::byte bytes[sizeof(time)]; std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } @@ -94,7 +94,7 @@ static T generate_timeseed() { uint64be time; time = std::chrono::duration_cast(std::chrono::high_resolution_clock().now().time_since_epoch()).count(); - mpt::byte bytes[sizeof(time)]; + std::byte bytes[sizeof(time)]; std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } @@ -126,8 +126,19 @@ crand::result_type crand::operator()() #endif // MODPLUG_TRACKER sane_random_device::sane_random_device() - : rd_reliable(rd.entropy() > 0.0) + : rd_reliable(false) { + try + { + prd = std::make_unique(); + rd_reliable = ((*prd).entropy() > 0.0); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(const std::exception &) + { + rd_reliable = false; + } if(!rd_reliable) { init_fallback(); @@ -136,9 +147,19 @@ sane_random_device::sane_random_device() sane_random_device::sane_random_device(const std::string & token_) : token(token_) - , rd(token) - , rd_reliable(rd.entropy() > 0.0) + , rd_reliable(false) { + try + { + prd = std::make_unique(token); + rd_reliable = ((*prd).entropy() > 0.0); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(const std::exception &) + { + rd_reliable = false; + } if(!rd_reliable) { init_fallback(); @@ -160,7 +181,7 @@ void sane_random_device::init_fallback() seeds.push_back(static_cast(static_cast(token[i]))); } std::seed_seq seed(seeds.begin(), seeds.end()); - rd_fallback = mpt::make_unique(seed); + rd_fallback = std::make_unique(seed); } else { uint64 seed_val = mpt::generate_timeseed(); @@ -168,53 +189,59 @@ void sane_random_device::init_fallback() seeds[0] = static_cast(seed_val >> 32); seeds[1] = static_cast(seed_val >> 0); std::seed_seq seed(seeds + 0, seeds + 2); - rd_fallback = mpt::make_unique(seed); + rd_fallback = std::make_unique(seed); } } } sane_random_device::result_type sane_random_device::operator()() { - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); result_type result = 0; - try + if(prd) { - if(rd.min() != 0 || !mpt::is_mask(rd.max())) - { // insane std::random_device - // This implementation is not exactly uniformly distributed but good enough - // for OpenMPT. - double rd_min = static_cast(rd.min()); - double rd_max = static_cast(rd.max()); - double rd_range = rd_max - rd_min; - double rd_size = rd_range + 1.0; - double rd_entropy = mpt::log2(rd_size); - int iterations = static_cast(std::ceil(result_bits() / rd_entropy)); - double tmp = 0.0; - for(int i = 0; i < iterations; ++i) - { - tmp = (tmp * rd_size) + (static_cast(rd()) - rd_min); - } - double result_01 = std::floor(tmp / std::pow(rd_size, iterations)); - result = static_cast(std::floor(result_01 * (static_cast(max() - min()) + 1.0))) + min(); - } else - { // sane std::random_device - result = 0; - std::size_t rd_bits = mpt::lower_bound_entropy_bits(rd.max()); - for(std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits) - { - if(rd_bits < (sizeof(result_type) * 8)) + try + { + if constexpr(std::random_device::min() != 0 || !mpt::is_mask(std::random_device::max())) + { // insane std::random_device + // This implementation is not exactly uniformly distributed but good enough + // for OpenMPT. + double rd_min = static_cast(std::random_device::min()); + double rd_max = static_cast(std::random_device::max()); + double rd_range = rd_max - rd_min; + double rd_size = rd_range + 1.0; + double rd_entropy = mpt::log2(rd_size); + int iterations = static_cast(std::ceil(result_bits() / rd_entropy)); + double tmp = 0.0; + for(int i = 0; i < iterations; ++i) { - result = (result << rd_bits) | static_cast(rd()); - } else + tmp = (tmp * rd_size) + (static_cast((*prd)()) - rd_min); + } + double result_01 = std::floor(tmp / std::pow(rd_size, iterations)); + result = static_cast(std::floor(result_01 * (static_cast(max() - min()) + 1.0))) + min(); + } else + { // sane std::random_device + result = 0; + std::size_t rd_bits = mpt::lower_bound_entropy_bits(std::random_device::max()); + for(std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits) { - result = result | static_cast(rd()); + if(rd_bits < (sizeof(result_type) * 8)) + { + result = (result << rd_bits) | static_cast((*prd)()); + } else + { + result = result | static_cast((*prd)()); + } } } + } catch(const std::exception &) + { + rd_reliable = false; + init_fallback(); } - } catch(const std::exception &) + } else { rd_reliable = false; - init_fallback(); } if(!rd_reliable) { // std::random_device is unreliable diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h index 1442e73ef..84a7c029b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h @@ -44,50 +44,113 @@ namespace mpt #ifdef MPT_BUILD_FUZZER -static const uint32 FUZZER_RNG_SEED = 3141592653u; // pi +static constexpr uint32 FUZZER_RNG_SEED = 3141592653u; // pi #endif // MPT_BUILD_FUZZER namespace detail { -MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x) +MPT_CONSTEXPR14_FUN int lower_bound_entropy_bits(unsigned int x) { - // easy to compile-time evaluate even for stupid compilers - return - x >= 0xffffffffu ? 32 : - x >= 0x7fffffffu ? 31 : - x >= 0x3fffffffu ? 30 : - x >= 0x1fffffffu ? 29 : - x >= 0x0fffffffu ? 28 : - x >= 0x07ffffffu ? 27 : - x >= 0x03ffffffu ? 26 : - x >= 0x01ffffffu ? 25 : - x >= 0x00ffffffu ? 24 : - x >= 0x007fffffu ? 23 : - x >= 0x003fffffu ? 22 : - x >= 0x001fffffu ? 21 : - x >= 0x000fffffu ? 20 : - x >= 0x0007ffffu ? 19 : - x >= 0x0003ffffu ? 18 : - x >= 0x0001ffffu ? 17 : - x >= 0x0000ffffu ? 16 : - x >= 0x00007fffu ? 15 : - x >= 0x00003fffu ? 14 : - x >= 0x00001fffu ? 13 : - x >= 0x00000fffu ? 12 : - x >= 0x000007ffu ? 11 : - x >= 0x000003ffu ? 10 : - x >= 0x000001ffu ? 9 : - x >= 0x000000ffu ? 8 : - x >= 0x0000007fu ? 7 : - x >= 0x0000003fu ? 6 : - x >= 0x0000001fu ? 5 : - x >= 0x0000000fu ? 4 : - x >= 0x00000007u ? 3 : - x >= 0x00000003u ? 2 : - x >= 0x00000001u ? 1 : - 0; + if(x >= 0xffffffffu) + { + return 32; + } else if(x >= 0x7fffffffu) + { + return 31; + } else if(x >= 0x3fffffffu) + { + return 30; + } else if(x >= 0x1fffffffu) + { + return 29; + } else if(x >= 0x0fffffffu) + { + return 28; + } else if(x >= 0x07ffffffu) + { + return 27; + } else if(x >= 0x03ffffffu) + { + return 26; + } else if(x >= 0x01ffffffu) + { + return 25; + } else if(x >= 0x00ffffffu) + { + return 24; + } else if(x >= 0x007fffffu) + { + return 23; + } else if(x >= 0x003fffffu) + { + return 22; + } else if(x >= 0x001fffffu) + { + return 21; + } else if(x >= 0x000fffffu) + { + return 20; + } else if(x >= 0x0007ffffu) + { + return 19; + } else if(x >= 0x0003ffffu) + { + return 18; + } else if(x >= 0x0001ffffu) + { + return 17; + } else if(x >= 0x0000ffffu) + { + return 16; + } else if(x >= 0x00007fffu) + { + return 15; + } else if(x >= 0x00003fffu) + { + return 14; + } else if(x >= 0x00001fffu) + { + return 13; + } else if(x >= 0x00000fffu) + { + return 12; + } else if(x >= 0x000007ffu) + { + return 11; + } else if(x >= 0x000003ffu) + { + return 10; + } else if(x >= 0x000001ffu) + { + return 9; + } else if(x >= 0x000000ffu) + { + return 8; + } else if(x >= 0x0000007fu) + { + return 7; + } else if(x >= 0x0000003fu) + { + return 6; + } else if(x >= 0x0000001fu) + { + return 5; + } else if(x >= 0x0000000fu) + { + return 4; + } else if(x >= 0x00000007u) + { + return 3; + } else if(x >= 0x00000003u) + { + return 2; + } else if(x >= 0x00000001u) + { + return 1; + } + return 0; } } @@ -111,15 +174,15 @@ template struct engine_traits template inline T random(Trng & rng) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::result_bits(); unsigned_T result = 0; for(std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits) { - MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8)) + if constexpr(rng_bits < (sizeof(T) * 8)) { - MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) result = (result << shift_bits) ^ static_cast(rng()); } else { @@ -132,22 +195,22 @@ inline T random(Trng & rng) template inline T random(Trng & rng) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::result_bits(); unsigned_T result = 0; - for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) + for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) { - MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8)) + if constexpr(rng_bits < (sizeof(T) * 8)) { - MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) result = (result << shift_bits) ^ static_cast(rng()); } else { result = static_cast(rng()); } } - MPT_CONSTANT_IF(required_entropy_bits >= (sizeof(T) * 8)) + if constexpr(required_entropy_bits >= (sizeof(T) * 8)) { return static_cast(result); } else @@ -159,15 +222,15 @@ inline T random(Trng & rng) template inline T random(Trng & rng, std::size_t required_entropy_bits) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::result_bits(); unsigned_T result = 0; - for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) + for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) { - MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8)) + if constexpr(rng_bits < (sizeof(T) * 8)) { - MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) result = (result << shift_bits) ^ static_cast(rng()); } else { @@ -183,20 +246,6 @@ inline T random(Trng & rng, std::size_t required_entropy_bits) } } -template struct float_traits { }; -template <> struct float_traits { - typedef uint32 mantissa_uint_type; - enum : int { mantissa_bits = 24 }; -}; -template <> struct float_traits { - typedef uint64 mantissa_uint_type; - enum : int { mantissa_bits = 53 }; -}; -template <> struct float_traits { - typedef uint64 mantissa_uint_type; - enum : int { mantissa_bits = 63 }; -}; - template struct uniform_real_distribution { @@ -213,9 +262,8 @@ public: template inline T operator()(Trng & rng) const { - typedef typename float_traits::mantissa_uint_type uint_type; - const int bits = float_traits::mantissa_bits; - return ((b - a) * static_cast(mpt::random(rng)) / static_cast((static_cast(1u) << bits))) + a; + const int mantissa_bits = std::numeric_limits::digits; + return ((b - a) * static_cast(mpt::random(rng)) / static_cast((static_cast(1u) << mantissa_bits))) + a; } }; @@ -223,7 +271,7 @@ public: template inline T random(Trng & rng, T min, T max) { - STATIC_ASSERT(!std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_integer); typedef mpt::uniform_real_distribution dis_type; dis_type dis(min, max); return static_cast(dis(rng)); @@ -265,12 +313,12 @@ public: } static MPT_CONSTEXPR11_FUN result_type max() { - STATIC_ASSERT(((result_mask >> result_shift) << result_shift) == result_mask); + static_assert(((result_mask >> result_shift) << result_shift) == result_mask); return static_cast(result_mask >> result_shift); } static MPT_CONSTEXPR11_FUN int result_bits() { - STATIC_ASSERT(((static_cast(1) << result_bits_) - 1) == (result_mask >> result_shift)); + static_assert(((static_cast(1) << result_bits_) - 1) == (result_mask >> result_shift)); return result_bits_; } inline result_type operator()() @@ -292,6 +340,59 @@ typedef lcg lcg_msvc typedef lcg lcg_c99; typedef lcg lcg_musl; +template +class modplug +{ +public: + typedef Tstate state_type; + typedef Tvalue result_type; +private: + state_type state1; + state_type state2; +public: + template + explicit inline modplug(Trng &rd) + : state1(mpt::random(rd)) + , state2(mpt::random(rd)) + { + } + explicit inline modplug(state_type seed1, state_type seed2) + : state1(seed1) + , state2(seed2) + { + } +public: + static MPT_CONSTEXPR11_FUN result_type min() + { + return static_cast(0); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return std::numeric_limits::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + static_assert(std::is_integral::value); + static_assert(std::is_unsigned::value); + return std::numeric_limits::digits; + } + inline result_type operator()() + { + state_type a = state1; + state_type b = state2; + a = mpt::rotl(a, rol1); + a ^= x1; + a += x2 + (b * x3); + b += mpt::rotl(a, rol2) * x4; + state1 = a; + state2 = b; + result_type result = static_cast(b); + return result; + } +}; + +typedef modplug modplug_dither; + } // namespace rng @@ -325,7 +426,7 @@ public: { return RAND_MAX; } - static MPT_CONSTEXPR11_FUN int result_bits() + static MPT_CONSTEXPR14_FUN int result_bits() { return detail::lower_bound_entropy_bits(RAND_MAX); } @@ -348,7 +449,7 @@ class sane_random_device private: mpt::mutex m; std::string token; - std::random_device rd; + std::unique_ptr prd; bool rd_reliable; std::unique_ptr rd_fallback; public: @@ -410,7 +511,7 @@ template <> struct engine_traits { static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { - std::unique_ptr> values = mpt::make_unique>(rd); + std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } @@ -423,7 +524,7 @@ template <> struct engine_traits { static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { - std::unique_ptr> values = mpt::make_unique>(rd); + std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } @@ -534,7 +635,7 @@ public: } result_type operator()() { - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); return mpt::random(rng); } }; @@ -609,7 +710,7 @@ public: public: typename engine_traits::result_type operator()() { - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); return Trng::operator()(); } }; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h b/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h index 064c198e2..02ec0b313 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h @@ -1,7 +1,7 @@ /* * mptSpan.h * --------- - * Purpose: Various useful utility functions. + * Purpose: C++20 span. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -15,8 +15,14 @@ #include "mptBaseTypes.h" +#if MPT_CXX_AT_LEAST(20) +#include +#include +#else // !C++20 #include #include +#include +#endif // C++20 @@ -29,6 +35,12 @@ namespace mpt { +#if MPT_CXX_AT_LEAST(20) + +using std::span; + +#else // !C++20 + // Simplified version of gsl::span. // Non-owning read-only or read-write view into a contiguous block of T // objects, i.e. equivalent to a (beg,end) or (data,size) tuple. @@ -40,18 +52,18 @@ class span public: - typedef std::size_t size_type; + using element_type = T; + using value_type = typename std::remove_cv::type; + using index_type = std::size_t; + using pointer = T *; + using const_pointer = const T *; + using reference = T &; + using const_reference = const T &; - typedef T value_type; - typedef T & reference; - typedef T * pointer; - typedef const T * const_pointer; - typedef const T & const_reference; + using iterator = pointer; + using const_iterator = const_pointer; - typedef pointer iterator; - typedef const_pointer const_iterator; - - typedef typename std::iterator_traits::difference_type difference_type; + using difference_type = typename std::iterator_traits::difference_type; private: @@ -60,22 +72,26 @@ private: public: - span() : m_beg(nullptr), m_end(nullptr) { } + span() noexcept : 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) { } + span(pointer data, index_type size) : m_beg(data), m_end(data + size) { } - template span(U (&arr)[N]) : m_beg(arr), m_end(arr + N) { } + template span(element_type (&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()) { } + template span(std::array &arr) : m_beg(arr.data()), m_end(arr.data() + arr.size()) { } + + template span(const std::array &arr) : m_beg(arr.data()), m_end(arr.data() + arr.size()) { } + + template span(Cont &cont) : m_beg(std::data(cont)), m_end(std::data(cont) + std::size(cont)) { } 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; } - + span & operator = (const span & other) noexcept = default; + iterator begin() const { return iterator(m_beg); } iterator end() const { return iterator(m_end); } @@ -84,24 +100,26 @@ public: 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); } + reference operator[](index_type index) { return at(index); } + const_reference operator[](index_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]; } + reference at(index_type index) { return m_beg[index]; } + const_reference at(index_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(); } + index_type size() const noexcept { return static_cast(std::distance(m_beg, m_end)); } + index_type length() const noexcept { return size(); } }; // class span +#endif // C++20 + template inline span as_span(T * beg, T * end) { return span(beg, end); } template inline span as_span(T * data, std::size_t size) { return span(data, size); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp index 823b6785b..b8a8563f3 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp @@ -12,17 +12,13 @@ #include "Endianness.h" -#if defined(MPT_CHARSET_CODECVTUTF8) -#include -#endif -#if defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) -#include -#endif #include #include #include #include +#include + #if defined(MODPLUG_TRACKER) #include #endif // MODPLUG_TRACKER @@ -35,11 +31,6 @@ #include #endif -#if defined(MPT_CHARSET_ICONV) -#include -#include -#endif - OPENMPT_NAMESPACE_BEGIN @@ -119,7 +110,7 @@ List of string types runtime conversion. Only use for string literals containing non-ascii characters (use MPT_USTRING otherwise). - * MPT_ULITERAL / MPT_UCHAR / MPT_UCHAR_TYPE (OpenMPT, libopenmpt) + * MPT_ULITERAL / MPT_UCHAR / mpt::uchar (OpenMPT, libopenmpt) Macros which generate string literals, char literals and the char literal type respectively. These are especially useful in constexpr contexts or global data where MPT_USTRING is either unusable or requires a global @@ -207,7 +198,7 @@ if in libopenmpt else if performance critical inner loop if needs unicode support - T = MPT_UCHAR_TYPE* / MPT_ULITERAL + T = mpt::uchar* / MPT_ULITERAL else T = char*, document the encoding if not clear from context fi @@ -227,7 +218,7 @@ else T = CString else if constexpr context or global data - T = MPT_UCHAR_TYPE* / MPT_ULITERAL + T = mpt::uchar* / MPT_ULITERAL else T = mpt::ustring fi @@ -312,7 +303,7 @@ namespace mpt { namespace String { /* default 1:1 mapping -static const uint32 CharsetTableISO8859_1[256] = { +static constexpr char32_t CharsetTableISO8859_1[256] = { 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, @@ -332,9 +323,7 @@ static const uint32 CharsetTableISO8859_1[256] = { }; */ -#if defined(MPT_CHARSET_CODECVTUTF8) || defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) - -static const uint32 CharsetTableISO8859_15[256] = { +static constexpr char32_t CharsetTableISO8859_15[256] = { 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, @@ -353,7 +342,7 @@ static const uint32 CharsetTableISO8859_15[256] = { 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff }; -static const uint32 CharsetTableWindows1252[256] = { +static constexpr char32_t CharsetTableWindows1252[256] = { 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, @@ -372,7 +361,7 @@ static const uint32 CharsetTableWindows1252[256] = { 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff }; -static const uint32 CharsetTableCP437[256] = { +static constexpr char32_t CharsetTableCP437[256] = { 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, @@ -391,14 +380,12 @@ static const uint32 CharsetTableCP437[256] = { 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 }; -#endif // MPT_CHARSET_CODECVTUTF8 || MPT_CHARSET_INTERNAL || MPT_CHARSET_WIN32 - #define C(x) (static_cast((x))) // AMS1 actually only supports ASCII plus the modified control characters and no high chars at all. // Just default to CP437 for those to keep things simple. -static const uint32 CharsetTableCP437AMS[256] = { +static constexpr char32_t CharsetTableCP437AMS[256] = { C(' '),0x0001,0x0002,0x0003,0x00e4,0x0005,0x00e5,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x00c4,0x00c5, // differs from CP437 0x0010,0x0011,0x0012,0x0013,0x00f6,0x0015,0x0016,0x0017,0x0018,0x00d6,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, // differs from CP437 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, @@ -418,7 +405,7 @@ static const uint32 CharsetTableCP437AMS[256] = { }; // AMS2: Looking at Velvet Studio's bitmap font (TPIC32.PCX), these appear to be the only supported non-ASCII chars. -static const uint32 CharsetTableCP437AMS2[256] = { +static constexpr char32_t CharsetTableCP437AMS2[256] = { C(' '),0x00a9,0x221a,0x00b7,C('0'),C('1'),C('2'),C('3'),C('4'),C('5'),C('6'),C('7'),C('8'),C('9'),C('A'),C('B'), // differs from CP437 C('C'),C('D'),C('E'),C('F'),C(' '),0x00a7,C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '), // differs from CP437 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, @@ -441,26 +428,106 @@ static const uint32 CharsetTableCP437AMS2[256] = { #if defined(MPT_COMPILER_QUIRK_NO_WCHAR) -typedef char32_t widechar; -typedef std::u32string widestring; +using widechar = char32_t; +using widestring = std::u32string; static constexpr widechar wide_default_replacement = 0xFFFD; #else // !MPT_COMPILER_QUIRK_NO_WCHAR -typedef wchar_t widechar; -typedef std::wstring widestring; +using widechar = wchar_t; +using widestring = std::wstring; 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) +#if MPT_OS_WINDOWS + +static bool TestCodePage(UINT cp) +{ + return IsValidCodePage(cp) ? true : false; +} + +static bool HasCharset(Charset charset) +{ + bool result = false; + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case Charset::Locale: result = true; break; +#endif + case Charset::UTF8: result = TestCodePage(CP_UTF8); break; + case Charset::ASCII: result = TestCodePage(20127); break; + case Charset::ISO8859_1: result = TestCodePage(28591); break; + case Charset::ISO8859_15: result = TestCodePage(28605); break; + case Charset::CP437: result = TestCodePage(437); break; + case Charset::Windows1252: result = TestCodePage(1252); break; + case Charset::CP437AMS: result = false; break; + case Charset::CP437AMS2: result = false; break; + } + return result; +} + +static UINT CharsetToCodepage(Charset charset) +{ + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case Charset::Locale: return CP_ACP; break; +#endif + case Charset::UTF8: return CP_UTF8; break; + case Charset::ASCII: return 20127; break; + case Charset::ISO8859_1: return 28591; break; + case Charset::ISO8859_15: return 28605; break; + case Charset::CP437: return 437; break; + case Charset::CP437AMS: return 437; break; // fallback, should not happen + case Charset::CP437AMS2: return 437; break; // fallback, should not happen + case Charset::Windows1252: return 1252; break; + } + return 0; +} + +template +static Tdststring EncodeCodepage(UINT codepage, const widestring &src) +{ + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + Tdststring encoded_string; + int required_size = WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), nullptr, 0, nullptr, nullptr); + if(required_size > 0) + { + encoded_string.resize(required_size); + WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), reinterpret_cast(encoded_string.data()), required_size, nullptr, nullptr); + } + return encoded_string; +} + +template +static widestring DecodeCodepage(UINT codepage, const Tsrcstring &src) +{ + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + widestring decoded_string; + int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), nullptr, 0); + if(required_size > 0) + { + decoded_string.resize(required_size); + MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), decoded_string.data(), required_size); + } + return decoded_string; +} + +#endif // MPT_OS_WINDOWS + + +template +static widestring From8bit(const Tsrcstring &str, const char32_t (&table)[256], widechar replacement = wide_default_replacement) { 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)) + std::size_t c = static_cast(static_cast(str[i])); + if(c < std::size(table)) { - res.push_back(static_cast(static_cast(table[c]))); + res.push_back(static_cast(table[c])); } else { res.push_back(replacement); @@ -469,22 +536,23 @@ static widestring From8bit(const std::string &str, const uint32 (&table)[256], w return res; } -static std::string To8bit(const widestring &str, const uint32 (&table)[256], char replacement = '?') +template +static Tdststring To8bit(const widestring &str, const char32_t (&table)[256], char replacement = '?') { - std::string res; + Tdststring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { - uint32 c = str[i]; + char32_t c = static_cast(str[i]); bool found = false; // Try non-control characters first. // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets), // characters in the common range are preferred this way. - for(std::size_t x = 0x20; x < mpt::size(table); ++x) + for(std::size_t x = 0x20; x < std::size(table); ++x) { if(c == table[x]) { - res.push_back(static_cast(static_cast(x))); + res.push_back(static_cast(static_cast(x))); found = true; break; } @@ -492,11 +560,11 @@ static std::string To8bit(const widestring &str, const uint32 (&table)[256], cha if(!found) { // try control characters - for(std::size_t x = 0x00; x < mpt::size(table) && x < 0x20; ++x) + for(std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) { if(c == table[x]) { - res.push_back(static_cast(static_cast(x))); + res.push_back(static_cast(static_cast(x))); found = true; break; } @@ -510,9 +578,8 @@ static std::string To8bit(const widestring &str, const uint32 (&table)[256], cha return res; } -#if defined(MPT_CHARSET_CODECVTUTF8) || defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) - -static widestring FromAscii(const std::string &str, widechar replacement = wide_default_replacement) +template +static widestring FromAscii(const Tsrcstring &str, widechar replacement = wide_default_replacement) { widestring res; res.reserve(str.length()); @@ -530,16 +597,17 @@ static widestring FromAscii(const std::string &str, widechar replacement = wide_ return res; } -static std::string ToAscii(const widestring &str, char replacement = '?') +template +static Tdststring ToAscii(const widestring &str, char replacement = '?') { - std::string res; + Tdststring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { - uint32 c = str[i]; + char32_t c = static_cast(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); @@ -548,7 +616,8 @@ static std::string ToAscii(const widestring &str, char replacement = '?') return res; } -static widestring FromISO_8859_1(const std::string &str, widechar replacement = wide_default_replacement) +template +static widestring FromISO_8859_1(const Tsrcstring &str, widechar replacement = wide_default_replacement) { MPT_UNREFERENCED_PARAMETER(replacement); widestring res; @@ -561,16 +630,17 @@ static widestring FromISO_8859_1(const std::string &str, widechar replacement = return res; } -static std::string ToISO_8859_1(const widestring &str, char replacement = '?') +template +static Tdststring ToISO_8859_1(const widestring &str, char replacement = '?') { - std::string res; + Tdststring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { - uint32 c = str[i]; + char32_t c = static_cast(str[i]); if(c <= 0xff) { - res.push_back(static_cast(static_cast(c))); + res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); @@ -603,7 +673,7 @@ static std::wstring LocaleDecode(const std::string &str, const std::locale & loc return std::wstring(); } std::vector out; - typedef std::codecvt codecvt_type; + using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; @@ -688,7 +758,7 @@ static std::string LocaleEncode(const std::wstring &str, const std::locale & loc return std::string(); } std::vector out; - typedef std::codecvt codecvt_type; + using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; @@ -766,12 +836,15 @@ static std::string LocaleEncode(const std::wstring &str, const std::locale & loc return std::string(&(out[0]), out_next); } -static std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\uFFFD') +static std::wstring FromLocaleCpp(const std::string &str, wchar_t replacement) { try { std::locale locale(""); // user locale return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -780,6 +853,9 @@ static std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\ { std::locale locale; // current c++ locale return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -788,20 +864,26 @@ static std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\ { std::locale locale = std::locale::classic(); // "C" locale return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing } MPT_ASSERT_NOTREACHED(); - return String::FromAscii(str, replacement); // fallback + return String::FromAscii(str, replacement); // fallback } -static std::string ToLocale(const std::wstring &str, char replacement = '?') +static std::string ToLocaleCpp(const std::wstring &str, char replacement) { try { std::locale locale(""); // user locale return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -810,6 +892,9 @@ static std::string ToLocale(const std::wstring &str, char replacement = '?') { std::locale locale; // current c++ locale return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -818,47 +903,55 @@ static std::string ToLocale(const std::wstring &str, char replacement = '?') { std::locale locale = std::locale::classic(); // "C" locale return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing } MPT_ASSERT_NOTREACHED(); - return String::ToAscii(str, replacement); // fallback + return String::ToAscii(str, replacement); // fallback } + +template +static std::wstring FromLocale(const Tsrcstring &str, wchar_t replacement = L'\uFFFD') +{ + std::string tmp(str.begin(), str.end()); + return FromLocaleCpp(tmp, replacement); +} +template <> +std::wstring FromLocale(const std::string &str, wchar_t replacement) +{ + return FromLocaleCpp(str, replacement); +} + +template +static Tdststring ToLocale(const std::wstring &str, char replacement = '?') +{ + std::string tmp = ToLocaleCpp(str, replacement); + return Tdststring(tmp.begin(), tmp.end()); +} +template <> +std::string ToLocale(const std::wstring &str, char replacement) +{ + return ToLocaleCpp(str, replacement); +} + + #endif // MPT_ENABLE_CHARSET_LOCALE && !MPT_LOCALE_ASSUME_CHARSET -#endif // MPT_CHARSET_CODECVTUTF8 || MPT_CHARSET_INTERNAL || MPT_CHARSET_WIN32 - -#if defined(MPT_CHARSET_CODECVTUTF8) - -static std::wstring FromUTF8(const std::string &str, wchar_t replacement = L'\uFFFD') +template +static widestring FromUTF8(const Tsrcstring &str, widechar replacement = wide_default_replacement) { - MPT_UNREFERENCED_PARAMETER(replacement); - std::wstring_convert > conv; - return conv.from_bytes(str); -} - -static std::string ToUTF8(const std::wstring &str, char replacement = '?') -{ - MPT_UNREFERENCED_PARAMETER(replacement); - std::wstring_convert > conv; - return conv.to_bytes(str); -} - -#endif // MPT_CHARSET_CODECVTUTF8 - -#if defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) - -static widestring FromUTF8(const std::string &str, widechar replacement = wide_default_replacement) -{ - const std::string &in = str; + const Tsrcstring &in = str; widestring out; // state: std::size_t charsleft = 0; - uint32 ucs4 = 0; + char32_t ucs4 = 0; for ( uint8 c : in ) { @@ -893,16 +986,16 @@ static widestring FromUTF8(const std::string &str, widechar replacement = wide_d charsleft--; if ( charsleft == 0 ) { - MPT_CONSTANT_IF ( sizeof( widechar ) == 2 ) { + if constexpr ( sizeof( widechar ) == 2 ) { if ( ucs4 > 0x1fffff ) { out.push_back( replacement ); ucs4 = 0; charsleft = 0; } if ( ucs4 <= 0xffff ) { - out.push_back( (uint16)ucs4 ); + out.push_back( static_cast(ucs4) ); } else { - uint32 surrogate = ucs4 - 0x10000; + uint32 surrogate = static_cast(ucs4) - 0x10000; uint16 hi_sur = static_cast( ( 0x36 << 10 ) | ( (surrogate>>10) & ((1<<10)-1) ) ); uint16 lo_sur = static_cast( ( 0x37 << 10 ) | ( (surrogate>> 0) & ((1<<10)-1) ) ); out.push_back( hi_sur ); @@ -928,18 +1021,19 @@ static widestring FromUTF8(const std::string &str, widechar replacement = wide_d } -static std::string ToUTF8(const widestring &str, char replacement = '?') +template +static Tdststring ToUTF8(const widestring &str, char replacement = '?') { const widestring &in = str; - std::string out; + Tdststring out; for ( std::size_t i=0; i( wc ); if ( i + 1 < in.length() ) { // check for surrogate pair @@ -953,14 +1047,14 @@ static std::string ToUTF8(const widestring &str, char replacement = '?') ucs4 = ( static_cast(hi_sur) << 10 ) | ( static_cast(lo_sur) << 0 ); } else { // no surrogate pair - ucs4 = static_cast( c ); + ucs4 = static_cast( c ); } } else { // no surrogate possible - ucs4 = static_cast( c ); + ucs4 = static_cast( c ); } } else { - ucs4 = static_cast( wc ); + ucs4 = static_cast( wc ); } if ( ucs4 > 0x1fffff ) { @@ -995,6 +1089,8 @@ static std::string ToUTF8(const widestring &str, char replacement = '?') if ( charsleft == numchars ) { out.push_back( utf8[ charsleft - 1 ] | ( ((1< -static Tdststring EncodeImplFallback(Charset charset, const widestring &src); -#endif // !MPT_CHARSET_ICONV // templated on 8bit strings because of type-safe variants template static Tdststring EncodeImpl(Charset charset, const widestring &src) { - 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; - if(charset == CharsetCP437AMS ) out = String::To8bit(src, CharsetTableCP437AMS ); - if(charset == CharsetCP437AMS2) out = String::To8bit(src, CharsetTableCP437AMS2); - return Tdststring(out.begin(), out.end()); - } -#if defined(MPT_ENABLE_CHARSET_LOCALE) - #if defined(MPT_LOCALE_ASSUME_CHARSET) - if(charset == CharsetLocale) - { - charset = MPT_LOCALE_ASSUME_CHARSET; - } - #endif -#endif - #if defined(MPT_CHARSET_WIN32) - if(!HasCharset(charset)) - { - return EncodeImplFallback(charset, src); - } - const UINT codepage = CharsetToCodepage(charset); - int required_size = WideCharToMultiByte(codepage, 0, src.c_str(), -1, nullptr, 0, nullptr, nullptr); - if(required_size <= 0) - { - return Tdststring(); - } - #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()); + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + #if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == Charset::Locale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } #endif - #elif defined(MPT_CHARSET_ICONV) - iconv_t conv = iconv_t(); - conv = iconv_open(CharsetToStringTranslit(charset), Charset_wchar_t()); - if(!conv) - { - conv = iconv_open(CharsetToString(charset), Charset_wchar_t()); - if(!conv) - { - throw std::runtime_error("iconv conversion not working"); - } - } - 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(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(widechar); - inbytesleft -= sizeof(widechar); - outbuf[0] = '?'; - outbuf++; - outbytesleft--; - iconv(conv, NULL, NULL, NULL, NULL); // reset state - } else - { - iconv_close(conv); - conv = iconv_t(); - return Tdststring(); - } - } - iconv_close(conv); - conv = iconv_t(); - return reinterpret_cast(encoded_string.data()); - #else - return EncodeImplFallback(charset, src); #endif -} - - -#if !defined(MPT_CHARSET_ICONV) -template -static Tdststring EncodeImplFallback(Charset charset, const widestring &src) -{ - std::string out; + #if MPT_OS_WINDOWS + if(HasCharset(charset)) + { + return EncodeCodepage(CharsetToCodepage(charset), src); + } + #endif + switch(charset) + { #if defined(MPT_ENABLE_CHARSET_LOCALE) #if defined(MPT_LOCALE_ASSUME_CHARSET) - if(charset == CharsetLocale) - { - charset = MPT_LOCALE_ASSUME_CHARSET; - } + case Charset::Locale: MPT_ASSERT_NOTREACHED(); break; + #else + case Charset::Locale: return String::ToLocale(src); break; #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; - case CharsetISO8859_1: out = String::ToISO_8859_1(src); break; - case CharsetISO8859_15: out = String::To8bit(src, CharsetTableISO8859_15); break; - case CharsetCP437: out = String::To8bit(src, CharsetTableCP437); break; - case CharsetCP437AMS: out = String::To8bit(src, CharsetTableCP437AMS); break; - case CharsetCP437AMS2: out = String::To8bit(src, CharsetTableCP437AMS2); break; - case CharsetWindows1252: out = String::To8bit(src, CharsetTableWindows1252); break; - } - return Tdststring(out.begin(), out.end()); + case Charset::UTF8: return String::ToUTF8(src); break; + case Charset::ASCII: return String::ToAscii(src); break; + case Charset::ISO8859_1: return String::ToISO_8859_1(src); break; + case Charset::ISO8859_15: return String::To8bit(src, CharsetTableISO8859_15); break; + case Charset::CP437: return String::To8bit(src, CharsetTableCP437); break; + case Charset::CP437AMS: return String::To8bit(src, CharsetTableCP437AMS); break; + case Charset::CP437AMS2: return String::To8bit(src, CharsetTableCP437AMS2); break; + case Charset::Windows1252: return String::To8bit(src, CharsetTableWindows1252); break; + } + return Tdststring(); } -#endif // !MPT_CHARSET_ICONV -#if !defined(MPT_CHARSET_ICONV) -template -static widestring DecodeImplFallback(Charset charset, const Tsrcstring &src); -#endif // !MPT_CHARSET_ICONV - // templated on 8bit strings because of type-safe variants template static widestring DecodeImpl(Charset charset, const Tsrcstring &src) { - 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()); - widestring out; - if(charset == CharsetCP437AMS ) out = String::From8bit(in, CharsetTableCP437AMS ); - if(charset == CharsetCP437AMS2) out = String::From8bit(in, CharsetTableCP437AMS2); - return out; - } -#if defined(MPT_ENABLE_CHARSET_LOCALE) - #if defined(MPT_LOCALE_ASSUME_CHARSET) - if(charset == CharsetLocale) - { - charset = MPT_LOCALE_ASSUME_CHARSET; - } - #endif -#endif - #if defined(MPT_CHARSET_WIN32) - if(!HasCharset(charset)) - { - return DecodeImplFallback(charset, src); - } - const UINT codepage = CharsetToCodepage(charset); - int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, nullptr, 0); - if(required_size <= 0) - { - return widestring(); - } - #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)); - if(!conv) - { - 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 - 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(widechar); - while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) - { - if(errno == EILSEQ || errno == EILSEQ) + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + #if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == Charset::Locale) { - inbuf++; - inbytesleft--; - for(std::size_t i = 0; i < sizeof(widechar); ++i) - { - outbuf[i] = 0; - } - 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 widestring(); + charset = MPT_LOCALE_ASSUME_CHARSET; } - } - iconv_close(conv); - conv = iconv_t(); - return wide_string.data(); - #else - return DecodeImplFallback(charset, src); + #endif #endif -} - -#if !defined(MPT_CHARSET_ICONV) -template -static widestring DecodeImplFallback(Charset charset, const Tsrcstring &src) -{ - std::string in(src.begin(), src.end()); - widestring out; + #if MPT_OS_WINDOWS + if(HasCharset(charset)) + { + return DecodeCodepage(CharsetToCodepage(charset), src); + } + #endif + switch(charset) + { #if defined(MPT_ENABLE_CHARSET_LOCALE) #if defined(MPT_LOCALE_ASSUME_CHARSET) - if(charset == CharsetLocale) - { - charset = MPT_LOCALE_ASSUME_CHARSET; - } + case Charset::Locale: MPT_ASSERT_NOTREACHED(); break; + #else + case Charset::Locale: return String::FromLocale(src); break; #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; - case CharsetISO8859_1: out = String::FromISO_8859_1(in); break; - case CharsetISO8859_15: out = String::From8bit(in, CharsetTableISO8859_15); break; - case CharsetCP437: out = String::From8bit(in, CharsetTableCP437); break; - case CharsetCP437AMS: out = String::From8bit(in, CharsetTableCP437AMS); break; - case CharsetCP437AMS2: out = String::From8bit(in, CharsetTableCP437AMS2); break; - case CharsetWindows1252: out = String::From8bit(in, CharsetTableWindows1252); break; - } - return out; + case Charset::UTF8: return String::FromUTF8(src); break; + case Charset::ASCII: return String::FromAscii(src); break; + case Charset::ISO8859_1: return String::FromISO_8859_1(src); break; + case Charset::ISO8859_15: return String::From8bit(src, CharsetTableISO8859_15); break; + case Charset::CP437: return String::From8bit(src, CharsetTableCP437); break; + case Charset::CP437AMS: return String::From8bit(src, CharsetTableCP437AMS); break; + case Charset::CP437AMS2: return String::From8bit(src, CharsetTableCP437AMS2); break; + case Charset::Windows1252: return String::From8bit(src, CharsetTableWindows1252); break; + } + return widestring(); } -#endif // !MPT_CHARSET_ICONV // templated on 8bit strings because of type-safe variants template static Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) { - STATIC_ASSERT(sizeof(typename Tdststring::value_type) == sizeof(char)); - STATIC_ASSERT(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); if(to == from) { const typename Tsrcstring::value_type * src_beg = src.data(); const typename Tsrcstring::value_type * src_end = src_beg + src.size(); return Tdststring(reinterpret_cast(src_beg), reinterpret_cast(src_end)); } - #if defined(MPT_CHARSET_ICONV) - if(to == CharsetCP437AMS || to == CharsetCP437AMS2 || from == CharsetCP437AMS || from == CharsetCP437AMS2) - { - return EncodeImpl(to, DecodeImpl(from, src)); - } - iconv_t conv = iconv_t(); - conv = iconv_open(CharsetToStringTranslit(to), CharsetToString(from)); - if(!conv) - { - conv = iconv_open(CharsetToString(to), CharsetToString(from)); - if(!conv) - { - throw std::runtime_error("iconv conversion not working"); - } - } - std::vector src_string(reinterpret_cast(src.c_str()), reinterpret_cast(src.c_str()) + src.length() + 1); - std::vector dst_string(src_string.size() * 8); // large enough - char * inbuf = src_string.data(); - size_t inbytesleft = src_string.size(); - char * outbuf = dst_string.data(); - size_t outbytesleft = dst_string.size(); - while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) - { - if(errno == EILSEQ || errno == EILSEQ) - { - inbuf++; - inbytesleft--; - outbuf[0] = '?'; - outbuf++; - outbytesleft--; - iconv(conv, NULL, NULL, NULL, NULL); // reset state - } else - { - iconv_close(conv); - conv = iconv_t(); - return Tdststring(); - } - } - iconv_close(conv); - conv = iconv_t(); - return reinterpret_cast(dst_string.data()); - #else - return EncodeImpl(to, DecodeImpl(from, src)); - #endif + return EncodeImpl(to, DecodeImpl(from, src)); } @@ -1457,7 +1209,7 @@ static Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) bool IsUTF8(const std::string &str) { - return (str == String::EncodeImpl(mpt::CharsetUTF8, String::DecodeImpl(mpt::CharsetUTF8, str))); + return (str == String::EncodeImpl(mpt::Charset::UTF8, String::DecodeImpl(mpt::Charset::UTF8, str))); } @@ -1469,7 +1221,7 @@ std::wstring ToWide(Charset from, const std::string &str) #if defined(MPT_ENABLE_CHARSET_LOCALE) std::wstring ToWide(const mpt::lstring &str) { - return String::DecodeImpl(CharsetLocale, str); + return String::DecodeImpl(Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #endif @@ -1487,7 +1239,7 @@ std::string ToCharset(Charset to, Charset from, const std::string &str) #if defined(MPT_ENABLE_CHARSET_LOCALE) std::string ToCharset(Charset to, const mpt::lstring &str) { - return String::ConvertImpl(to, CharsetLocale, str); + return String::ConvertImpl(to, Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE @@ -1495,12 +1247,12 @@ std::string ToCharset(Charset to, const mpt::lstring &str) #if MPT_WSTRING_CONVERT mpt::lstring ToLocale(const std::wstring &str) { - return String::EncodeImpl(CharsetLocale, str); + return String::EncodeImpl(Charset::Locale, str); } #endif mpt::lstring ToLocale(Charset from, const std::string &str) { - return String::ConvertImpl(CharsetLocale, from, str); + return String::ConvertImpl(Charset::Locale, from, str); } #endif // MPT_ENABLE_CHARSET_LOCALE @@ -1536,14 +1288,14 @@ mpt::winstring ToWin(const mpt::lstring &str) #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) CString ToCString(const std::wstring &str) { #ifdef UNICODE return str.c_str(); #else - return ToCharset(CharsetLocale, str).c_str(); + return ToCharset(Charset::Locale, str).c_str(); #endif } CString ToCString(Charset from, const std::string &str) @@ -1551,7 +1303,7 @@ CString ToCString(Charset from, const std::string &str) #ifdef UNICODE return ToWide(from, str).c_str(); #else - return ToCharset(CharsetLocale, from, str).c_str(); + return ToCharset(Charset::Locale, from, str).c_str(); #endif } std::wstring ToWide(const CString &str) @@ -1559,7 +1311,7 @@ std::wstring ToWide(const CString &str) #ifdef UNICODE return str.GetString(); #else - return ToWide(CharsetLocale, str.GetString()); + return ToWide(Charset::Locale, str.GetString()); #endif } std::string ToCharset(Charset to, const CString &str) @@ -1567,7 +1319,7 @@ std::string ToCharset(Charset to, const CString &str) #ifdef UNICODE return ToCharset(to, str.GetString()); #else - return ToCharset(to, CharsetLocale, str.GetString()); + return ToCharset(to, Charset::Locale, str.GetString()); #endif } #if defined(MPT_ENABLE_CHARSET_LOCALE) @@ -1582,7 +1334,7 @@ CString ToCString(const mpt::lstring &str) mpt::lstring ToLocale(const CString &str) { #ifdef UNICODE - return String::EncodeImpl(CharsetLocale, str.GetString()); + return String::EncodeImpl(Charset::Locale, str.GetString()); #else return str.GetString(); #endif @@ -1595,7 +1347,7 @@ mpt::winstring ToWin(const CString &str) } #endif // MPT_OS_WINDOWS -#endif // MFC +#endif // MPT_WITH_MFC #if MPT_USTRING_MODE_WIDE @@ -1604,29 +1356,29 @@ mpt::winstring ToWin(const CString &str) #if MPT_WSTRING_CONVERT mpt::ustring ToUnicode(const std::wstring &str) { - return String::EncodeImpl(mpt::CharsetUTF8, str); + return String::EncodeImpl(mpt::Charset::UTF8, str); } #endif mpt::ustring ToUnicode(Charset from, const std::string &str) { - return String::ConvertImpl(mpt::CharsetUTF8, from, str); + return String::ConvertImpl(mpt::Charset::UTF8, from, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::ustring ToUnicode(const mpt::lstring &str) { - return String::ConvertImpl(mpt::CharsetUTF8, mpt::CharsetLocale, str); + return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) mpt::ustring ToUnicode(const CString &str) { #ifdef UNICODE - return String::EncodeImpl(mpt::CharsetUTF8, str.GetString()); + return String::EncodeImpl(mpt::Charset::UTF8, str.GetString()); #else // !UNICODE - return String::ConvertImpl(mpt::CharsetUTF8, mpt::CharsetLocale, str.GetString()); + return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str.GetString()); #endif // UNICODE } -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_WIDE @@ -1635,39 +1387,39 @@ mpt::ustring ToUnicode(const CString &str) #if MPT_WSTRING_CONVERT std::wstring ToWide(const mpt::ustring &str) { - return String::DecodeImpl(mpt::CharsetUTF8, str); + return String::DecodeImpl(mpt::Charset::UTF8, str); } #endif std::string ToCharset(Charset to, const mpt::ustring &str) { - return String::ConvertImpl(to, mpt::CharsetUTF8, str); + return String::ConvertImpl(to, mpt::Charset::UTF8, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::lstring ToLocale(const mpt::ustring &str) { - return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str); + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS mpt::winstring ToWin(const mpt::ustring &str) { #ifdef UNICODE - return String::DecodeImpl(mpt::CharsetUTF8, str); + return String::DecodeImpl(mpt::Charset::UTF8, str); #else - return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str); + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); #endif } #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) CString ToCString(const mpt::ustring &str) { #ifdef UNICODE - return String::DecodeImpl(mpt::CharsetUTF8, str).c_str(); + return String::DecodeImpl(mpt::Charset::UTF8, str).c_str(); #else // !UNICODE - return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str).c_str(); + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str).c_str(); #endif // UNICODE } -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE @@ -1680,27 +1432,27 @@ static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, switch(codepage) { case 65001: - result = mpt::CharsetUTF8; + result = mpt::Charset::UTF8; if(isFallback) *isFallback = false; break; case 20127: - result = mpt::CharsetASCII; + result = mpt::Charset::ASCII; if(isFallback) *isFallback = false; break; case 28591: - result = mpt::CharsetISO8859_1; + result = mpt::Charset::ISO8859_1; if(isFallback) *isFallback = false; break; case 28605: - result = mpt::CharsetISO8859_15; + result = mpt::Charset::ISO8859_15; if(isFallback) *isFallback = false; break; case 437: - result = mpt::CharsetCP437; + result = mpt::Charset::CP437; if(isFallback) *isFallback = false; break; case 1252: - result = mpt::CharsetWindows1252; + result = mpt::Charset::Windows1252; if(isFallback) *isFallback = false; break; default: @@ -1711,46 +1463,18 @@ static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, 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)) + mpt::Charset charset = mpt::CharsetFromCodePage(codepage, fallback, &noCharsetMatch); + if(noCharsetMatch && mpt::String::TestCodePage(codepage)) { - result = mpt::FromCodePageDirect(codepage, str); + result = mpt::ToUnicode(mpt::String::DecodeCodepage(codepage, str)); } else { - result = mpt::ToUnicode(fileCharset, str); + result = mpt::ToUnicode(charset, str); } return result; #else // !MPT_OS_WINDOWS @@ -1811,7 +1535,7 @@ int CompareNoCaseAscii(const char *a, const char *b, std::size_t n) return 0; } -int CompareNoCaseAscii(const std::string &a, const std::string &b) +int CompareNoCaseAscii(std::string_view a, std::string_view b) { for(std::size_t i = 0; i < std::min(a.length(), b.length()); ++i) { @@ -1832,11 +1556,17 @@ int CompareNoCaseAscii(const std::string &a, const std::string &b) return a.length() < b.length() ? -1 : 1; } +int CompareNoCaseAscii(const std::string &a, const std::string &b) +{ + return CompareNoCaseAscii(std::string_view(a), std::string_view(b)); +} + + #if defined(MODPLUG_TRACKER) mpt::ustring ToLowerCase(const mpt::ustring &s) { - #if defined(_MFC_VER) + #if defined(MPT_WITH_MFC) #if defined(UNICODE) CString tmp = mpt::ToCString(s); tmp.MakeLower(); @@ -1846,16 +1576,16 @@ mpt::ustring ToLowerCase(const mpt::ustring &s) tmp.MakeLower(); return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE - #else // !_MFC_VER + #else // !MPT_WITH_MFC std::wstring ws = mpt::ToWide(s); std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); return mpt::ToUnicode(ws); - #endif // _MFC_VER + #endif // MPT_WITH_MFC } mpt::ustring ToUpperCase(const mpt::ustring &s) { - #if defined(_MFC_VER) + #if defined(MPT_WITH_MFC) #if defined(UNICODE) CString tmp = mpt::ToCString(s); tmp.MakeUpper(); @@ -1865,11 +1595,11 @@ mpt::ustring ToUpperCase(const mpt::ustring &s) tmp.MakeUpper(); return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE - #else // !_MFC_VER + #else // !MPT_WITH_MFC std::wstring ws = mpt::ToWide(s); std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); return mpt::ToUnicode(ws); - #endif // _MFC_VER + #endif // MPT_WITH_MFC } #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.h b/Frameworks/OpenMPT/OpenMPT/common/mptString.h index 1e09e722e..e4a0a73db 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptString.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.h @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -47,9 +48,9 @@ template struct string_traits { - typedef Tstring string_type; - typedef typename string_type::size_type size_type; - typedef typename string_type::value_type char_type; + using string_type = Tstring; + using size_type = typename string_type::size_type; + using char_type = typename string_type::value_type; static inline std::size_t length(const string_type &str) { return str.length(); } @@ -68,14 +69,14 @@ struct string_traits }; -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template <> struct string_traits { - typedef CString string_type; - typedef int size_type; - typedef typename CString::XCHAR char_type; + using string_type = CString; + using size_type = int; + using char_type = typename CString::XCHAR; static inline size_type length(const string_type &str) { return str.GetLength(); } @@ -94,7 +95,7 @@ struct string_traits } }; -#endif +#endif // MPT_WITH_MFC @@ -178,54 +179,64 @@ inline Tstring Replace(Tstring str, const Tstring2 &oldStr_, const Tstring3 &new } // namespace String -static inline std::size_t strnlen(const char *str, std::size_t n) +static inline std::string truncate(std::string str, std::size_t maxLen) { -#if MPT_COMPILER_MSVC - return ::strnlen(str, n); -#else - if(n >= std::numeric_limits::max()) + if(str.length() > maxLen) { - return std::strlen(str); + str.resize(maxLen); } - for(std::size_t i = 0; i < n; ++i) - { - if(str[i] == '\0') - { - return i; - } - } - return n; -#endif + return str; } -enum Charset { +enum class Charset { - CharsetUTF8, + UTF8, - CharsetASCII, // strictly 7-bit ASCII + ASCII, // strictly 7-bit ASCII - CharsetISO8859_1, - CharsetISO8859_15, + ISO8859_1, + ISO8859_15, - CharsetCP437, - CharsetCP437AMS, - CharsetCP437AMS2, + CP437, + CP437AMS, + CP437AMS2, - CharsetWindows1252, + Windows1252, #if defined(MPT_ENABLE_CHARSET_LOCALE) - CharsetLocale, // CP_ACP on windows, current C locale otherwise + Locale, // CP_ACP on windows, current C locale otherwise #endif // MPT_ENABLE_CHARSET_LOCALE }; + +// source code / preprocessor (i.e. # token) +inline constexpr Charset CharsetSource = Charset::ASCII; + +// debug log files +inline constexpr Charset CharsetLogfile = Charset::UTF8; + +// std::clog / std::cout / std::cerr +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS && defined(MPT_ENABLE_CHARSET_LOCALE) +inline constexpr Charset CharsetStdIO = Charset::Locale; +#else +inline constexpr Charset CharsetStdIO = Charset::UTF8; +#endif + +// std::exception::what() +#if defined(MPT_ENABLE_CHARSET_LOCALE) +inline constexpr Charset CharsetException = Charset::Locale; +#else +inline constexpr Charset CharsetException = Charset::UTF8; +#endif + // Locale in tracker builds, UTF8 in non-locale-aware libopenmpt builds. #if defined(MPT_ENABLE_CHARSET_LOCALE) -const Charset CharsetLocaleOrUTF8 = CharsetLocale; +inline constexpr Charset CharsetLocaleOrUTF8 = Charset::Locale; #else -const Charset CharsetLocaleOrUTF8 = CharsetUTF8; +inline constexpr Charset CharsetLocaleOrUTF8 = Charset::UTF8; #endif @@ -267,30 +278,41 @@ struct charset_char_traits : std::char_traits { #if defined(MPT_ENABLE_CHARSET_LOCALE) -typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetLocale) lstring; +using lstring = MPT_ENCODED_STRING_TYPE(mpt::Charset::Locale); #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; }; +template <> struct windows_char_traits { using string_type = mpt::lstring; }; +template <> struct windows_char_traits { using string_type = std::wstring; }; #ifdef UNICODE -typedef windows_char_traits::string_type tstring; +using tstring = windows_char_traits::string_type; #else -typedef windows_char_traits::string_type tstring; +using tstring = windows_char_traits::string_type; #endif -typedef mpt::tstring winstring; +using winstring = mpt::tstring; #endif // MPT_OS_WINDOWS #if MPT_ENABLE_U8STRING -typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetUTF8) u8string; +#if MPT_CXX_AT_LEAST(20) + +using u8string = std::u8string; + +#define MPT_U8CHAR_TYPE char8_t +#define MPT_U8CHAR(x) u8 ## x +#define MPT_U8LITERAL(x) u8 ## x +#define MPT_U8STRING(x) std::u8string( u8 ## x ) + +#else // !C++20 + +using u8string = MPT_ENCODED_STRING_TYPE(mpt::Charset::UTF8); #define MPT_U8CHAR_TYPE char #define MPT_U8CHAR(x) x @@ -311,6 +333,8 @@ typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetUTF8) u8string; // mpt::u8string is meant as an alternative implementaion to std::wstring // for implementing the unicode string type mpt::ustring. +#endif // C++20 + #endif // MPT_ENABLE_U8STRING @@ -367,7 +391,7 @@ mpt::winstring ToWin(const mpt::lstring &str); #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) #if !(MPT_WSTRING_CONVERT) #error "MFC depends on MPT_WSTRING_CONVERT)" #endif @@ -394,7 +418,7 @@ mpt::winstring ToWin(const CString &str); std::wstring ToWide(const CString &str); std::string ToCharset(Charset to, const CString &str); -#endif // MFC +#endif // MPT_WITH_MFC // mpt::ustring @@ -423,8 +447,8 @@ std::string ToCharset(Charset to, const CString &str); #error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." #endif -typedef std::wstring ustring; -#define MPT_UCHAR_TYPE wchar_t +using ustring = std::wstring; +using uchar = wchar_t; #define MPT_UCHAR(x) L ## x #define MPT_ULITERAL(x) L ## x #define MPT_USTRING(x) std::wstring( L ## x ) @@ -436,11 +460,11 @@ typedef std::wstring ustring; #error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." #endif -typedef mpt::u8string ustring; -#define MPT_UCHAR_TYPE char -#define MPT_UCHAR(x) x -#define MPT_ULITERAL(x) x -#define MPT_USTRING(x) mpt::ustring( x ) +using ustring = mpt::u8string; +using uchar = MPT_U8CHAR_TYPE; +#define MPT_UCHAR(x) MPT_U8CHAR( x ) +#define MPT_ULITERAL(x) MPT_U8LITERAL( x ) +#define MPT_USTRING(x) MPT_U8STRING( x ) #endif // MPT_USTRING_MODE_UTF8 @@ -459,7 +483,7 @@ static inline mpt::ustring ToUnicode(Charset from, const char * str) { return To #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) +#if defined(MPT_WITH_MFC) static inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); } #endif // MFC #else // !MPT_USTRING_MODE_WIDE @@ -473,9 +497,9 @@ static inline mpt::ustring ToUnicode(Charset from, const char * str) { return To #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::ustring ToUnicode(const mpt::lstring &str); #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) mpt::ustring ToUnicode(const CString &str); -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_WIDE @@ -494,9 +518,9 @@ mpt::lstring ToLocale(const mpt::ustring &str); #if MPT_OS_WINDOWS mpt::winstring ToWin(const mpt::ustring &str); #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) CString ToCString(const mpt::ustring &str); -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE // The MPT_UTF8 allows specifying UTF8 char arrays. @@ -504,7 +528,7 @@ CString ToCString(const mpt::ustring &str); // i.e. it is NOT generally available at compile time. // Use explicit UTF8 encoding, // i.e. U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) would be written as "\xC3\xBC". -#define MPT_UTF8(x) mpt::ToUnicode(mpt::CharsetUTF8, x ) +#define MPT_UTF8(x) mpt::ToUnicode(mpt::Charset::UTF8, x ) @@ -522,8 +546,10 @@ std::string ToLowerCaseAscii(std::string s); std::string ToUpperCaseAscii(std::string s); int CompareNoCaseAscii(const char *a, const char *b, std::size_t n); +int CompareNoCaseAscii(std::string_view a, std::string_view b); int CompareNoCaseAscii(const std::string &a, const std::string &b); + #if defined(MODPLUG_TRACKER) mpt::ustring ToLowerCase(const mpt::ustring &s); @@ -548,7 +574,7 @@ mpt::ustring ToUpperCase(const mpt::ustring &s); // Warning: These types will silently do charset conversions. Only use them when this can be tolerated. // BasicAnyString is convertable to mpt::ustring and constructable from any string at all. -template +template class BasicAnyString : public mpt::ustring { @@ -556,17 +582,17 @@ private: static mpt::ustring From8bit(const std::string &str) { - MPT_CONSTANT_IF(charset == mpt::CharsetUTF8) + if constexpr(charset == mpt::Charset::UTF8) { - return mpt::ToUnicode(mpt::CharsetUTF8, str); + return mpt::ToUnicode(mpt::Charset::UTF8, str); } else { // auto utf8 detection - MPT_CONSTANT_IF(tryUTF8) + if constexpr(tryUTF8) { if(mpt::IsUTF8(str)) { - return mpt::ToUnicode(mpt::CharsetUTF8, str); + return mpt::ToUnicode(mpt::Charset::UTF8, str); } else { return mpt::ToUnicode(charset, str); @@ -600,9 +626,9 @@ public: #endif // mfc -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#endif +#endif // MPT_WITH_MFC // fallback for custom string types template BasicAnyString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } @@ -632,9 +658,9 @@ public: #endif // mfc -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#endif +#endif // MPT_WITH_MFC // fallback for custom string types template AnyUnicodeString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } @@ -645,32 +671,32 @@ public: // AnyString // Try to do the smartest auto-magic we can do. #if defined(MPT_ENABLE_CHARSET_LOCALE) -typedef BasicAnyString AnyString; +using AnyString = BasicAnyString; #elif MPT_OS_WINDOWS -typedef BasicAnyString AnyString; +using AnyString = BasicAnyString; #else -typedef BasicAnyString AnyString; +using AnyString = BasicAnyString; #endif // AnyStringLocale // char-based strings are assumed to be in locale encoding. #if defined(MPT_ENABLE_CHARSET_LOCALE) -typedef BasicAnyString AnyStringLocale; +using AnyStringLocale = BasicAnyString; #else -typedef BasicAnyString AnyStringLocale; +using AnyStringLocale = BasicAnyString; #endif // AnyStringUTF8orLocale // char-based strings are tried in UTF8 first, if this fails, locale is used. #if defined(MPT_ENABLE_CHARSET_LOCALE) -typedef BasicAnyString AnyStringUTF8orLocale; +using AnyStringUTF8orLocale = BasicAnyString; #else -typedef BasicAnyString AnyStringUTF8orLocale; +using AnyStringUTF8orLocale = BasicAnyString; #endif // AnyStringUTF8 // char-based strings are assumed to be in UTF8. -typedef BasicAnyString AnyStringUTF8; +using AnyStringUTF8 = BasicAnyString; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h index f0b7e59f8..4c8681ac9 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h @@ -13,6 +13,7 @@ #include "BuildSettings.h" +#include "mptMemory.h" #include "mptString.h" #include @@ -33,19 +34,19 @@ namespace String { - enum ReadWriteMode + enum ReadWriteMode : uint8 { // Reading / Writing: Standard null-terminated string handling. - nullTerminated, + nullTerminated = 1, // Reading: Source string is not guaranteed to be null-terminated (if it fills the whole char array). // Writing: Destination string is not guaranteed to be null-terminated (if it fills the whole char array). - maybeNullTerminated, + maybeNullTerminated = 2, // Reading: String may contain null characters anywhere. They should be treated as spaces. // Writing: A space-padded string is written. - spacePadded, + spacePadded = 3, // Reading: String may contain null characters anywhere. The last character is ignored (it is supposed to be 0). // Writing: A space-padded string with a trailing null is written. - spacePaddedNull + spacePaddedNull = 4, }; namespace detail @@ -75,7 +76,7 @@ public: : buf(buf) , size(size) { - MPT_STATIC_ASSERT(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); MPT_ASSERT(size > 0); } StringBufRefImpl(const StringBufRefImpl &) = delete; @@ -87,6 +88,10 @@ public: std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return Tstring(buf, buf + len); } + bool empty() const + { + return buf[0] == Tchar('\0'); + } StringBufRefImpl & operator = (const Tstring & str) { std::fill(buf, buf + size, Tchar('\0')); @@ -109,7 +114,7 @@ public: : buf(buf) , size(size) { - MPT_STATIC_ASSERT(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); MPT_ASSERT(size > 0); } StringBufRefImpl(const StringBufRefImpl &) = delete; @@ -121,6 +126,10 @@ public: std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return Tstring(buf, buf + len); } + bool empty() const + { + return buf[0] == Tchar('\0'); + } }; namespace String { @@ -169,6 +178,49 @@ inline StringBufRefImpl(0)> struct charbuf; + +template +struct charbuf(0)> +{ +public: + typedef char Tchar; + using char_type = Tchar; + using string_type = std::basic_string; + constexpr std::size_t static_length() const { return len; } +public: + Tchar buf[len]; +public: + charbuf() + { + Clear(buf); + } + charbuf(const charbuf &) = default; + charbuf(charbuf &&) = default; + charbuf & operator = (const charbuf &) = default; + charbuf & operator = (charbuf &&) = default; + const Tchar & operator[](std::size_t i) const { return buf[i]; } + std::string str() const { return static_cast(*this); } + operator string_type () const + { + return mpt::String::ReadAutoBuf(buf); + } + bool empty() const + { + return mpt::String::ReadAutoBuf(buf).empty(); + } + charbuf & operator = (const string_type & str) + { + mpt::String::WriteAutoBuf(buf) = str; + return *this; + } +public: + friend bool operator!=(const std::string & a, const charbuf & b) { return a != b.str(); } + friend bool operator!=(const charbuf & a, const std::string & b) { return a.str() != b; } + friend bool operator==(const std::string & a, const charbuf & b) { return a == b.str(); } + friend bool operator==(const charbuf & a, const std::string & b) { return a.str() == b; } +}; + template class StringModeBufRefImpl { @@ -184,7 +236,7 @@ public: , size(size) , mode(mode) { - MPT_STATIC_ASSERT(sizeof(Tchar) == 1); + static_assert(sizeof(Tchar) == 1); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; @@ -194,6 +246,10 @@ public: { return String::detail::ReadStringBuffer(mode, buf, size); } + bool empty() const + { + return String::detail::ReadStringBuffer(mode, buf, size).empty(); + } StringModeBufRefImpl & operator = (const std::string & str) { String::detail::WriteStringBuffer(mode, buf, size, str.data(), str.size()); @@ -216,7 +272,7 @@ public: , size(size) , mode(mode) { - MPT_STATIC_ASSERT(sizeof(Tchar) == 1); + static_assert(sizeof(Tchar) == 1); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; @@ -226,6 +282,10 @@ public: { return String::detail::ReadStringBuffer(mode, buf, size); } + bool empty() const + { + return String::detail::ReadStringBuffer(mode, buf, size).empty(); + } }; namespace String { @@ -251,6 +311,46 @@ inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar * } } // namespace String +template +struct charbuf +{ +public: + typedef char Tchar; + using char_type = Tchar; + using string_type = std::basic_string; +public: + Tchar buf[len]; +public: + charbuf() = default; + charbuf(const charbuf &) = default; + charbuf(charbuf &&) = default; + charbuf & operator = (const charbuf &) = default; + charbuf & operator = (charbuf &&) = default; + operator string_type () const + { + return mpt::String::ReadBuf(mode, buf); + } + bool empty() const + { + return mpt::String::ReadBuf(mode, buf).empty(); + } + charbuf & operator = (const string_type & str) + { + mpt::String::WriteBuf(mode, buf) = str; + return *this; + } +}; + + +// see MPT_BINARY_STRUCT +template +struct is_binary_safe> : public std::true_type { }; +template +struct is_binary_safe(0)>> : public std::false_type { }; +static_assert(sizeof(mpt::charbuf<7>) == 7); +static_assert(alignof(mpt::charbuf<7>) == 1); +static_assert(std::is_standard_layout>::value); + #ifdef MODPLUG_TRACKER @@ -279,7 +379,7 @@ inline StringBufRefImpl class CStringBufRefImpl @@ -363,7 +463,7 @@ inline CStringBufRefImpl WriteCStringBuf(Tchar * buf, std::size_t size) } } // namespace String -#endif // _MFC_VER +#endif // MPT_WITH_MFC #endif // MPT_OS_WINDOWS @@ -388,7 +488,7 @@ namespace String template void SetNullTerminator(char (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); buffer[size - 1] = 0; } @@ -403,7 +503,7 @@ namespace String template void SetNullTerminator(wchar_t (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); buffer[size - 1] = 0; } @@ -420,7 +520,7 @@ namespace String template void FixNullString(char (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); SetNullTerminator(buffer); size_t pos = 0; // Find the first null char. @@ -449,244 +549,6 @@ namespace String } - // 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()); - } - - -#if MPT_GCC_AT_LEAST(8,1,0) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-truncation" -#endif - - // 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_GCC_AT_LEAST(8,1,0) -#pragma GCC diagnostic pop -#endif - #if MPT_COMPILER_MSVC #pragma warning(pop) diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp index a8f92ae6d..76c6868ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp @@ -10,7 +10,7 @@ #include "stdafx.h" #include "mptStringFormat.h" -#if MPT_CXX_AT_LEAST(17) && MPT_COMPILER_MSVC +#if MPT_COMPILER_MSVC #define MPT_FORMAT_CXX17_INT 1 #else #define MPT_FORMAT_CXX17_INT 0 @@ -59,7 +59,7 @@ template inline void SaneInsert(Tstream & s, const unsigned ch #if MPT_FORMAT_CXX17_INT #if MPT_WSTRING_FORMAT -std::wstring ToWideSimple(const std::string &nstr) +static std::wstring ToWideSimple(const std::string &nstr) { std::wstring wstr(nstr.size(), L'\0'); for(std::size_t i = 0; i < nstr.size(); ++i) @@ -148,17 +148,6 @@ static inline std::wstring ToWStringHelperFloat(const T & x) } #endif -#if MPT_WSTRING_CONVERT -std::string ToString(const std::wstring & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } -std::string ToString(const wchar_t * const & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } -std::string ToString(const wchar_t & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, std::wstring(1, x)); } -#endif -#if MPT_USTRING_MODE_UTF8 -std::string ToString(const mpt::ustring & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } -#endif -#if defined(_MFC_VER) -std::string ToString(const CString & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); } -#endif 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); } @@ -174,19 +163,15 @@ 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); } -mpt::ustring ToUString(const char & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, std::string(1, x)); } #if MPT_WSTRING_FORMAT #if MPT_USTRING_MODE_UTF8 mpt::ustring ToUString(const std::wstring & x) { return mpt::ToUnicode(x); } #endif mpt::ustring ToUString(const wchar_t * const & x) { return mpt::ToUnicode(x); } -mpt::ustring ToUString(const wchar_t & x) { return mpt::ToUnicode(std::wstring(1, x)); } #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); } -#endif +#endif // MPT_WITH_MFC #if MPT_USTRING_MODE_WIDE mpt::ustring ToUString(const bool & x) { return ToWStringHelperInt(static_cast(x)); } mpt::ustring ToUString(const signed char & x) { return ToWStringHelperInt(x); } @@ -204,32 +189,29 @@ 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, 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)); } +mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(static_cast(x))); } +mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } #endif #if MPT_WSTRING_FORMAT -std::wstring ToWString(const std::string & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, x); } -std::wstring ToWString(const char * const & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, x); } -std::wstring ToWString(const char & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, std::string(1, x)); } #if MPT_USTRING_MODE_UTF8 std::wstring ToWString(const mpt::ustring & x) { return mpt::ToWide(x); } #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) std::wstring ToWString(const CString & x) { return mpt::ToWide(x); } -#endif +#endif // MPT_WITH_MFC 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); } @@ -468,10 +450,6 @@ static inline std::wstring FormatValWHelperFloat(const T & x, const FormatSpec & #endif -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); } @@ -488,8 +466,6 @@ std::string FormatVal(const double & x, const FormatSpec & f) { return FormatVal std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelperFloat(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); } @@ -506,31 +482,23 @@ mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return FormatV 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)); } +mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(static_cast(x), f)); } +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } #endif #if MPT_WSTRING_FORMAT -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); } @@ -548,145 +516,6 @@ std::wstring FormatValW(const long double & x, const FormatSpec & f) { return Fo #endif -namespace String -{ - - -namespace detail -{ - -template -Tstring PrintImplTemplate(const Tstring & format - , const Tstring & x1 - , const Tstring & x2 - , const Tstring & x3 - , const Tstring & x4 - , const Tstring & x5 - , const Tstring & x6 - , const Tstring & x7 - , const Tstring & x8 - ) -{ - typedef typename mpt::string_traits traits; - Tstring result; - const typename traits::size_type len = traits::length(format); - traits::reserve(result, len); - for(typename traits::size_type pos = 0; pos != len; ++pos) - { - typename traits::char_type c = format[pos]; - if(pos + 1 != len && c == typename traits::char_type('%')) - { - pos++; - c = format[pos]; - if(typename traits::char_type('1') <= c && c <= typename traits::char_type('9')) - { - const std::size_t n = c - typename traits::char_type('0'); - switch(n) - { - 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 != typename traits::char_type('%')) - { - traits::append(result, 1, typename traits::char_type('%')); - } - } - traits::append(result, 1, c); - } - return result; -} - -std::string PrintImpl(const std::string & format - , const std::string & x1 - , const std::string & x2 - , const std::string & x3 - , const std::string & x4 - , const std::string & x5 - , const std::string & x6 - , const std::string & x7 - , const std::string & x8 - ) -{ - return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); -} - -#if MPT_WSTRING_FORMAT -std::wstring PrintImpl(const std::wstring & format - , const std::wstring & x1 - , const std::wstring & x2 - , const std::wstring & x3 - , const std::wstring & x4 - , const std::wstring & x5 - , const std::wstring & x6 - , const std::wstring & x7 - , const std::wstring & x8 - ) -{ - return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); -} -#endif - -#if MPT_USTRING_MODE_UTF8 -mpt::ustring PrintImpl(const mpt::ustring & format - , const mpt::ustring & x1 - , const mpt::ustring & x2 - , const mpt::ustring & x3 - , const mpt::ustring & x4 - , const mpt::ustring & x5 - , const mpt::ustring & x6 - , const mpt::ustring & x7 - , const mpt::ustring & x8 - ) -{ - return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); -} -#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 - , const CString & x2 - , const CString & x3 - , const CString & x4 - , const CString & x5 - , const CString & x6 - , const CString & x7 - , const CString & x8 - ) -{ - return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); -} -#endif - -} // namespace detail - - -} // namespace String - - } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h index 2ddad8e71..2eb6a7c37 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h @@ -71,25 +71,25 @@ namespace mpt // 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()); } +template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, 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; } -MPT_DEPRECATED static inline std::string ToString(const char & x) { return std::string(1, x); } // deprecated to catch potential API mis-use, use std::string(1, x) instead +std::string ToString(const char &x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if MPT_WSTRING_FORMAT -MPT_DEPRECATED std::string ToString(const std::wstring & x); // Unknown encoding. -MPT_DEPRECATED std::string ToString(const wchar_t * const & x); // Unknown encoding. -MPT_DEPRECATED std::string ToString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::string ToString(const std::wstring & x) = delete; // Unknown encoding. +std::string ToString(const wchar_t * const & x) = delete; // Unknown encoding. +std::string ToString(const wchar_t &x ) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif #if MPT_USTRING_MODE_UTF8 -MPT_DEPRECATED std::string ToString(const mpt::ustring & x); // Unknown encoding. -#endif -#if defined(_MFC_VER) -MPT_DEPRECATED std::string ToString(const CString & x); +std::string ToString(const mpt::ustring & x) = delete; // Unknown encoding. #endif +#if defined(MPT_WITH_MFC) +std::string ToString(const CString & x) = delete; // unknown encoding +#endif // MPT_WITH_MFC std::string ToString(const bool & x); std::string ToString(const signed char & x); std::string ToString(const unsigned char & x); @@ -109,19 +109,19 @@ std::string ToString(const long double & x); template auto ToUString(const T & x) -> decltype(x.ToUString()) { return x.ToUString(); } static inline mpt::ustring ToUString(const mpt::ustring & x) { return x; } -MPT_DEPRECATED mpt::ustring ToUString(const std::string & x); // Unknown encoding. -MPT_DEPRECATED mpt::ustring ToUString(const char * const & x); // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. -MPT_DEPRECATED mpt::ustring ToUString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead +mpt::ustring ToUString(const std::string & x) = delete; // Unknown encoding. +mpt::ustring ToUString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. +mpt::ustring ToUString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if MPT_WSTRING_FORMAT #if MPT_USTRING_MODE_UTF8 mpt::ustring ToUString(const std::wstring & x); #endif mpt::ustring ToUString(const wchar_t * const & x); -MPT_DEPRECATED mpt::ustring ToUString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +mpt::ustring ToUString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) mpt::ustring ToUString(const CString & x); -#endif +#endif // MPT_WITH_MFC mpt::ustring ToUString(const bool & x); mpt::ustring ToUString(const signed char & x); mpt::ustring ToUString(const unsigned char & x); @@ -138,18 +138,18 @@ mpt::ustring ToUString(const double & x); mpt::ustring ToUString(const long double & x); #if MPT_WSTRING_FORMAT -MPT_DEPRECATED std::wstring ToWString(const std::string & x); // Unknown encoding. -MPT_DEPRECATED std::wstring ToWString(const char * const & x); // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. -MPT_DEPRECATED std::wstring ToWString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead +std::wstring ToWString(const std::string & x) = delete; // Unknown encoding. +std::wstring ToWString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. +std::wstring ToWString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead static inline std::wstring ToWString(const std::wstring & x) { return x; } static inline std::wstring ToWString(const wchar_t * const & x) { return x; } -MPT_DEPRECATED static inline std::wstring ToWString(const wchar_t & x) { return std::wstring(1, x); } // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::wstring ToWString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #if MPT_USTRING_MODE_UTF8 std::wstring ToWString(const mpt::ustring & x); #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) std::wstring ToWString(const CString & x); -#endif +#endif // MPT_WITH_MFC std::wstring ToWString(const bool & x); std::wstring ToWString(const signed char & x); std::wstring ToWString(const unsigned char & x); @@ -173,10 +173,10 @@ template struct ToLocaleHelper { mpt::lstring operator () (const T template <> struct ToLocaleHelper { mpt::lstring operator () (const mpt::lstring & v) { return v; } }; #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template struct ToCStringHelper { CString operator () (const T & v) { return mpt::ToCString(ToUString(v)); } }; template <> struct ToCStringHelper { CString operator () (const CString & v) { return v; } }; -#endif +#endif // MPT_WITH_MFC template struct ToStringTFunctor {}; template <> struct ToStringTFunctor { template inline std::string operator() (const T & x) { return ToString(x); } }; @@ -187,9 +187,9 @@ template <> struct ToStringTFunctor { template inline #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) +#if defined(MPT_WITH_MFC) template <> struct ToStringTFunctor { template inline CString operator() (const T & x) { return mpt::ToCStringHelper()(x); } }; -#endif +#endif // MPT_WITH_MFC template inline Tstring ToStringT(const T & x) { return ToStringTFunctor()(x); } @@ -215,13 +215,13 @@ enum FormatFlagsEnum typedef unsigned int FormatFlags; -STATIC_ASSERT(sizeof(FormatFlags) >= sizeof(fmt_base::FormatFlagsEnum)); +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 +std::string FormatVal(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -MPT_DEPRECATED std::string FormatVal(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +std::string FormatVal(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::string FormatVal(const bool & x, const FormatSpec & f); std::string FormatVal(const signed char & x, const FormatSpec & f); @@ -238,9 +238,9 @@ 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 +mpt::ustring FormatValU(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -MPT_DEPRECATED mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR mpt::ustring FormatValU(const bool & x, const FormatSpec & f); mpt::ustring FormatValU(const signed char & x, const FormatSpec & f); @@ -258,9 +258,9 @@ 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 +std::wstring FormatValW(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -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 +std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::wstring FormatValW(const bool & x, const FormatSpec & f); std::wstring FormatValW(const signed char & x, const FormatSpec & f); @@ -285,15 +285,15 @@ template <> struct FormatValTFunctor { template inlin template <> struct FormatValTFunctor { template inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } }; #endif #if defined(MPT_ENABLE_CHARSET_LOCALE) -template <> struct FormatValTFunctor { template inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::CharsetLocale, FormatVal(x, f)); } }; +template <> struct FormatValTFunctor { template inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::Charset::Locale, FormatVal(x, f)); } }; #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC ) #ifdef UNICODE template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(FormatValW(x, f)); } }; -#else -template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::CharsetLocale, FormatVal(x, f)); } }; -#endif -#endif +#else // !UNICODE +template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::Charset::Locale, FormatVal(x, f)); } }; +#endif // UNICODE +#endif // MPT_WITH_MFC class FormatSpec @@ -353,6 +353,28 @@ public: MPT_CONSTEXPR14_FUN FormatSpec & Precision(int p) noexcept { return Prec(p); } }; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tsrc & src) const { return src; } +}; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tptr * const & src) const { return reinterpret_cast(src); } +}; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tptr * const & src) const { return reinterpret_cast(src); } +}; + + +template +Tdst pointer_cast(const Tsrc & src) +{ + return pointer_cast_helper()(src); +} template struct fmtT : fmt_base @@ -373,98 +395,111 @@ static inline Tstring fmt(const T& x, const FormatSpec& f) template static inline Tstring dec(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff()); } template static inline Tstring dec0(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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); + 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); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring hex(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff()); } template static inline Tstring HEX(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff()); } template static inline Tstring hex0(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width)); } template static inline Tstring HEX0(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width)); } template static inline Tstring hex(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s)); } template static inline Tstring HEX(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s)); } template static inline Tstring hex0(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring flt(const T& x, int precision = -1) { - STATIC_ASSERT(std::is_floating_point::value); + static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillOff().Precision(precision)); } template static inline Tstring fix(const T& x, int precision = -1) { - STATIC_ASSERT(std::is_floating_point::value); + 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); + static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaSci().FillOff().Precision(precision)); } +template +static inline Tstring ptr(const T& x) +{ + static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); + return hex0(pointer_cast(x)); +} +template +static inline Tstring PTR(const T& x) +{ + static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); + return HEX0(pointer_cast(x)); +} + static inline Tstring pad_left(std::size_t width_, const Tstring &str) { typedef mpt::string_traits traits; @@ -514,9 +549,9 @@ typedef fmtT lfmt; #if MPT_OS_WINDOWS typedef fmtT tfmt; #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) typedef fmtT cfmt; -#endif +#endif // MPT_WITH_MFC } // namespace mpt @@ -546,305 +581,117 @@ template <> struct to_string_type { typedef mpt::ustring type; #if defined(MPT_ENABLE_CHARSET_LOCALE) template <> struct to_string_type { typedef mpt::lstring type; }; #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template <> struct to_string_type { typedef CString type; }; -#endif +#endif // MPT_WITH_MFC template struct to_string_type { typedef typename to_string_type::type type; }; -std::string PrintImpl(const std::string & format - , const std::string & x1 = std::string() - , const std::string & x2 = std::string() - , const std::string & x3 = std::string() - , const std::string & x4 = std::string() - , const std::string & x5 = std::string() - , const std::string & x6 = std::string() - , const std::string & x7 = std::string() - , const std::string & x8 = std::string() - ); - -#if MPT_WSTRING_FORMAT -std::wstring PrintImpl(const std::wstring & format - , const std::wstring & x1 = std::wstring() - , const std::wstring & x2 = std::wstring() - , const std::wstring & x3 = std::wstring() - , const std::wstring & x4 = std::wstring() - , const std::wstring & x5 = std::wstring() - , const std::wstring & x6 = std::wstring() - , const std::wstring & x7 = std::wstring() - , const std::wstring & x8 = std::wstring() - ); -#endif - -#if MPT_USTRING_MODE_UTF8 -mpt::ustring PrintImpl(const mpt::ustring & format - , const mpt::ustring & x1 = mpt::ustring() - , const mpt::ustring & x2 = mpt::ustring() - , const mpt::ustring & x3 = mpt::ustring() - , const mpt::ustring & x4 = mpt::ustring() - , const mpt::ustring & x5 = mpt::ustring() - , const mpt::ustring & x6 = mpt::ustring() - , const mpt::ustring & x7 = mpt::ustring() - , const mpt::ustring & x8 = mpt::ustring() - ); -#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() - , const CString & x2 = CString() - , const CString & x3 = CString() - , const CString & x4 = CString() - , const CString & x5 = CString() - , const CString & x6 = CString() - , const CString & x7 = CString() - , const CString & x8 = CString() - ); -#endif - } // namespace detail } // namespace String template -struct message_formatter +class message_formatter { -typedef typename mpt::String::detail::to_string_type::type Tstring; -Tstring format; -message_formatter(const Tstring & format) : format(format) {} -Tstring operator() ( - ) const -{ - return mpt::String::detail::PrintImpl(format - ); -} +public: -template< - typename T1 -> -Tstring operator() ( - const T1& x1 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - ); -} + typedef typename mpt::String::detail::to_string_type::type Tstring; -template< - typename T1 - , typename T2 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - ); -} +private: -template< - typename T1 - , typename T2 - , typename T3 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - , const T3& x3 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - , ToStringTFunctor()(x3) - ); -} + Tstring format; -template< - typename T1 - , typename T2 - , typename T3 - , typename T4 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - , const T3& x3 - , const T4& x4 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - , ToStringTFunctor()(x3) - , ToStringTFunctor()(x4) - ); -} +private: -template< - typename T1 - , typename T2 - , typename T3 - , typename T4 - , typename T5 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - , const T3& x3 - , const T4& x4 - , const T5& x5 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - , ToStringTFunctor()(x3) - , ToStringTFunctor()(x4) - , ToStringTFunctor()(x5) - ); -} + MPT_NOINLINE Tstring do_format(mpt::span vals) const + { + typedef typename mpt::string_traits traits; + Tstring result; + const typename traits::size_type len = traits::length(format); + traits::reserve(result, len); + for(typename traits::size_type pos = 0; pos != len; ++pos) + { + typename traits::char_type c = format[pos]; + if(pos + 1 != len && c == typename traits::char_type('%')) + { + pos++; + c = format[pos]; + if(typename traits::char_type('1') <= c && c <= typename traits::char_type('9')) + { + const std::size_t n = c - typename traits::char_type('0') - 1; + if(n < std::size(vals)) + { + traits::append(result, vals[n]); + } + continue; + } else if(c != typename traits::char_type('%')) + { + traits::append(result, 1, typename traits::char_type('%')); + } + } + traits::append(result, 1, c); + } + return result; + } -template< - typename T1 - , typename T2 - , typename T3 - , typename T4 - , typename T5 - , typename T6 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - , const T3& x3 - , const T4& x4 - , const T5& x5 - , const T6& x6 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - , ToStringTFunctor()(x3) - , ToStringTFunctor()(x4) - , ToStringTFunctor()(x5) - , ToStringTFunctor()(x6) - ); -} +public: -template< - typename T1 - , typename T2 - , typename T3 - , typename T4 - , typename T5 - , typename T6 - , typename T7 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - , const T3& x3 - , const T4& x4 - , const T5& x5 - , const T6& x6 - , const T7& x7 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - , ToStringTFunctor()(x3) - , ToStringTFunctor()(x4) - , ToStringTFunctor()(x5) - , ToStringTFunctor()(x6) - , ToStringTFunctor()(x7) - ); -} + message_formatter(Tstring format_) + : format(std::move(format_)) + { + } -template< - typename T1 - , typename T2 - , typename T3 - , typename T4 - , typename T5 - , typename T6 - , typename T7 - , typename T8 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - , const T3& x3 - , const T4& x4 - , const T5& x5 - , const T6& x6 - , const T7& x7 - , const T8& x8 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - , ToStringTFunctor()(x3) - , ToStringTFunctor()(x4) - , ToStringTFunctor()(x5) - , ToStringTFunctor()(x6) - , ToStringTFunctor()(x7) - , ToStringTFunctor()(x8) - ); -} +public: + + template + Tstring operator() (const Ts&... xs) const + { + const std::array vals{{ToStringTFunctor()(xs)...}}; + return do_format(mpt::as_span(vals)); + } }; // struct message_formatter template -message_formatter::type> format(const Tformat &format) +message_formatter::type> format(Tformat format) { typedef typename mpt::String::detail::to_string_type::type Tstring; - return message_formatter(Tstring(format)); + return message_formatter(Tstring(std::move(format))); } #if MPT_WSTRING_FORMAT -static inline message_formatter wformat(const std::wstring &format) +static inline message_formatter wformat(std::wstring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #endif -static inline message_formatter uformat(const mpt::ustring &format) +static inline message_formatter uformat(mpt::ustring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #if defined(MPT_ENABLE_CHARSET_LOCALE) -static inline message_formatter lformat(const mpt::lstring &format) +static inline message_formatter lformat(mpt::lstring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS -static inline message_formatter tformat(const mpt::tstring &format) +static inline message_formatter tformat(mpt::tstring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #endif -#if defined(_MFC_VER) -static inline message_formatter cformat(const CString &format) +#if defined(MPT_WITH_MFC) +static inline message_formatter cformat(CString format) { - return message_formatter(format); + return message_formatter(std::move(format)); } -#endif +#endif // MPT_WITH_MFC } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h index 269a44f2f..488df8939 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h @@ -80,19 +80,19 @@ template<> inline double ConvertStrTo(const std::wstring &str) { return ConvertS template<> inline long double ConvertStrTo(const std::wstring &str) { return ConvertStrToLongDouble(str); } #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template inline T ConvertStrTo(const CString &str) { #if defined(UNICODE) && MPT_WSTRING_FORMAT return ConvertStrTo(mpt::ToWide(str)); #elif defined(UNICODE) - return ConvertStrTo(mpt::ToCharset(mpt::CharsetUTF8, str)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); #else // !UNICODE - return ConvertStrTo(mpt::ToCharset(mpt::CharsetLocale, str)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); #endif // UNICODE } -#endif // _MFC_VER +#endif // MPT_WITH_MFC template inline T ConvertStrTo(const char *str) @@ -123,7 +123,7 @@ inline T ConvertStrTo(const wchar_t *str) template inline T ConvertStrTo(const mpt::ustring &str) { - return ConvertStrTo(mpt::ToCharset(mpt::CharsetUTF8, str)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); } template<> inline mpt::ustring ConvertStrTo(const mpt::ustring &str) { return str; } #if MPT_WSTRING_CONVERT @@ -135,7 +135,7 @@ template<> inline std::wstring ConvertStrTo(const mpt::ustring &str) { return mp template inline T ConvertStrTo(const mpt::lstring &str) { - return ConvertStrTo(mpt::ToCharset(mpt::CharsetLocale, str)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); } template<> inline mpt::lstring ConvertStrTo(const mpt::lstring &str) { return str; } #endif @@ -176,7 +176,7 @@ inline T Hex(const char *str) template inline T Hex(const std::wstring &str) { - return Hex(mpt::ToCharset(mpt::CharsetUTF8, str)); + return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); } template @@ -195,7 +195,7 @@ inline T Hex(const wchar_t *str) template inline T Hex(const mpt::ustring &str) { - return Hex(mpt::ToCharset(mpt::CharsetUTF8, str)); + return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); } #endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h index 86a479464..8b55c9d4d 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h @@ -104,6 +104,18 @@ public: DWORD dummy = 0; // For Win9x threadHandle = CreateThread(NULL, 0, function, userData, 0, &dummy); } + + UnmanagedThread(UnmanagedThread &&) = default; + UnmanagedThread & operator=(UnmanagedThread &&) = default; + + UnmanagedThread(const UnmanagedThread &) = delete; + UnmanagedThread & operator=(const UnmanagedThread &) = delete; + + // unmanaged, user has to free resources + ~UnmanagedThread() + { + } + }; // Thread that operates on a member function diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp index 704d1f957..3b1440117 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp @@ -47,7 +47,7 @@ uint64 Now() mpt::ustring ToUString(uint64 time100ns) { - static const std::size_t bufsize = 256; + constexpr std::size_t bufsize = 256; mpt::ustring result; @@ -233,7 +233,7 @@ void MultimediaClock::SetPeriod(uint32 ms) { return; } - ms = mpt::clamp(ms, caps.wPeriodMin, caps.wPeriodMax); + ms = std::clamp(mpt::saturate_cast(ms), caps.wPeriodMin, caps.wPeriodMax); if(timeBeginPeriod(ms) != MMSYSERR_NOERROR) { return; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp index 1dbbc3507..6a975db2a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp @@ -20,9 +20,9 @@ #if MPT_OS_WINDOWS #include #include -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) || MPT_OS_WINDOWS_WINRT +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) || MPT_OS_WINDOWS_WINRT #include -#endif // MODPLUG_TRACKER || !NO_DMO || MPT_OS_WINDOWS_WINRT +#endif // MODPLUG_TRACKER || MPT_WITH_DMO || MPT_OS_WINDOWS_WINRT #endif // MPT_OS_WINDOWS @@ -36,7 +36,7 @@ namespace Util { -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) mpt::winstring CLSIDToString(CLSID clsid) @@ -298,7 +298,7 @@ bool IsValid(UUID uuid) } -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO } // namespace Util @@ -348,7 +348,7 @@ mpt::UUID UUIDFromWin32(::UUID uuid) return result; } -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) UUID::UUID(::UUID uuid) { @@ -360,7 +360,7 @@ UUID::operator ::UUID () const return UUIDToWin32(*this); } -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO #endif // MPT_OS_WINDOWS @@ -535,6 +535,7 @@ UUID::UUID(GUIDms guid) UUID::operator UUIDbin() const { UUIDbin result; + Clear(result); result.Data1 = GetData1(); result.Data2 = GetData2(); result.Data3 = GetData3(); @@ -545,6 +546,7 @@ UUID::operator UUIDbin() const UUID::operator GUIDms() const { GUIDms result; + Clear(result); result.Data1 = GetData1(); result.Data2 = GetData2(); result.Data3 = GetData3(); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h index 677fafce3..6892e9201 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h @@ -15,11 +15,13 @@ #include "Endianness.h" +#include + #if MPT_OS_WINDOWS -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) #include #include -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO #endif // MPT_OS_WINDOWS @@ -30,7 +32,7 @@ OPENMPT_NAMESPACE_BEGIN namespace Util { -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) // COM CLSID<->string conversion // A CLSID string is not necessarily a standard UUID string, @@ -56,7 +58,7 @@ GUID CreateGUID(); // Checks the UUID against the NULL UUID. Returns false if it is NULL, true otherwise. bool IsValid(UUID uuid); -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO } // namespace Util @@ -96,6 +98,9 @@ public: 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: + MPT_CONSTEXPR11_FUN uint64 GetData64_1() const noexcept { return (static_cast(Data1) << 32) | (static_cast(Data2) << 16) | (static_cast(Data3) << 0); } + MPT_CONSTEXPR11_FUN uint64 GetData64_2() const noexcept { return Data4; } public: // xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx // <--32-->-<16>-<16>-<-------64------> @@ -109,10 +114,10 @@ private: 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)) +#if MPT_OS_WINDOWS && (defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO)) explicit UUID(::UUID uuid); operator ::UUID () const; -#endif // MPT_OS_WINDOWS && (MODPLUG_TRACKER || !NO_DMO) +#endif // MPT_OS_WINDOWS && (MODPLUG_TRACKER || MPT_WITH_DMO) private: static MPT_CONSTEXPR11_FUN uint8 NibbleFromChar(char x) { @@ -120,7 +125,7 @@ private: ('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(""); + mpt::constexpr_throw(std::domain_error("")); } static MPT_CONSTEXPR11_FUN uint8 ByteFromHex(char x, char y) { @@ -159,7 +164,7 @@ public: | (static_cast(ParseHex16(str + 24)) << 32) | (static_cast(ParseHex32(str + 28)) << 0) ) - : throw std::domain_error(""); + : mpt::constexpr_throw(std::domain_error("")); } public: MPT_CONSTEXPR11_FUN UUID() noexcept : Data1(0), Data2(0), Data3(0), Data4(0) { } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp index adf1b23c6..12f62963a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp @@ -247,11 +247,12 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet " + EscapePosixShell(dirPosix) + "exit" + "\n"; + wrapperscript += scriptFilenamePosixEscape + "\n"; + wrapperscript += "MPT_RESULT=$?" "\n"; + wrapperscript += "echo ${MPT_RESULT} > " + dirPosixEscape + "exit" "\n"; } else if(flags[ExecFlagSplitOutput]) { - wrapperscript += std::string() + "(" + EscapePosixShell(scriptFilenamePosix) + "; echo $? >&4) 4>" + EscapePosixShell(dirPosix) + "exit 1>" + EscapePosixShell(dirPosix) + "out 2>" + EscapePosixShell(dirPosix) + "err" + "\n"; + wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 4>" + dirPosixEscape + "exit 1>" + dirPosixEscape + "out 2>" + dirPosixEscape + "err" "\n"; } else { - wrapperscript += std::string() + "(" + EscapePosixShell(scriptFilenamePosix) + "; echo $? >&4) 2>&1 4>" + EscapePosixShell(dirPosix) + "exit | tee " + EscapePosixShell(dirPosix) + "out" + "\n"; + wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 2>&1 4>" + dirPosixEscape + "exit | tee " + dirPosixEscape + "out" "\n"; } - wrapperscript += std::string() + "echo done > " + EscapePosixShell(dirPosix) + "done" + "\n"; - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "done" + "\n"; - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "exit" + "\n"; + wrapperscript += "echo done > " + dirPosixEscape + "done" "\n"; + cleanupscript += "rm " + dirPosixEscape + "done" "\n"; + cleanupscript += "rm " + dirPosixEscape + "exit" "\n"; if(flags[ExecFlagInteractive]) { // nothing } else if(flags[ExecFlagSplitOutput]) { - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "out" + "\n"; - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "err" + "\n"; + cleanupscript += "rm " + dirPosixEscape + "out" "\n"; + cleanupscript += "rm " + dirPosixEscape + "err" "\n"; } else { - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "out" + "\n"; + cleanupscript += "rm " + dirPosixEscape + "out" "\n"; } - cleanupscript += std::string() + "rm -r " + EscapePosixShell(dirPosix) + "filetree" + "\n"; - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "script.sh" + "\n"; - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "wrapper.sh" + "\n"; - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" + "\n"; - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "terminal.sh" + "\n"; + cleanupscript += "rm -r " + dirPosixEscape + "filetree" "\n"; + cleanupscript += "rm " + dirPosixEscape + "script.sh" "\n"; + cleanupscript += "rm " + dirPosixEscape + "wrapper.sh" "\n"; + cleanupscript += "rm " + dirPosixEscape + "wrapperstarter.sh" "\n"; + cleanupscript += "rm " + dirPosixEscape + "terminal.sh" "\n"; if(flags[ExecFlagAsync]) { wrapperscript += cleanupscript; @@ -346,10 +348,10 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet path = mpt::String::Split(mpt::ToUnicode(mpt::CharsetUTF8, file.first), U_("/")); + std::vector path = mpt::String::Split(mpt::ToUnicode(mpt::Charset::UTF8, file.first), U_("/")); mpt::PathString combinedPath = dirWindows + P_("filetree") + P_("\\"); if(path.size() > 1) { @@ -362,7 +364,7 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet/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 += " chmod u+x " + dirPosixEscape + "wrapperstarter.sh" "\n"; + terminalscript += " exec `command -v " + terminal + "` -e \"" + dirPosixEscape + "wrapperstarter.sh\"" "\n"; terminalscript += "fi" "\n"; } @@ -430,21 +432,19 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet commandline = std::vector(unixcommandW.data(), unixcommandW.data() + unixcommandW.length() + 1); - std::wstring titleW = mpt::ToWide(mpt::CharsetUTF8, title); - std::vector titleWv = std::vector(titleW.data(), titleW.data() + titleW.length() + 1); + std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand); + std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title); STARTUPINFOW startupInfo; MemsetZero(startupInfo); - startupInfo.lpTitle = &titleWv[0]; + startupInfo.lpTitle = titleW.data(); startupInfo.cb = sizeof(startupInfo); PROCESS_INFORMATION processInformation; MemsetZero(processInformation); @@ -454,15 +454,15 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet commandline = std::vector(unixcommandW.data(), unixcommandW.data() + unixcommandW.length() + 1); - std::wstring titleW = mpt::ToWide(mpt::CharsetUTF8, title); - std::vector titleWv = std::vector(titleW.data(), titleW.data() + titleW.length() + 1); + std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand); + std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title); STARTUPINFOW startupInfo; MemsetZero(startupInfo); - startupInfo.lpTitle = &titleWv[0]; + startupInfo.lpTitle = titleW.data(); startupInfo.cb = sizeof(startupInfo); PROCESS_INFORMATION processInformation; MemsetZero(processInformation); @@ -527,21 +524,20 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet >() diff --git a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp index 5ac868411..48d223a51 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp @@ -14,9 +14,9 @@ #include #include +#include #include "misc_util.h" -#include "mptBufferIO.h" OPENMPT_NAMESPACE_BEGIN @@ -26,11 +26,13 @@ namespace srlztn { -//#define SSB_LOGGING +#ifdef MPT_ALL_LOGGING +#define SSB_LOGGING +#endif #ifdef SSB_LOGGING -#define SSB_LOG(x) Log(x) +#define SSB_LOG(x) MPT_LOG(LogDebug, "serialization", x) #else #define SSB_LOG(x) MPT_DO { } MPT_WHILE_0 #endif @@ -73,7 +75,7 @@ static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str) void WriteItemString(std::ostream& oStrm, const std::string &str) { - uint32 id = static_cast(std::min(str.size(), (uint32_max >> 4))) << 4; + uint32 id = static_cast(std::min(str.size(), static_cast((uint32_max >> 4)))) << 4; id |= 12; // 12 == 1100b Binarywrite(oStrm, id); id >>= 4; @@ -91,7 +93,7 @@ void ReadItemString(std::istream& iStrm, std::string& str, const DataSize) const uint8 nSizeBytes = (id & 12) >> 2; // 12 == 1100b if (nSizeBytes > 0) { - uint8 bytes = std::min(3, nSizeBytes); + uint8 bytes = std::min(uint8(3), nSizeBytes); uint8 v2 = 0; uint8 v3 = 0; uint8 v4 = 0; @@ -102,7 +104,7 @@ void ReadItemString(std::istream& iStrm, std::string& str, const DataSize) id |= (v2 << 8) | (v3 << 16) | (v4 << 24); } // Limit to 1 MB. - str.resize(std::min(id >> 4, 1000000)); + str.resize(std::min(id >> 4, uint32(1000000))); for(size_t i = 0; i < str.size(); i++) iStrm.read(&str[i], 1); @@ -116,7 +118,7 @@ mpt::ustring ID::AsString() const { if(IsPrintable()) { - return mpt::ToUnicode(mpt::CharsetISO8859_1, m_ID); + return mpt::ToUnicode(mpt::Charset::ISO8859_1, m_ID); } if(m_ID.length() > 8) { @@ -133,22 +135,22 @@ const char Ssb::s_EntryID[3] = {'2','2','8'}; #ifdef SSB_LOGGING -static const MPT_UCHAR_TYPE tstrWriteHeader[] = UL_("Write header with ID = %1\n"); -static const MPT_UCHAR_TYPE tstrWriteProgress[] = UL_("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n"); -static const MPT_UCHAR_TYPE tstrWritingMap[] = UL_("Writing map to rpos: %1\n"); -static const MPT_UCHAR_TYPE tstrMapEntryWrite[] = UL_("Writing map entry: id=%1, rpos=%2, size=%3\n"); -static const MPT_UCHAR_TYPE strWriteNote[] = UL_("Write note: "); -static const MPT_UCHAR_TYPE tstrEndOfStream[] = UL_("End of stream(rpos): %1\n"); +static const mpt::uchar tstrWriteHeader[] = UL_("Write header with ID = %1\n"); +static const mpt::uchar tstrWriteProgress[] = UL_("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n"); +static const mpt::uchar tstrWritingMap[] = UL_("Writing map to rpos: %1\n"); +static const mpt::uchar tstrMapEntryWrite[] = UL_("Writing map entry: id=%1, rpos=%2, size=%3\n"); +static const mpt::uchar strWriteNote[] = UL_("Write note: "); +static const mpt::uchar tstrEndOfStream[] = UL_("End of stream(rpos): %1\n"); -static const MPT_UCHAR_TYPE tstrReadingHeader[] = UL_("Read header with expected ID = %1\n"); -static const MPT_UCHAR_TYPE strNoMapInFile[] = UL_("No map in the file.\n"); -static const MPT_UCHAR_TYPE strIdMismatch[] = UL_("ID mismatch, terminating read.\n"); -static const MPT_UCHAR_TYPE strIdMatch[] = UL_("ID match, continuing reading.\n"); -static const MPT_UCHAR_TYPE tstrReadingMap[] = UL_("Reading map from rpos: %1\n"); -static const MPT_UCHAR_TYPE tstrEndOfMap[] = UL_("End of map(rpos): %1\n"); -static const MPT_UCHAR_TYPE tstrReadProgress[] = UL_("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n"); -static const MPT_UCHAR_TYPE tstrNoEntryFound[] = UL_("No entry with id %1 found.\n"); -static const MPT_UCHAR_TYPE strReadNote[] = UL_("Read note: "); +static const mpt::uchar tstrReadingHeader[] = UL_("Read header with expected ID = %1\n"); +static const mpt::uchar strNoMapInFile[] = UL_("No map in the file.\n"); +static const mpt::uchar strIdMismatch[] = UL_("ID mismatch, terminating read.\n"); +static const mpt::uchar strIdMatch[] = UL_("ID match, continuing reading.\n"); +static const mpt::uchar tstrReadingMap[] = UL_("Reading map from rpos: %1\n"); +static const mpt::uchar tstrEndOfMap[] = UL_("End of map(rpos): %1\n"); +static const mpt::uchar tstrReadProgress[] = UL_("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n"); +static const mpt::uchar tstrNoEntryFound[] = UL_("No entry with id %1 found.\n"); +static const mpt::uchar strReadNote[] = UL_("Read note: "); #endif @@ -246,7 +248,7 @@ void SsbWrite::WriteMapItem(const ID &id, rposDataStart, nDatasize)); - mpt::ostringstream mapStream; + std::ostringstream mapStream; if(m_nIdbytes > 0) { @@ -718,7 +720,7 @@ void SsbWrite::FinishWrite() } // Seek to end. - oStrm.seekp(std::max(posMapEnd, posDataEnd)); + oStrm.seekp(std::max(posMapEnd, posDataEnd)); SSB_LOG(mpt::format(mpt::ustring(tstrEndOfStream))(oStrm.tellp() - m_posStart)); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h index 51984f946..ee8f58b51 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h +++ b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h @@ -180,7 +180,7 @@ template <> inline void Binaryread(std::istream& iStrm, float& data, const Offtype bytecount) { typedef IEEE754binary32LE T; - mpt::byte bytes[sizeof(T)]; + std::byte bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); // There is not much we can sanely do for truncated floats, @@ -192,7 +192,7 @@ template <> inline void Binaryread(std::istream& iStrm, double& data, const Offtype bytecount) { typedef IEEE754binary64LE T; - mpt::byte bytes[sizeof(T)]; + std::byte bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); // There is not much we can sanely do for truncated floats, @@ -234,7 +234,7 @@ public: template static ID FromInt(const T &val) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); typename mpt::make_le::type valle; valle = val; return ID(std::string(mpt::byte_cast(mpt::as_raw_memory(valle).data()), mpt::byte_cast(mpt::as_raw_memory(valle).data() + sizeof(valle)))); diff --git a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h index a6a36a56c..e1210fcef 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h +++ b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h @@ -18,9 +18,7 @@ #if defined(MODPLUG_TRACKER) -#if MPT_OS_WINDOWS - -#if !defined(MPT_BUILD_WINESUPPORT) +#if defined(MPT_WITH_MFC) // cppcheck-suppress missingInclude #include // MFC core @@ -41,7 +39,9 @@ // cppcheck-suppress missingInclude #include -#endif // !MPT_BUILD_WINESUPPORT +#endif // MPT_WITH_MFC + +#if MPT_OS_WINDOWS #include #include @@ -61,7 +61,9 @@ // this will be available everywhere #include "../common/mptBaseMacros.h" +// // +// // // @@ -97,23 +99,34 @@ // "mptBaseTypes.h" // "mptSpan.h" // +// // #include "../common/mptAlloc.h" // "mptBaseMacros.h" // "mptMemory.h" // "mptSpan.h" +// // -// +// // #include "../common/mptString.h" // // // +// // // +#include "../common/mptStringBuffer.h" + +#include "../common/mptOSError.h" +// "mptException.h" +// "mptString.h" +// +// + #include "../common/mptExceptionText.h" // "mptException.h" // "mptString.h" @@ -124,6 +137,7 @@ #include "../common/mptPathString.h" #include "../common/Logging.h" +// #include "../common/misc_util.h" diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.cpp b/Frameworks/OpenMPT/OpenMPT/common/version.cpp index 60ad87c54..40efdaf40 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/version.cpp @@ -23,6 +23,13 @@ OPENMPT_NAMESPACE_BEGIN +#define MPT_MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) Version( prefix ## v0 , prefix ## v1 , prefix ## v2 , prefix ## v3 ) +#define MPT_MAKE_VERSION_NUMERIC(v0,v1,v2,v3) MPT_MAKE_VERSION_NUMERIC_HELPER(0x, v0, v1, v2, v3) + +#define MPT_VERSION_CURRENT MPT_MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) + + + static_assert((MPT_VERSION_CURRENT.GetRawVersion() & 0xffffu) != 0x0000u, "Version numbers ending in .00.00 shall never exist again, as they make interpreting the version number ambiguous for file formats which can only store the two major parts of the version number (e.g. IT and S3M)."); @@ -80,10 +87,10 @@ bool Version::IsTestVersion() const noexcept { return ( // Legacy - (*this > MAKE_VERSION_NUMERIC(1,17,02,54) && *this < MAKE_VERSION_NUMERIC(1,18,02,00) && *this != MAKE_VERSION_NUMERIC(1,18,00,00)) + (*this > MPT_V("1.17.02.54") && *this < MPT_V("1.18.02.00") && *this != MPT_V("1.18.00.00")) || // Test builds have non-zero VER_MINORMINOR - (*this > MAKE_VERSION_NUMERIC(1,18,02,00) && ((m_Version & 0xFFFFFF00u) != m_Version)) + (*this > MPT_V("1.18.02.00") && ((m_Version & 0xFFFFFF00u) != m_Version)) ); } @@ -94,7 +101,7 @@ namespace Source { static mpt::ustring GetUrl() { #ifdef OPENMPT_VERSION_URL - return mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_VERSION_URL); + return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_URL); #else return mpt::ustring(); #endif @@ -132,15 +139,7 @@ static int GetRevision() } return ConvertStrTo(svnversion); #else - #if MPT_COMPILER_MSVC - #pragma message("SVN revision unknown. Please check your build system.") - #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. - // Try to provoke a warning with an unused variable. - int SVN_revision_unknown__Please_check_your_build_system; - #endif + MPT_WARNING_STATEMENT("SVN revision unknown. Please check your build system."); return 0; #endif } @@ -209,7 +208,7 @@ static bool IsPackage() static mpt::ustring GetSourceDate() { #if defined(OPENMPT_VERSION_DATE) - return mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_VERSION_DATE); + return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_DATE); #else return mpt::ustring(); #endif @@ -286,9 +285,9 @@ mpt::ustring GetBuildDateString() mpt::ustring result; #ifdef MODPLUG_TRACKER #if defined(OPENMPT_BUILD_DATE) - result = mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_BUILD_DATE ); + result = mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_BUILD_DATE ); #else - result = mpt::ToUnicode(mpt::CharsetASCII, __DATE__ " " __TIME__ ); + result = mpt::ToUnicode(mpt::Charset::ASCII, __DATE__ " " __TIME__ ); #endif #else // !MODPLUG_TRACKER result = SourceInfo::Current().Date(); @@ -317,18 +316,6 @@ mpt::ustring GetBuildFeaturesString() mpt::ustring retval; #ifdef LIBOPENMPT_BUILD retval = UL_("") - #if defined(MPT_CHARSET_WIN32) - UL_(" +WINAPI") - #endif - #if defined(MPT_CHARSET_ICONV) - UL_(" +ICONV") - #endif - #if defined(MPT_CHARSET_CODECVTUTF8) - UL_(" +CODECVTUTF8") - #endif - #if defined(MPT_CHARSET_INTERNAL) - UL_(" +INTERNALCHARSETS") - #endif #if defined(MPT_WITH_ZLIB) UL_(" +ZLIB") #endif @@ -364,13 +351,13 @@ mpt::ustring GetBuildFeaturesString() #else UL_(" -PLUGINS") #endif - #if !defined(NO_DMO) + #if defined(MPT_WITH_DMO) UL_(" +DMO") #endif ; #endif #ifdef MODPLUG_TRACKER - MPT_CONSTANT_IF(mpt::arch_bits == 64) + if constexpr(mpt::arch_bits == 64) { if (true && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP64) @@ -378,7 +365,7 @@ mpt::ustring GetBuildFeaturesString() ) { retval += UL_(" WIN64OLD"); } - } else MPT_CONSTANT_IF(mpt::arch_bits == 32) + } else if constexpr(mpt::arch_bits == 32) { if (true && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP) @@ -396,7 +383,7 @@ mpt::ustring GetBuildFeaturesString() #ifdef NO_VST UL_(" NO_VST") #endif - #ifdef NO_DMO + #ifndef MPT_WITH_DMO UL_(" NO_DMO") #endif #ifdef NO_PLUGINS @@ -588,19 +575,19 @@ mpt::ustring GetURL(Build::Url key) mpt::ustring GetFullCreditsString() { - return mpt::ToUnicode(mpt::CharsetUTF8, + return mpt::ToUnicode(mpt::Charset::UTF8, #ifdef MODPLUG_TRACKER "OpenMPT / ModPlug Tracker\n" #else "libopenmpt (based on OpenMPT / ModPlug Tracker)\n" #endif "\n" - "Copyright \xC2\xA9 2004-2019 Contributors\n" + "Copyright \xC2\xA9 2004-2020 Contributors\n" "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" "\n" "Contributors:\n" - "Johannes Schultz (2008-2019)\n" - "J\xC3\xB6rn Heusipp (2012-2019)\n" + "Johannes Schultz (2008-2020)\n" + "J\xC3\xB6rn Heusipp (2012-2020)\n" "Ahti Lepp\xC3\xA4nen (2005-2011)\n" "Robin Fernandes (2004-2007)\n" "Sergiy Pylypenko (2007)\n" @@ -610,6 +597,7 @@ mpt::ustring GetFullCreditsString() "\n" "Additional patch submitters:\n" "coda (https://coda.s3m.us/)\n" + "Jo\xC3\xA3o Baptista de Paula e Silva (https://joaobapt.com/)\n" "kode54 (https://kode54.net/)\n" "Revenant (https://revenant1.net/)\n" "xaimus (http://xaimus.com/)\n" @@ -657,6 +645,9 @@ mpt::ustring GetFullCreditsString() "Shayde / Reality Productions for Opal OPL3 emulator\n" "https://www.3eality.com/\n" "\n" + "Ryuhei Mori for TinyFFT\n" + "https://github.com/ryuhei-mori/tinyfft\n" + "\n" #ifdef MPT_WITH_ZLIB "Jean-loup Gailly and Mark Adler for zlib\n" "https://zlib.net/\n" @@ -736,7 +727,8 @@ mpt::ustring GetFullCreditsString() #endif #if defined(MPT_WITH_LAME) "The LAME project for LAME\n" - "http://lame.sourceforge.net/\n" + "https://lame.sourceforge.io/\n" + "\n" #endif #if defined(MPT_WITH_NLOHMANNJSON) "Niels Lohmann et al. for nlohmann-json\n" @@ -785,7 +777,7 @@ mpt::ustring GetFullCreditsString() mpt::ustring GetLicenseString() { return MPT_UTF8( - "Copyright (c) 2004-2019, OpenMPT contributors" "\n" + "Copyright (c) 2004-2020, OpenMPT contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.h b/Frameworks/OpenMPT/OpenMPT/common/version.h index 5a8d33274..95e8c5b8a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.h +++ b/Frameworks/OpenMPT/OpenMPT/common/version.h @@ -12,8 +12,11 @@ #include "BuildSettings.h" +#include "mptString.h" #include "FlagSet.h" +#include + OPENMPT_NAMESPACE_BEGIN @@ -106,6 +109,74 @@ public: // Returns true if a given version number is from a test build, false if it's a release build. bool IsTestVersion() const noexcept; +public: + + struct LiteralParser + { + + public: + + // Work-around for GCC 5 which complains about instanciating non-literal type inside a constexpr function when using mpt::constexpr_throw(std::runtime_error("")). + struct ParseException {}; + + private: + + static MPT_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) : + mpt::constexpr_throw(std::domain_error("")); + } + + public: + + static MPT_CONSTEXPR14_FUN Version Parse(const char * str, std::size_t len) + { + // 0123456789 + // 1.23.45.67 + uint8 v[4] = {0, 0, 0, 0}; + std::size_t field = 0; + std::size_t fieldlen = 0; + for(std::size_t i = 0; i < len; ++i) + { + char c = str[i]; + if(c == '.') + { + if(field >= 3) + { + mpt::constexpr_throw(ParseException()); + } + if(fieldlen == 0) + { + mpt::constexpr_throw(ParseException()); + } + field++; + fieldlen = 0; + } else if(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) + { + fieldlen++; + if(fieldlen > 2) + { + mpt::constexpr_throw(ParseException()); + } + v[field] <<= 4; + v[field] |= NibbleFromChar(c); + } else + { + mpt::constexpr_throw(ParseException()); + } + } + if(fieldlen == 0) + { + mpt::constexpr_throw(ParseException()); + } + return Version(v[0], v[1], v[2], v[3]); + } + + }; + }; MPT_CONSTEXPR11_FUN bool operator == (const Version &a, const Version &b) noexcept @@ -134,11 +205,15 @@ MPT_CONSTEXPR11_FUN bool operator > (const Version &a, const Version &b) noexcep } -//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) +MPT_CONSTEXPR14_FUN Version operator "" _LiteralVersionImpl (const char * str, std::size_t len) +{ + return Version::LiteralParser::Parse(str, len); +} + +// Create Version object from version string and check syntax, all at compile time. +// cppcheck false-positive +// cppcheck-suppress preprocessorErrorDirective +#define MPT_V(strver) Version{MPT_FORCE_CONSTEXPR(( strver ## _LiteralVersionImpl ).GetRawVersion())} diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 35bb47f03..0a66669a7 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -1,7 +1,7 @@ /* * versionNumber.h * --------------- - * Purpose: OpenMPT version handling. + * Purpose: OpenMPT version number. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -14,16 +14,10 @@ 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. +// Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 -#define VER_MAJOR 28 -#define VER_MINOR 08 +#define VER_MAJOR 29 +#define VER_MINOR 03 #define VER_MINORMINOR 00 -//Numerical value of the version. -#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/fuzz-master.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh index 0820ff7a8..227d9695e 100755 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh @@ -7,9 +7,6 @@ mkdir $FUZZING_TEMPDIR sudo mount -t tmpfs -o size=200M none $FUZZING_TEMPDIR rm -rf $FUZZING_TEMPDIR/bin mkdir $FUZZING_TEMPDIR/bin -cp ../../bin/* $FUZZING_TEMPDIR/bin/ -rm $FUZZING_TEMPDIR/bin/libopenmpt.so -cp ../../bin/libopenmpt.so.1 $FUZZING_TEMPDIR/bin/libopenmpt.so -cp ../../bin/libopenmpt.so.1 $FUZZING_TEMPDIR/bin/libopenmpt.so.1 +cp -d ../../bin/* $FUZZING_TEMPDIR/bin/ LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01 diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh index 43d0f4bb4..7b93d4c33 100755 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh @@ -1,21 +1,16 @@ #!/usr/bin/env bash cd "${0%/*}" -AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/google/AFL/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')" +AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/vanhauser-thc/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')" AFL_FILENAME="$AFL_VERSION.tar.gz" -AFL_URL="https://github.com/google/AFL/archive/$AFL_FILENAME" +AFL_URL="https://github.com/vanhauser-thc/AFLplusplus/archive/$AFL_FILENAME" rm $AFL_FILENAME wget $AFL_URL || exit tar -xzvf $AFL_FILENAME rm $AFL_FILENAME -cd AFL-* -make || exit -cd llvm_mode -# may need to prepend LLVM_CONFIG=/usr/bin/llvm-config-3.8 or similar, depending on the system -make || exit -cd ../libdislocator -make || exit -cd ../.. +cd AFLplusplus-* +make source-only || exit +cd .. rm -rf afl -mv AFL-* afl \ No newline at end of file +mv AFLplusplus-* afl \ No newline at end of file diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE new file mode 100644 index 000000000..e81fc3ff8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2004-2020, OpenMPT contributors +Copyright (c) 1997-2003, Olivier Lapicque +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the OpenMPT project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am new file mode 100644 index 000000000..9f4f96516 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am @@ -0,0 +1,46 @@ + +ACLOCAL_AMFLAGS = -I m4 --install +EXTRA_DIST = +EXTRA_DIST += LICENSE +EXTRA_DIST += libopenmpt_modplug.pc.in +EXTRA_DIST += libmodplug.pc.in +EXTRA_DIST += test.sh +MOSTLYCLEANFILES = + +dist_doc_DATA = +dist_doc_DATA += LICENSE +nobase_dist_doc_DATA = + +bin_PROGRAMS = +check_PROGRAMS = +lib_LTLIBRARIES = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = +nobase_include_HEADERS = + +if ENABLE_LIBOPENMPT_MODPLUG +lib_LTLIBRARIES += libopenmpt_modplug.la +libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libopenmpt_modplug_la_SOURCES = +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif + +if ENABLE_LIBMODPLUG +pkgconfig_DATA += libmodplug.pc +lib_LTLIBRARIES += libmodplug.la +libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h +libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libmodplug_la_SOURCES = +libmodplug_la_SOURCES += libopenmpt_modplug.c +libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh new file mode 100755 index 000000000..05ff21447 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +autoreconf -i diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh new file mode 100755 index 000000000..fac1804fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh +./configure +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in new file mode 100644 index 000000000..b72c3f925 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in @@ -0,0 +1,81 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* define if the compiler supports basic C++14 syntax */ +#undef HAVE_CXX14 + +/* define if the compiler supports basic C++17 syntax */ +#undef HAVE_CXX17 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac new file mode 100644 index 000000000..fd543eca3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac @@ -0,0 +1,66 @@ +AC_INIT([libopenmpt-modplug], [0.8.8.5-openmpt1], [https://bugs.openmpt.org/], [libopenmpt-modplug], [https://lib.openmpt.org/]) +AC_PREREQ([2.68]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_FILES([Makefile libopenmpt_modplug.pc libmodplug.pc]) + +AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects]) + +AM_PROG_AR + +LT_INIT + +AC_SYS_LARGEFILE + +PKG_PROG_PKG_CONFIG([0.24]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_INSTALL + +LIBOPENMPT_REQUIRES_PRIVATE= +LIBOPENMPT_LIBS_PRIVATE= + +# We want a modern C compiler +AC_PROG_CC_STDC +#AC_PROG_CC_C99 + +# We need C++11 support +AX_CXX_COMPILE_STDCXX(17, [noext], [optional]) +AS_IF([test "x$HAVE_CXX17" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(14, [noext], [optional]) + AS_IF([test "x$HAVE_CXX14" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(11, [noext], [mandatory]) + ],[] + ) + ],[] +) + +AC_LANG_PUSH([C]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden"]) +AX_CFLAGS_WARN_ALL +AC_LANG_POP([C]) + +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden"]) +AX_CXXFLAGS_WARN_ALL +AC_LANG_POP([C++]) + +PKG_CHECK_MODULES([LIBOPENMPT], [libopenmpt]) + +# libmodplug emulation +AC_ARG_ENABLE([libopenmpt_modplug], AS_HELP_STRING([--disable-libopenmpt_modplug], [Disable the libopenmpt_modplug emulation library of the libmodplug interface.])) +AM_CONDITIONAL([ENABLE_LIBOPENMPT_MODPLUG], [test "x$enable_libopenmpt_modplug" != "xno"]) + +# libmodplug replacement +AC_ARG_ENABLE([libmodplug], AS_HELP_STRING([--enable-libmodplug], [Enable libmodplug replacement library based on libopenmpt. +WARNING: This will replace your current libmodplug installation. +CAUTION: The emulation of the libmodplug interface is not complete as libmodplug exposes lots of internal implementation details. If any of those is used by an application, the emulation via libopenmpt will fail and/or crash. +])) +AM_CONDITIONAL([ENABLE_LIBMODPLUG], [test "x$enable_libmodplug" = "xyes"]) + +AC_OUTPUT diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh new file mode 100755 index 000000000..e96826f07 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +./test.sh + +./autogen.sh +./configure +make dist diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in similarity index 91% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in index dc35085b2..e8c796d1d 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in @@ -5,7 +5,7 @@ includedir=${prefix}/include Name: libmodplug Description: The ModPlug mod file playing library (emulated via libopenmpt). -Version: 0.8.8.5 +Version: @PACKAGE_VERSION@ Requires.private: libopenmpt Libs: -L${libdir} -lmodplug Libs.private: diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/modplug.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/modplug.h diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/sndfile.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/sndfile.h diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/stdafx.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/stdafx.h diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c similarity index 99% rename from Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c index da13edc3f..51dd3d79f 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c @@ -19,7 +19,7 @@ #endif #endif /* _MSC_VER */ -#include "libopenmpt.h" +#include #include #include diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in new file mode 100644 index 000000000..89c1fee89 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libopenmpt_modplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lopenmpt_modplug +Libs.private: +Cflags: -I${includedir} diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp similarity index 99% rename from Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp index f487339f2..fd5375d3b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp @@ -36,7 +36,7 @@ Metadata and other state is not provided or updated. #endif #endif /* _MSC_VER */ -#include "libopenmpt.hpp" +#include #include #include diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..0b6cb3a7d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,972 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 9 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh new file mode 100755 index 000000000..7df7ed3fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh + +./configure +make +make distcheck +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE new file mode 100644 index 000000000..e81fc3ff8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2004-2020, OpenMPT contributors +Copyright (c) 1997-2003, Olivier Lapicque +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the OpenMPT project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am new file mode 100644 index 000000000..9f4f96516 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am @@ -0,0 +1,46 @@ + +ACLOCAL_AMFLAGS = -I m4 --install +EXTRA_DIST = +EXTRA_DIST += LICENSE +EXTRA_DIST += libopenmpt_modplug.pc.in +EXTRA_DIST += libmodplug.pc.in +EXTRA_DIST += test.sh +MOSTLYCLEANFILES = + +dist_doc_DATA = +dist_doc_DATA += LICENSE +nobase_dist_doc_DATA = + +bin_PROGRAMS = +check_PROGRAMS = +lib_LTLIBRARIES = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = +nobase_include_HEADERS = + +if ENABLE_LIBOPENMPT_MODPLUG +lib_LTLIBRARIES += libopenmpt_modplug.la +libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libopenmpt_modplug_la_SOURCES = +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif + +if ENABLE_LIBMODPLUG +pkgconfig_DATA += libmodplug.pc +lib_LTLIBRARIES += libmodplug.la +libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h +libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libmodplug_la_SOURCES = +libmodplug_la_SOURCES += libopenmpt_modplug.c +libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh new file mode 100755 index 000000000..05ff21447 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +autoreconf -i diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh new file mode 100755 index 000000000..fac1804fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh +./configure +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in new file mode 100644 index 000000000..b72c3f925 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in @@ -0,0 +1,81 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* define if the compiler supports basic C++14 syntax */ +#undef HAVE_CXX14 + +/* define if the compiler supports basic C++17 syntax */ +#undef HAVE_CXX17 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac new file mode 100644 index 000000000..341700686 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac @@ -0,0 +1,66 @@ +AC_INIT([libopenmpt-modplug], [0.8.9.0-openmpt1], [https://bugs.openmpt.org/], [libopenmpt-modplug], [https://lib.openmpt.org/]) +AC_PREREQ([2.68]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_FILES([Makefile libopenmpt_modplug.pc libmodplug.pc]) + +AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects]) + +AM_PROG_AR + +LT_INIT + +AC_SYS_LARGEFILE + +PKG_PROG_PKG_CONFIG([0.24]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_INSTALL + +LIBOPENMPT_REQUIRES_PRIVATE= +LIBOPENMPT_LIBS_PRIVATE= + +# We want a modern C compiler +AC_PROG_CC_STDC +#AC_PROG_CC_C99 + +# We need C++11 support +AX_CXX_COMPILE_STDCXX(17, [noext], [optional]) +AS_IF([test "x$HAVE_CXX17" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(14, [noext], [optional]) + AS_IF([test "x$HAVE_CXX14" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(11, [noext], [mandatory]) + ],[] + ) + ],[] +) + +AC_LANG_PUSH([C]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden -DSYM_VISIBILITY"]) +AX_CFLAGS_WARN_ALL +AC_LANG_POP([C]) + +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden -DSYM_VISIBILITY"]) +AX_CXXFLAGS_WARN_ALL +AC_LANG_POP([C++]) + +PKG_CHECK_MODULES([LIBOPENMPT], [libopenmpt]) + +# libmodplug emulation +AC_ARG_ENABLE([libopenmpt_modplug], AS_HELP_STRING([--disable-libopenmpt_modplug], [Disable the libopenmpt_modplug emulation library of the libmodplug interface.])) +AM_CONDITIONAL([ENABLE_LIBOPENMPT_MODPLUG], [test "x$enable_libopenmpt_modplug" != "xno"]) + +# libmodplug replacement +AC_ARG_ENABLE([libmodplug], AS_HELP_STRING([--enable-libmodplug], [Enable libmodplug replacement library based on libopenmpt. +WARNING: This will replace your current libmodplug installation. +CAUTION: The emulation of the libmodplug interface is not complete as libmodplug exposes lots of internal implementation details. If any of those is used by an application, the emulation via libopenmpt will fail and/or crash. +])) +AM_CONDITIONAL([ENABLE_LIBMODPLUG], [test "x$enable_libmodplug" = "xyes"]) + +AC_OUTPUT diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh new file mode 100755 index 000000000..e96826f07 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +./test.sh + +./autogen.sh +./configure +make dist diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in new file mode 100644 index 000000000..e8c796d1d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libmodplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lmodplug +Libs.private: +Cflags: -I${includedir} + diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h new file mode 100644 index 000000000..62dd1f42c --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h @@ -0,0 +1,185 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda (C interface wrapper) + */ + +#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 +#elif defined(MODPLUG_BUILD) && defined(SYM_VISIBILITY) +# define MODPLUG_EXPORT __attribute__((visibility("default"))) +#else +#define MODPLUG_EXPORT +#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. */ +MODPLUG_EXPORT ModPlugFile* ModPlug_Load(const void* data, int size); +/* Unload a mod 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. */ +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. */ +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. */ +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 + * 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. */ +MODPLUG_EXPORT 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 mStereoSeparation; /* Stereo separation, 1 - 256 */ + int mMaxMixChannels; /* Maximum number of mixing channels (polyphony), 32 - 256 */ + + 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. */ +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) */ +MODPLUG_EXPORT unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +MODPLUG_EXPORT void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; + +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); + +MODPLUG_EXPORT void ModPlug_SeekOrder(ModPlugFile* file,int order); +MODPLUG_EXPORT int ModPlug_GetModuleType(ModPlugFile* file); +MODPLUG_EXPORT char* ModPlug_GetMessage(ModPlugFile* file); + +#define MODPLUG_NO_FILESAVE /* experimental yet. must match stdafx.h. */ +#ifndef MODPLUG_NO_FILESAVE +/* + * EXPERIMENTAL Export Functions + */ +/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); + +/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); + +/*Export to a Amiga MOD file. EXPERIMENTAL.*/ +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 :-) */ +MODPLUG_EXPORT char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +#endif /* MODPLUG_NO_FILESAVE */ + +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 + */ +MODPLUG_EXPORT 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) + */ +MODPLUG_EXPORT void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +MODPLUG_EXPORT void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h new file mode 100644 index 000000000..f390e4144 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h @@ -0,0 +1,1028 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) +*/ + +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +#include "config.h" +#define CONFIG_H_INCLUDED 1 +#endif + +#ifndef MODPLUG_SNDFILE_H +#define MODPLUG_SNDFILE_H + +#ifdef UNDER_CE +int _strnicmp(const char *str1,const char *str2, int n); +#endif + +#ifndef LPCBYTE +typedef const BYTE * LPCBYTE; +#endif + +#define MOD_AMIGAC2 0x1AB +#define MAX_SAMPLE_LENGTH 16000000 +#define MAX_SAMPLE_RATE 192000 +#define MAX_ORDERS 256 +#define MAX_PATTERNS 240 +#define MAX_SAMPLES 240 +#define MAX_INSTRUMENTS MAX_SAMPLES +#ifdef MODPLUG_FASTSOUNDLIB +#define MAX_CHANNELS 80 +#else +#define MAX_CHANNELS 128 +#endif +#define MAX_BASECHANNELS 64 +#define MAX_ENVPOINTS 32 +#define MIN_PERIOD 0x0020 +#define MAX_PERIOD 0xFFFF +#define MAX_PATTERNNAME 32 +#define MAX_CHANNELNAME 20 +#define MAX_INFONAME 80 +#define MAX_EQ_BANDS 6 +#define MAX_MIXPLUGINS 8 + + +#define MOD_TYPE_NONE 0x00 +#define MOD_TYPE_MOD 0x01 +#define MOD_TYPE_S3M 0x02 +#define MOD_TYPE_XM 0x04 +#define MOD_TYPE_MED 0x08 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type +#define MAX_MODTYPE 24 + + + +// Channel flags: +// Bits 0-7: Sample Flags +#define CHN_16BIT 0x01 +#define CHN_LOOP 0x02 +#define CHN_PINGPONGLOOP 0x04 +#define CHN_SUSTAINLOOP 0x08 +#define CHN_PINGPONGSUSTAIN 0x10 +#define CHN_PANNING 0x20 +#define CHN_STEREO 0x40 +#define CHN_PINGPONGFLAG 0x80 +// Bits 8-31: Channel Flags +#define CHN_MUTE 0x100 +#define CHN_KEYOFF 0x200 +#define CHN_NOTEFADE 0x400 +#define CHN_SURROUND 0x800 +#define CHN_NOIDO 0x1000 +#define CHN_HQSRC 0x2000 +#define CHN_FILTER 0x4000 +#define CHN_VOLUMERAMP 0x8000 +#define CHN_VIBRATO 0x10000 +#define CHN_TREMOLO 0x20000 +#define CHN_PANBRELLO 0x40000 +#define CHN_PORTAMENTO 0x80000 +#define CHN_GLISSANDO 0x100000 +#define CHN_VOLENV 0x200000 +#define CHN_PANENV 0x400000 +#define CHN_PITCHENV 0x800000 +#define CHN_FASTVOLRAMP 0x1000000 +#define CHN_EXTRALOUD 0x2000000 +#define CHN_REVERB 0x4000000 +#define CHN_NOREVERB 0x8000000 + + +#define ENV_VOLUME 0x0001 +#define ENV_VOLSUSTAIN 0x0002 +#define ENV_VOLLOOP 0x0004 +#define ENV_PANNING 0x0008 +#define ENV_PANSUSTAIN 0x0010 +#define ENV_PANLOOP 0x0020 +#define ENV_PITCH 0x0040 +#define ENV_PITCHSUSTAIN 0x0080 +#define ENV_PITCHLOOP 0x0100 +#define ENV_SETPANNING 0x0200 +#define ENV_FILTER 0x0400 +#define ENV_VOLCARRY 0x0800 +#define ENV_PANCARRY 0x1000 +#define ENV_PITCHCARRY 0x2000 + +#define CMD_NONE 0 +#define CMD_ARPEGGIO 1 +#define CMD_PORTAMENTOUP 2 +#define CMD_PORTAMENTODOWN 3 +#define CMD_TONEPORTAMENTO 4 +#define CMD_VIBRATO 5 +#define CMD_TONEPORTAVOL 6 +#define CMD_VIBRATOVOL 7 +#define CMD_TREMOLO 8 +#define CMD_PANNING8 9 +#define CMD_OFFSET 10 +#define CMD_VOLUMESLIDE 11 +#define CMD_POSITIONJUMP 12 +#define CMD_VOLUME 13 +#define CMD_PATTERNBREAK 14 +#define CMD_RETRIG 15 +#define CMD_SPEED 16 +#define CMD_TEMPO 17 +#define CMD_TREMOR 18 +#define CMD_MODCMDEX 19 +#define CMD_S3MCMDEX 20 +#define CMD_CHANNELVOLUME 21 +#define CMD_CHANNELVOLSLIDE 22 +#define CMD_GLOBALVOLUME 23 +#define CMD_GLOBALVOLSLIDE 24 +#define CMD_KEYOFF 25 +#define CMD_FINEVIBRATO 26 +#define CMD_PANBRELLO 27 +#define CMD_XFINEPORTAUPDOWN 28 +#define CMD_PANNINGSLIDE 29 +#define CMD_SETENVPOSITION 30 +#define CMD_MIDI 31 + + +// Volume Column commands +#define VOLCMD_VOLUME 1 +#define VOLCMD_PANNING 2 +#define VOLCMD_VOLSLIDEUP 3 +#define VOLCMD_VOLSLIDEDOWN 4 +#define VOLCMD_FINEVOLUP 5 +#define VOLCMD_FINEVOLDOWN 6 +#define VOLCMD_VIBRATOSPEED 7 +#define VOLCMD_VIBRATO 8 +#define VOLCMD_PANSLIDELEFT 9 +#define VOLCMD_PANSLIDERIGHT 10 +#define VOLCMD_TONEPORTAMENTO 11 +#define VOLCMD_PORTAUP 12 +#define VOLCMD_PORTADOWN 13 + +#define RSF_16BIT 0x04 +#define RSF_STEREO 0x08 + +#define RS_PCM8S 0 // 8-bit signed +#define RS_PCM8U 1 // 8-bit unsigned +#define RS_PCM8D 2 // 8-bit delta values +#define RS_ADPCM4 3 // 4-bit ADPCM-packed +#define RS_PCM16D 4 // 16-bit delta values +#define RS_PCM16S 5 // 16-bit signed +#define RS_PCM16U 6 // 16-bit unsigned +#define RS_PCM16M 7 // 16-bit motorola order +#define RS_STPCM8S (RS_PCM8S|RSF_STEREO) // stereo 8-bit signed +#define RS_STPCM8U (RS_PCM8U|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STPCM8D (RS_PCM8D|RSF_STEREO) // stereo 8-bit delta values +#define RS_STPCM16S (RS_PCM16S|RSF_STEREO) // stereo 16-bit signed +#define RS_STPCM16U (RS_PCM16U|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STPCM16D (RS_PCM16D|RSF_STEREO) // stereo 16-bit delta values +#define RS_STPCM16M (RS_PCM16M|RSF_STEREO) // stereo 16-bit signed big endian +// IT 2.14 compressed samples +#define RS_IT2148 0x10 +#define RS_IT21416 0x14 +#define RS_IT2158 0x12 +#define RS_IT21516 0x16 +// AMS Packed Samples +#define RS_AMS8 0x11 +#define RS_AMS16 0x15 +// DMF Huffman compression +#define RS_DMF8 0x13 +#define RS_DMF16 0x17 +// MDL Huffman compression +#define RS_MDL8 0x20 +#define RS_MDL16 0x24 +#define RS_PTM8DTO16 0x25 +// Stereo Interleaved Samples +#define RS_STIPCM8S (RS_PCM8S|0x40|RSF_STEREO) // stereo 8-bit signed +#define RS_STIPCM8U (RS_PCM8U|0x40|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STIPCM16S (RS_PCM16S|0x40|RSF_STEREO) // stereo 16-bit signed +#define RS_STIPCM16U (RS_PCM16U|0x40|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STIPCM16M (RS_PCM16M|0x40|RSF_STEREO) // stereo 16-bit signed big endian +// 24-bit signed +#define RS_PCM24S (RS_PCM16S|0x80) // mono 24-bit signed +#define RS_STIPCM24S (RS_PCM16S|0x80|RSF_STEREO) // stereo 24-bit signed +#define RS_PCM32S (RS_PCM16S|0xC0) // mono 24-bit signed +#define RS_STIPCM32S (RS_PCM16S|0xC0|RSF_STEREO) // stereo 24-bit signed + +// NNA types +#define NNA_NOTECUT 0 +#define NNA_CONTINUE 1 +#define NNA_NOTEOFF 2 +#define NNA_NOTEFADE 3 + +// DCT types +#define DCT_NONE 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INSTRUMENT 3 + +// DNA types +#define DNA_NOTECUT 0 +#define DNA_NOTEOFF 1 +#define DNA_NOTEFADE 2 + +// Mixer Hardware-Dependent features +#define SYSMIX_ENABLEMMX 0x01 +#define SYSMIX_WINDOWSNT 0x02 +#define SYSMIX_SLOWCPU 0x04 +#define SYSMIX_FASTCPU 0x08 + +// Module flags +#define SONG_EMBEDMIDICFG 0x0001 +#define SONG_FASTVOLSLIDES 0x0002 +#define SONG_ITOLDEFFECTS 0x0004 +#define SONG_ITCOMPATMODE 0x0008 +#define SONG_LINEARSLIDES 0x0010 +#define SONG_PATTERNLOOP 0x0020 +#define SONG_STEP 0x0040 +#define SONG_PAUSED 0x0080 +#define SONG_FADINGSONG 0x0100 +#define SONG_ENDREACHED 0x0200 +#define SONG_GLOBALFADE 0x0400 +#define SONG_CPUVERYHIGH 0x0800 +#define SONG_FIRSTTICK 0x1000 +#define SONG_MPTFILTERMODE 0x2000 +#define SONG_SURROUNDPAN 0x4000 +#define SONG_EXFILTERRANGE 0x8000 +#define SONG_AMIGALIMITS 0x10000 + +// Global Options (Renderer) +#define SNDMIX_REVERSESTEREO 0x0001 +#define SNDMIX_NOISEREDUCTION 0x0002 +#define SNDMIX_AGC 0x0004 +#define SNDMIX_NORESAMPLING 0x0008 +#define SNDMIX_HQRESAMPLER 0x0010 +#define SNDMIX_MEGABASS 0x0020 +#define SNDMIX_SURROUND 0x0040 +#define SNDMIX_REVERB 0x0080 +#define SNDMIX_EQ 0x0100 +#define SNDMIX_SOFTPANNING 0x0200 +#define SNDMIX_ULTRAHQSRCMODE 0x0400 +// Misc Flags (can safely be turned on or off) +#define SNDMIX_DIRECTTODISK 0x10000 +#define SNDMIX_ENABLEMMX 0x20000 +#define SNDMIX_NOBACKWARDJUMPS 0x40000 +#define SNDMIX_MAXDEFAULTPAN 0x80000 // Used by the MOD loader + + +// Reverb Types (GM2 Presets) +enum { + REVERBTYPE_SMALLROOM, + REVERBTYPE_MEDIUMROOM, + REVERBTYPE_LARGEROOM, + REVERBTYPE_SMALLHALL, + REVERBTYPE_MEDIUMHALL, + REVERBTYPE_LARGEHALL, + NUM_REVERBTYPES +}; + + +enum { + SRCMODE_NEAREST, + SRCMODE_LINEAR, + SRCMODE_SPLINE, + SRCMODE_POLYPHASE, + NUM_SRC_MODES +}; + + +// Sample Struct +typedef struct _MODINSTRUMENT +{ + UINT nLength,nLoopStart,nLoopEnd; + UINT nSustainStart, nSustainEnd; + signed char *pSample; + UINT nC4Speed; + WORD nPan; + WORD nVolume; + WORD nGlobalVol; + WORD uFlags; + signed char RelativeTone; + signed char nFineTune; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; + CHAR name[22]; +} MODINSTRUMENT; + + +// Instrument Struct +typedef struct _INSTRUMENTHEADER +{ + UINT nFadeOut; + DWORD dwFlags; + WORD nGlobalVol; + WORD nPan; + WORD VolPoints[MAX_ENVPOINTS]; + WORD PanPoints[MAX_ENVPOINTS]; + WORD PitchPoints[MAX_ENVPOINTS]; + BYTE VolEnv[MAX_ENVPOINTS]; + BYTE PanEnv[MAX_ENVPOINTS]; + BYTE PitchEnv[MAX_ENVPOINTS]; + BYTE Keyboard[128]; + BYTE NoteMap[128]; + + BYTE nVolEnv; + BYTE nPanEnv; + BYTE nPitchEnv; + BYTE nVolLoopStart; + BYTE nVolLoopEnd; + BYTE nVolSustainBegin; + BYTE nVolSustainEnd; + BYTE nPanLoopStart; + BYTE nPanLoopEnd; + BYTE nPanSustainBegin; + BYTE nPanSustainEnd; + BYTE nPitchLoopStart; + BYTE nPitchLoopEnd; + BYTE nPitchSustainBegin; + BYTE nPitchSustainEnd; + BYTE nNNA; + BYTE nDCT; + BYTE nDNA; + BYTE nPanSwing; + BYTE nVolSwing; + BYTE nIFC; + BYTE nIFR; + WORD wMidiBank; + BYTE nMidiProgram; + BYTE nMidiChannel; + BYTE nMidiDrumKey; + signed char nPPS; + unsigned char nPPC; + CHAR name[32]; + CHAR filename[12]; +} INSTRUMENTHEADER; + + +// Channel Struct +typedef struct _MODCHANNEL +{ + // First 32-bytes: Most used mixing information: don't change it + signed char * pCurrentSample; + DWORD nPos; + DWORD nPosLo; // actually 16-bit + LONG nInc; // 16.16 + LONG nRightVol; + LONG nLeftVol; + LONG nRightRamp; + LONG nLeftRamp; + // 2nd cache line + DWORD nLength; + DWORD dwFlags; + DWORD nLoopStart; + DWORD nLoopEnd; + LONG nRampRightVol; + LONG nRampLeftVol; + LONG nFilter_Y1, nFilter_Y2, nFilter_Y3, nFilter_Y4; + LONG nFilter_A0, nFilter_B0, nFilter_B1; + LONG nROfs, nLOfs; + LONG nRampLength; + // Information not used in the mixer + signed char * pSample; + LONG nNewRightVol, nNewLeftVol; + LONG nRealVolume, nRealPan; + LONG nVolume, nPan, nFadeOutVol; + LONG nPeriod, nC4Speed, nPortamentoDest; + INSTRUMENTHEADER *pHeader; + MODINSTRUMENT *pInstrument; + DWORD nVolEnvPosition, nPanEnvPosition, nPitchEnvPosition; + DWORD nMasterChn, nVUMeter; + LONG nGlobalVol, nInsVol; + LONG nFineTune, nTranspose; + LONG nPortamentoSlide, nAutoVibDepth; + UINT nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; + // 16-bit members + signed short nVolSwing, nPanSwing; + // 8-bit members + BYTE nNote, nNNA; + BYTE nNewNote, nNewIns, nCommand, nArpeggio; + BYTE nOldVolumeSlide, nOldFineVolUpDown; + BYTE nOldPortaUpDown, nOldFinePortaUpDown; + BYTE nOldPanSlide, nOldChnVolSlide; + BYTE nVibratoType, nVibratoSpeed, nVibratoDepth; + BYTE nTremoloType, nTremoloSpeed, nTremoloDepth; + BYTE nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; + BYTE nOldCmdEx, nOldVolParam, nOldTempo; + BYTE nOldOffset, nOldHiOffset; + BYTE nCutOff, nResonance; + BYTE nRetrigCount, nRetrigParam; + BYTE nTremorCount, nTremorParam; + BYTE nPatternLoop, nPatternLoopCount; + BYTE nRowNote, nRowInstr; + BYTE nRowVolCmd, nRowVolume; + BYTE nRowCommand, nRowParam; + BYTE nLeftVU, nRightVU; + BYTE nActiveMacro, nPadding; +} MODCHANNEL; + + +typedef struct _MODCHANNELSETTINGS +{ + UINT nPan; + UINT nVolume; + DWORD dwFlags; + UINT nMixPlugin; + char szName[MAX_CHANNELNAME]; // changed from CHAR +} MODCHANNELSETTINGS; + + +typedef struct _MODCOMMAND +{ + BYTE note; + BYTE instr; + BYTE volcmd; + BYTE command; + BYTE vol; + BYTE param; +} MODCOMMAND, *LPMODCOMMAND; + +//////////////////////////////////////////////////////////////////// +// Mix Plugins +#define MIXPLUG_MIXREADY 0x01 // Set when cleared + +class MODPLUG_EXPORT IMixPlugin +{ +public: + virtual ~IMixPlugin() {}; + virtual int AddRef() = 0; + virtual int Release() = 0; + virtual void SaveAllParameters() = 0; + virtual void RestoreAllParameters() = 0; + virtual void Process(float *pOutL, float *pOutR, unsigned long nSamples) = 0; + virtual void Init(unsigned long nFreq, int bReset) = 0; + virtual void MidiSend(DWORD dwMidiCode) = 0; + virtual void MidiCommand(UINT nMidiCh, UINT nMidiProg, UINT note, UINT vol) = 0; +}; + + +#define MIXPLUG_INPUTF_MASTEREFFECT 0x01 // Apply to master mix +#define MIXPLUG_INPUTF_BYPASS 0x02 // Bypass effect +#define MIXPLUG_INPUTF_WETMIX 0x04 // Wet Mix (dry added) + +typedef struct _SNDMIXPLUGINSTATE +{ + DWORD dwFlags; // MIXPLUG_XXXX + LONG nVolDecayL, nVolDecayR; // Buffer click removal + int *pMixBuffer; // Stereo effect send buffer + float *pOutBufferL; // Temp storage for int -> float conversion + float *pOutBufferR; +} SNDMIXPLUGINSTATE, *PSNDMIXPLUGINSTATE; + +typedef struct _SNDMIXPLUGININFO +{ + DWORD dwPluginId1; + DWORD dwPluginId2; + DWORD dwInputRouting; // MIXPLUG_INPUTF_XXXX + DWORD dwOutputRouting; // 0=mix 0x80+=fx + DWORD dwReserved[4]; // Reserved for routing info + CHAR szName[32]; + CHAR szLibraryName[64]; // original DLL name +} SNDMIXPLUGININFO, *PSNDMIXPLUGININFO; // Size should be 128 + +typedef struct _SNDMIXPLUGIN +{ + IMixPlugin *pMixPlugin; + PSNDMIXPLUGINSTATE pMixState; + ULONG nPluginDataSize; + PVOID pPluginData; + SNDMIXPLUGININFO Info; +} SNDMIXPLUGIN, *PSNDMIXPLUGIN; + +typedef BOOL (*PMIXPLUGINCREATEPROC)(PSNDMIXPLUGIN); + +//////////////////////////////////////////////////////////////////// + +enum { + MIDIOUT_START=0, + MIDIOUT_STOP, + MIDIOUT_TICK, + MIDIOUT_NOTEON, + MIDIOUT_NOTEOFF, + MIDIOUT_VOLUME, + MIDIOUT_PAN, + MIDIOUT_BANKSEL, + MIDIOUT_PROGRAM, +}; + + +typedef struct MODMIDICFG +{ + char szMidiGlb[9*32]; // changed from CHAR + char szMidiSFXExt[16*32]; // changed from CHAR + char szMidiZXXExt[128*32]; // changed from CHAR +} MODMIDICFG, *LPMODMIDICFG; + +#define NOTE_MAX 120 //Defines maximum notevalue as well as maximum number of notes. + +typedef VOID (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels + + + +//============== +class MODPLUG_EXPORT CSoundFile +//============== +{ +public: // Static Members + static UINT m_nXBassDepth; + static UINT m_nXBassRange; + static UINT m_nReverbDepth; + static UINT m_nReverbDelay; + static UINT gnReverbType; + static UINT m_nProLogicDepth; + static UINT m_nProLogicDelay; + static UINT m_nStereoSeparation; + static UINT m_nMaxMixChannels; + static LONG m_nStreamVolume; + static DWORD gdwSysInfo; + static DWORD gdwSoundSetup; + static DWORD gdwMixingFreq; + static DWORD gnBitsPerSample; + static DWORD gnChannels; + static UINT gnAGC; + static UINT gnVolumeRampSamples; + static UINT gnVUMeter; + static UINT gnCPUUsage; + static LPSNDMIXHOOKPROC gpSndMixHook; + static PMIXPLUGINCREATEPROC gpMixPluginCreateProc; + +public: // for Editing + MODCHANNEL Chn[MAX_CHANNELS]; // Channels + UINT ChnMix[MAX_CHANNELS]; // Channels to be mixed + MODINSTRUMENT Ins[MAX_SAMPLES]; // Instruments + INSTRUMENTHEADER *Headers[MAX_INSTRUMENTS]; // Instrument Headers + MODCHANNELSETTINGS ChnSettings[MAX_BASECHANNELS]; // Channels settings + MODCOMMAND *Patterns[MAX_PATTERNS]; // Patterns + WORD PatternSize[MAX_PATTERNS]; // Patterns Lengths + BYTE Order[MAX_ORDERS]; // Pattern Orders + MODMIDICFG m_MidiCfg; // Midi macro config table + SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins + UINT m_nDefaultSpeed, m_nDefaultTempo, m_nDefaultGlobalVolume; + DWORD m_dwSongFlags; // Song flags SONG_XXXX + UINT m_nChannels, m_nMixChannels, m_nMixStat, m_nBufferCount; + UINT m_nType, m_nSamples, m_nInstruments; + UINT m_nTickCount, m_nTotalCount, m_nPatternDelay, m_nFrameDelay; + UINT m_nMusicSpeed, m_nMusicTempo; + UINT m_nNextRow, m_nRow, m_nNextStartRow; + UINT m_nPattern,m_nCurrentPattern,m_nNextPattern,m_nRestartPos; + UINT m_nMasterVolume, m_nGlobalVolume, m_nSongPreAmp; + UINT m_nFreqFactor, m_nTempoFactor, m_nOldGlbVolSlide; + LONG m_nMinPeriod, m_nMaxPeriod, m_nRepeatCount, m_nInitialRepeatCount; + DWORD m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples; + UINT m_nMaxOrderPosition; + UINT m_nPatternNames; + LPSTR m_lpszSongComments, m_lpszPatternNames; + char m_szNames[MAX_INSTRUMENTS][32]; // changed from CHAR + CHAR CompressionTable[16]; + +public: + CSoundFile(); + ~CSoundFile(); + +public: + BOOL Create(LPCBYTE lpStream, DWORD dwMemLength=0); + BOOL Destroy(); + UINT GetType() const { return m_nType; } + UINT GetNumChannels() const; + UINT GetLogicalChannels() const { return m_nChannels; } + BOOL SetMasterVolume(UINT vol, BOOL bAdjustAGC=FALSE); + UINT GetMasterVolume() const { return m_nMasterVolume; } + UINT GetNumPatterns() const; + UINT GetNumInstruments() const; + UINT GetNumSamples() const { return m_nSamples; } + UINT GetCurrentPos() const; + UINT GetCurrentPattern() const { return m_nPattern; } + UINT GetCurrentOrder() const { return m_nCurrentPattern; } + UINT GetSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetRawSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetMaxPosition() const; + void SetCurrentPos(UINT nPos); + void SetCurrentOrder(UINT nOrder); + void GetTitle(LPSTR s) const { lstrcpyn(s,m_szNames[0],32); } + LPCSTR GetTitle() const { return m_szNames[0]; } + UINT GetSampleName(UINT nSample,LPSTR s=NULL) const; + UINT GetInstrumentName(UINT nInstr,LPSTR s=NULL) const; + UINT GetMusicSpeed() const { return m_nMusicSpeed; } + UINT GetMusicTempo() const { return m_nMusicTempo; } + DWORD GetLength(BOOL bAdjust, BOOL bTotal=FALSE); + DWORD GetSongTime() { return GetLength(FALSE, TRUE); } + void SetRepeatCount(int n) { m_nRepeatCount = n; m_nInitialRepeatCount = n; } + int GetRepeatCount() const { return m_nRepeatCount; } + BOOL IsPaused() const { return (m_dwSongFlags & SONG_PAUSED) ? TRUE : FALSE; } + void LoopPattern(int nPat, int nRow=0); + void CheckCPUUsage(UINT nCPU); + BOOL SetPatternName(UINT nPat, LPCSTR lpszName); + BOOL GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize=MAX_PATTERNNAME) const; + // Module Loaders + BOOL ReadXM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadS3M(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMod(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMed(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadSTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadIT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL Read669(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUlt(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadWav(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadFAR(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMDL(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadOKT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDBM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMT2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUMX(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPAT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestPAT(LPCBYTE lpStream, DWORD dwMemLength); + // Save Functions +#ifndef MODPLUG_NO_FILESAVE + UINT WriteSample(FILE *f, MODINSTRUMENT *pins, UINT nFlags, UINT nMaxLen=0); + BOOL SaveXM(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveS3M(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveMod(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveIT(LPCSTR lpszFileName, UINT nPacking=0); +#endif // MODPLUG_NO_FILESAVE + // MOD Convert function + UINT GetBestSaveFormat() const; + UINT GetSaveFormats() const; + void ConvertModCommand(MODCOMMAND *) const; + void S3MConvert(MODCOMMAND *m, BOOL bIT) const; + void S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const; + WORD ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const; + +public: + // Real-time sound functions + VOID ResetChannels(); + + UINT Read(LPVOID lpBuffer, UINT cbBuffer); + UINT CreateStereoMix(int count); + BOOL FadeSong(UINT msec); + BOOL GlobalFadeSong(UINT msec); + UINT GetTotalTickCount() const { return m_nTotalCount; } + VOID ResetTotalTickCount() { m_nTotalCount = 0; } + +public: + // Mixer Config + static BOOL InitPlayer(BOOL bReset=FALSE); + static BOOL SetMixConfig(UINT nStereoSeparation, UINT nMaxMixChannels); + static BOOL SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX=FALSE); + static BOOL SetResamplingMode(UINT nMode); // SRCMODE_XXXX + static BOOL IsStereo() { return (gnChannels > 1) ? TRUE : FALSE; } + static DWORD GetSampleRate() { return gdwMixingFreq; } + static DWORD GetBitsPerSample() { return gnBitsPerSample; } + static DWORD InitSysInfo(); + static DWORD GetSysInfo() { return gdwSysInfo; } + // AGC + static BOOL GetAGC() { return (gdwSoundSetup & SNDMIX_AGC) ? TRUE : FALSE; } + static void SetAGC(BOOL b); + static void ResetAGC(); + static void ProcessAGC(int count); + + //GCCFIX -- added these functions back in! + static BOOL SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ); + // DSP Effects + static void InitializeDSP(BOOL bReset); + static void ProcessStereoDSP(int count); + static void ProcessMonoDSP(int count); + // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] + static BOOL SetReverbParameters(UINT nDepth, UINT nDelay); + // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] + static BOOL SetXBassParameters(UINT nDepth, UINT nRange); + // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] + static BOOL SetSurroundParameters(UINT nDepth, UINT nDelay); +public: + BOOL ReadNote(); + BOOL ProcessRow(); + BOOL ProcessEffects(); + UINT GetNNAChannel(UINT nChn) const; + void CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut); + void NoteChange(UINT nChn, int note, BOOL bPorta=FALSE, BOOL bResetEnv=TRUE); + void InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta=FALSE,BOOL bUpdVol=TRUE,BOOL bResetEnv=TRUE); + // Channel Effects + void PortamentoUp(MODCHANNEL *pChn, UINT param); + void PortamentoDown(MODCHANNEL *pChn, UINT param); + void FinePortamentoUp(MODCHANNEL *pChn, UINT param); + void FinePortamentoDown(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param); + void TonePortamento(MODCHANNEL *pChn, UINT param); + void Vibrato(MODCHANNEL *pChn, UINT param); + void FineVibrato(MODCHANNEL *pChn, UINT param); + void VolumeSlide(MODCHANNEL *pChn, UINT param); + void PanningSlide(MODCHANNEL *pChn, UINT param); + void ChannelVolSlide(MODCHANNEL *pChn, UINT param); + void FineVolumeUp(MODCHANNEL *pChn, UINT param); + void FineVolumeDown(MODCHANNEL *pChn, UINT param); + void Tremolo(MODCHANNEL *pChn, UINT param); + void Panbrello(MODCHANNEL *pChn, UINT param); + void RetrigNote(UINT nChn, UINT param); + void NoteCut(UINT nChn, UINT nTick); + void KeyOff(UINT nChn); + int PatternLoop(MODCHANNEL *, UINT param); + void ExtendedMODCommands(UINT nChn, UINT param); + void ExtendedS3MCommands(UINT nChn, UINT param); + void ExtendedChannelEffect(MODCHANNEL *, UINT param); + void ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param=0); + void SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier=256) const; + // Low-Level effect processing + void DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide); + // Global Effects + void SetTempo(UINT param); + void SetSpeed(UINT param); + void GlobalVolSlide(UINT param); + DWORD IsSongFinished(UINT nOrder, UINT nRow) const; + BOOL IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const; + // Read/Write sample functions + signed char GetDeltaValue(signed char prev, UINT n) const { return (signed char)(prev + CompressionTable[n & 0x0F]); } + UINT PackSample(int &sample, int next); + BOOL CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result=NULL); + UINT ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength); + BOOL DestroySample(UINT nSample); + BOOL DestroyInstrument(UINT nInstr); + BOOL IsSampleUsed(UINT nSample); + BOOL IsInstrumentUsed(UINT nInstr); + BOOL RemoveInstrumentSamples(UINT nInstr); + UINT DetectUnusedSamples(BOOL *); + BOOL RemoveSelectedSamples(BOOL *); + void AdjustSampleLoop(MODINSTRUMENT *pIns); + // I/O from another sound file + BOOL ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument); + BOOL ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample); + // Period/Note functions + UINT GetNoteFromPeriod(UINT period) const; + UINT GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const; + UINT GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac=0) const; + // Misc functions + MODINSTRUMENT *GetSample(UINT n) { return Ins+n; } + void ResetMidiCfg(); + UINT MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote); + BOOL ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers); + UINT SaveMixPlugins(FILE *f=NULL, BOOL bUpdate=TRUE); + UINT LoadMixPlugins(const void *pData, UINT nLen); +#ifndef NO_FILTER + DWORD CutOffToFrequency(UINT nCutOff, int flt_modifier=256) const; // [0-255] => [1-10KHz] +#endif + + // Static helper functions +public: + static DWORD TransposeToFrequency(int transp, int ftune=0); + static int FrequencyToTranspose(DWORD freq); + static void FrequencyToTranspose(MODINSTRUMENT *psmp); + + // System-Dependant functions +public: + static MODCOMMAND *AllocatePattern(UINT rows, UINT nchns); + static signed char* AllocateSample(UINT nbytes); + static void FreePattern(LPVOID pat); + static void FreeSample(LPVOID p); + static UINT Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc); +}; + + +// inline DWORD BigEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); } +// inline WORD BigEndianW(WORD x) { return (WORD)(((x >> 8) & 0xFF) | ((x << 8) & 0xFF00)); } + + +////////////////////////////////////////////////////////// +// WAVE format information + +#pragma pack(1) + +// Standard IFF chunks IDs +#define IFFID_FORM 0x4d524f46 +#define IFFID_RIFF 0x46464952 +#define IFFID_WAVE 0x45564157 +#define IFFID_LIST 0x5453494C +#define IFFID_INFO 0x4F464E49 + +// IFF Info fields +#define IFFID_ICOP 0x504F4349 +#define IFFID_IART 0x54524149 +#define IFFID_IPRD 0x44525049 +#define IFFID_INAM 0x4D414E49 +#define IFFID_ICMT 0x544D4349 +#define IFFID_IENG 0x474E4549 +#define IFFID_ISFT 0x54465349 +#define IFFID_ISBJ 0x4A425349 +#define IFFID_IGNR 0x524E4749 +#define IFFID_ICRD 0x44524349 + +// Wave IFF chunks IDs +#define IFFID_wave 0x65766177 +#define IFFID_fmt 0x20746D66 +#define IFFID_wsmp 0x706D7377 +#define IFFID_pcm 0x206d6370 +#define IFFID_data 0x61746164 +#define IFFID_smpl 0x6C706D73 +#define IFFID_xtra 0x61727478 + +typedef struct WAVEFILEHEADER +{ + DWORD id_RIFF; // "RIFF" + DWORD filesize; // file length-8 + DWORD id_WAVE; +} WAVEFILEHEADER; + + +typedef struct WAVEFORMATHEADER +{ + DWORD id_fmt; // "fmt " + DWORD hdrlen; // 16 + WORD format; // 1 + WORD channels; // 1:mono, 2:stereo + DWORD freqHz; // sampling freq + DWORD bytessec; // bytes/sec=freqHz*samplesize + WORD samplesize; // sizeof(sample) + WORD bitspersample; // bits per sample (8/16) +} WAVEFORMATHEADER; + + +typedef struct WAVEDATAHEADER +{ + DWORD id_data; // "data" + DWORD length; // length of data +} WAVEDATAHEADER; + + +typedef struct WAVESMPLHEADER +{ + // SMPL + DWORD smpl_id; // "smpl" -> 0x6C706D73 + DWORD smpl_len; // length of smpl: 3Ch (54h with sustain loop) + DWORD dwManufacturer; + DWORD dwProduct; + DWORD dwSamplePeriod; // 1000000000/freqHz + DWORD dwBaseNote; // 3Ch = C-4 -> 60 + RelativeTone + DWORD dwPitchFraction; + DWORD dwSMPTEFormat; + DWORD dwSMPTEOffset; + DWORD dwSampleLoops; // number of loops + DWORD cbSamplerData; +} WAVESMPLHEADER; + + +typedef struct SAMPLELOOPSTRUCT +{ + DWORD dwIdentifier; + DWORD dwLoopType; // 0=normal, 1=bidi + DWORD dwLoopStart; + DWORD dwLoopEnd; // Byte offset ? + DWORD dwFraction; + DWORD dwPlayCount; // Loop Count, 0=infinite +} SAMPLELOOPSTRUCT; + + +typedef struct WAVESAMPLERINFO +{ + WAVESMPLHEADER wsiHdr; + SAMPLELOOPSTRUCT wsiLoops[2]; +} WAVESAMPLERINFO; + + +typedef struct WAVELISTHEADER +{ + DWORD list_id; // "LIST" -> 0x5453494C + DWORD list_len; + DWORD info; // "INFO" +} WAVELISTHEADER; + + +typedef struct WAVEEXTRAHEADER +{ + DWORD xtra_id; // "xtra" -> 0x61727478 + DWORD xtra_len; + DWORD dwFlags; + WORD wPan; + WORD wVolume; + WORD wGlobalVol; + WORD wReserved; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; +} WAVEEXTRAHEADER; + +#pragma pack() + +/////////////////////////////////////////////////////////// +// Low-level Mixing functions + +#define MIXBUFFERSIZE 512 +#define MIXING_ATTENUATION 4 +#define MIXING_CLIPMIN (-0x08000000) +#define MIXING_CLIPMAX (0x07FFFFFF) +#define VOLUMERAMPPRECISION 12 +#define FADESONGDELAY 100 +#define EQ_BUFFERSIZE (MIXBUFFERSIZE) +#define AGC_PRECISION 9 +#define AGC_UNITY (1 << AGC_PRECISION) + +// Calling conventions +#ifdef MSC_VER +#define MPPASMCALL __cdecl +#define MPPFASTCALL __fastcall +#else +#define MPPASMCALL +#define MPPFASTCALL +#endif + +#define MOD2XMFineTune(k) ((int)( (signed char)((k)<<4) )) +#define XM2MODFineTune(k) ((int)( (k>>4)&0x0f )) + +int _muldiv(long a, long b, long c); +int _muldivr(long a, long b, long c); + + +// Byte swapping functions from the GNU C Library and libsdl + +/* Swap bytes in 16 bit value. */ +#ifdef __GNUC__ +# define bswap_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) +#else +static __inline unsigned short int +bswap_16 (unsigned short int __bsx) +{ + return ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); +} +#endif + +/* Swap bytes in 32 bit value. */ +#ifdef __GNUC__ +# define bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) +#else +static __inline unsigned int +bswap_32 (unsigned int __bsx) +{ + return ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); +} +#endif + +#if (defined ARM) && (defined _WIN32_WCE) +static __inline unsigned short int +ARM_get16(const void *data) +{ + unsigned short int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +static __inline unsigned int +ARM_get32(const void *data) +{ + unsigned int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +#define bswapLE16(X) ARM_get16(&X) +#define bswapLE32(X) ARM_get32(&X) +#define bswapBE16(X) bswap_16(ARM_get16(&X)) +#define bswapBE32(X) bswap_32(ARM_get32(&X)) + +// From libsdl +#elif defined(WORDS_BIGENDIAN) && WORDS_BIGENDIAN +#define bswapLE16(X) bswap_16(X) +#define bswapLE32(X) bswap_32(X) +#define bswapBE16(X) (X) +#define bswapBE32(X) (X) +#else +#define bswapLE16(X) (X) +#define bswapLE32(X) (X) +#define bswapBE16(X) bswap_16(X) +#define bswapBE32(X) bswap_32(X) +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h new file mode 100644 index 000000000..5daea9182 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h @@ -0,0 +1,141 @@ +/* + * This source code is public domain. + * + * Authors: Rani Assaf , + * Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) + */ + +#ifndef MODPLUG_STDAFX_H +#define MODPLUG_STDAFX_H + +/* Autoconf detection of stdint/inttypes */ +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +# include "config.h" +# define CONFIG_H_INCLUDED 1 +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif + +/* disable AGC and FILESAVE for all targets for uniformity. */ +#define NO_AGC +#define MODPLUG_NO_FILESAVE + +#ifdef _WIN32 + +#ifdef MSC_VER +#pragma warning (disable:4201) +#pragma warning (disable:4514) +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#define srandom(_seed) srand(_seed) +#define random() rand() +#define sleep(_ms) Sleep(_ms) + +inline void ProcessPlugins(int n) {} + +#undef strcasecmp +#undef strncasecmp +#define strcasecmp(a,b) _stricmp(a,b) +#define strncasecmp(a,b,c) _strnicmp(a,b,c) + +#define HAVE_SINF 1 + +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + +#else + +#include +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif + +typedef int8_t CHAR; +typedef uint8_t UCHAR; +typedef uint8_t* PUCHAR; +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint32_t UINT; +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef int64_t LONGLONG; +typedef int32_t* LPLONG; +typedef uint32_t* LPDWORD; +typedef uint16_t WORD; +typedef uint8_t BYTE; +typedef uint8_t* LPBYTE; +typedef bool BOOL; +typedef char* LPSTR; +typedef void* LPVOID; +typedef uint16_t* LPWORD; +typedef const char* LPCSTR; +typedef void* PVOID; +typedef void VOID; + +inline LONG MulDiv (long a, long b, long c) +{ +/*if (!c) return 0;*/ + return ((uint64_t) a * (uint64_t) b ) / c; +} + +#define LPCTSTR LPCSTR +#define lstrcpyn strncpy +#define lstrcpy strcpy +#define lstrcmp strcmp +#define wsprintf sprintf + +#define WAVE_FORMAT_PCM 1 + +#define GHND 0 +#define GlobalFreePtr(p) free((void *)(p)) +inline int8_t * GlobalAllocPtr(unsigned int, size_t size) +{ + int8_t * p = (int8_t *) malloc(size); + + if (p != NULL) memset(p, 0, size); + return p; +} + +inline void ProcessPlugins(int /* n */ ) {} + +#ifndef FALSE +#define FALSE false +#endif + +#ifndef TRUE +#define TRUE true +#endif + +#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 +#elif defined(MODPLUG_BUILD) && defined(SYM_VISIBILITY) +# define MODPLUG_EXPORT __attribute__((visibility("default"))) +#else +#define MODPLUG_EXPORT +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c new file mode 100644 index 000000000..51dd3d79f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c @@ -0,0 +1,604 @@ +/* + * libopenmpt_modplug.c + * -------------------- + * Purpose: libopenmpt emulation of the libmodplug interface + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +#ifdef LIBOPENMPT_BUILD_DLL +#undef LIBOPENMPT_BUILD_DLL +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#include + +#include +#include +#include +#include +#include +#include + +#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 +#define LIBOPENMPT_MODPLUG_API +#else /* !_MSC_VER */ +#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT +#endif /* _MSC_VER */ +#include "libmodplug/modplug.h" + +/* from libmodplug/sndfile.h */ +/* header is not c clean */ +#define MIXING_ATTENUATION 4 +#define MOD_TYPE_NONE 0x0 +#define MOD_TYPE_MOD 0x1 +#define MOD_TYPE_S3M 0x2 +#define MOD_TYPE_XM 0x4 +#define MOD_TYPE_MED 0x8 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type + +#define BUFFER_COUNT 1024 + +struct _ModPlugFile { + openmpt_module* mod; + signed short* buf; + signed int* mixerbuf; + char* name; + char* message; + ModPlug_Settings settings; + ModPlugMixerProc mixerproc; + ModPlugNote** patterns; +}; + +static ModPlug_Settings globalsettings = { + MODPLUG_ENABLE_OVERSAMPLING|MODPLUG_ENABLE_NOISE_REDUCTION, + 2, + 16, + 44100, + MODPLUG_RESAMPLE_LINEAR, + 128, + 256, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +static int32_t modplugresamplingmode_to_filterlength(int mode) +{ + if(mode<0){ + return 1; + } + switch(mode){ + case MODPLUG_RESAMPLE_NEAREST: return 1; break; + case MODPLUG_RESAMPLE_LINEAR: return 2; break; + case MODPLUG_RESAMPLE_SPLINE: return 4; break; + case MODPLUG_RESAMPLE_FIR: return 8; break; + } + return 8; +} + +LIBOPENMPT_MODPLUG_API ModPlugFile* ModPlug_Load(const void* data, int size) +{ + ModPlugFile* file = malloc(sizeof(ModPlugFile)); + const char* name = NULL; + const char* message = NULL; + if(!file) return NULL; + memset(file,0,sizeof(ModPlugFile)); + memcpy(&file->settings,&globalsettings,sizeof(ModPlug_Settings)); + file->mod = openmpt_module_create_from_memory2(data,size,NULL,NULL,NULL,NULL,NULL,NULL,NULL); + if(!file->mod){ + free(file); + return NULL; + } + file->buf = malloc(BUFFER_COUNT*sizeof(signed short)*4); + if(!file->buf){ + openmpt_module_destroy(file->mod); + free(file); + return NULL; + } + openmpt_module_set_repeat_count(file->mod,file->settings.mLoopCount); + name = openmpt_module_get_metadata(file->mod,"title"); + if(name){ + file->name = malloc(strlen(name)+1); + if(file->name){ + strcpy(file->name,name); + } + openmpt_free_string(name); + name = NULL; + }else{ + file->name = malloc(strlen("")+1); + if(file->name){ + strcpy(file->name,""); + } + } + message = openmpt_module_get_metadata(file->mod,"message"); + if(message){ + file->message = malloc(strlen(message)+1); + if(file->message){ + strcpy(file->message,message); + } + openmpt_free_string(message); + message = NULL; + }else{ + file->message = malloc(strlen("")+1); + if(file->message){ + strcpy(file->message,""); + } + } + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,file->settings.mStereoSeparation*100/128); + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,modplugresamplingmode_to_filterlength(file->settings.mResamplingMode)); + return file; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Unload(ModPlugFile* file) +{ + int p; + if(!file) return; + if(file->patterns){ + for(p=0;pmod);p++){ + if(file->patterns[p]){ + free(file->patterns[p]); + file->patterns[p] = NULL; + } + } + free(file->patterns); + file->patterns = NULL; + } + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } + openmpt_module_destroy(file->mod); + file->mod = NULL; + free(file->name); + file->name = NULL; + free(file->message); + file->message = NULL; + free(file->buf); + file->buf = NULL; + free(file); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_Read(ModPlugFile* file, void* buffer, int size) +{ + int framesize; + int framecount; + int frames; + int rendered; + int frame; + int channel; + int totalrendered; + signed short* in; + signed int* mixbuf; + unsigned char* buf8; + signed short* buf16; + signed int* buf32; + if(!file) return 0; + framesize = file->settings.mBits/8*file->settings.mChannels; + framecount = size/framesize; + buf8 = buffer; + buf16 = buffer; + buf32 = buffer; + totalrendered = 0; + while(framecount>0){ + frames = framecount; + if(frames>BUFFER_COUNT){ + frames = BUFFER_COUNT; + } + if(file->settings.mChannels==1){ + rendered = (int)openmpt_module_read_mono(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0]); + }else if(file->settings.mChannels==2){ + rendered = (int)openmpt_module_read_stereo(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1]); + }else if(file->settings.mChannels==4){ + rendered = (int)openmpt_module_read_quad(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1],&file->buf[frames*2],&file->buf[frames*3]); + }else{ + return 0; + } + in = file->buf; + if(file->mixerproc&&file->mixerbuf){ + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + *mixbuf = in[frames*channel+frame]<<(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + file->mixerproc(file->mixerbuf,file->settings.mChannels*frames,file->settings.mChannels); + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + in[frames*channel+frame] = *mixbuf>>(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + } + if(file->settings.mBits==8){ + for(frame=0;framesettings.mChannels;channel++){ + *buf8 = in[frames*channel+frame]/256+0x80; + buf8++; + } + } + }else if(file->settings.mBits==16){ + for(frame=0;framesettings.mChannels;channel++){ + *buf16 = in[frames*channel+frame]; + buf16++; + } + } + }else if(file->settings.mBits==32){ + for(frame=0;framesettings.mChannels;channel++){ + *buf32 = in[frames*channel+frame] << (32-16-1-MIXING_ATTENUATION); + buf32++; + } + } + }else{ + return 0; + } + totalrendered += rendered; + framecount -= frames; + if(!rendered) break; + } + memset(((char*)buffer)+totalrendered*framesize,0,size-totalrendered*framesize); + return totalrendered*framesize; +} + +LIBOPENMPT_MODPLUG_API const char* ModPlug_GetName(ModPlugFile* file) +{ + if(!file) return NULL; + return file->name; +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetLength(ModPlugFile* file) +{ + if(!file) return 0; + return (int)(openmpt_module_get_duration_seconds(file->mod)*1000.0); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Seek(ModPlugFile* file, int millisecond) +{ + if(!file) return; + openmpt_module_set_position_seconds(file->mod,(double)millisecond*0.001); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_GetSettings(ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(settings,&globalsettings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetSettings(const ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(&globalsettings,settings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) +{ + int32_t val; + if(!file) return 0; + val = 0; + if(!openmpt_module_get_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,&val)) return 128; + return (unsigned int)(128.0*pow(10.0,val*0.0005)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) +{ + if(!file) return; + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,(int32_t)(2000.0*log10(cvol/128.0))); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentSpeed(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_speed(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentTempo(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_tempo(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentOrder(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_order(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentPattern(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_pattern(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentRow(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_row(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetPlayingChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_playing_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SeekOrder(ModPlugFile* file,int order) +{ + if(!file) return; + openmpt_module_set_position_order_row(file->mod,order,0); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetModuleType(ModPlugFile* file) +{ + const char* type; + int retval; + if(!file) return 0; + type = openmpt_module_get_metadata(file->mod,"type"); + retval = MOD_TYPE_NONE; + if(!type){ + return retval; + } + if(!strcmp(type,"mod")){ + retval = MOD_TYPE_MOD; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_S3M; + }else if(!strcmp(type,"xm")){ + retval = MOD_TYPE_XM; + }else if(!strcmp(type,"med")){ + retval = MOD_TYPE_MED; + }else if(!strcmp(type,"mtm")){ + retval = MOD_TYPE_MTM; + }else if(!strcmp(type,"it")){ + retval = MOD_TYPE_IT; + }else if(!strcmp(type,"669")){ + retval = MOD_TYPE_669; + }else if(!strcmp(type,"ult")){ + retval = MOD_TYPE_ULT; + }else if(!strcmp(type,"stm")){ + retval = MOD_TYPE_STM; + }else if(!strcmp(type,"far")){ + retval = MOD_TYPE_FAR; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_WAV; + }else if(!strcmp(type,"amf")){ + retval = MOD_TYPE_AMF; + }else if(!strcmp(type,"ams")){ + retval = MOD_TYPE_AMS; + }else if(!strcmp(type,"dsm")){ + retval = MOD_TYPE_DSM; + }else if(!strcmp(type,"mdl")){ + retval = MOD_TYPE_MDL; + }else if(!strcmp(type,"okt")){ + retval = MOD_TYPE_OKT; + }else if(!strcmp(type,"mid")){ + retval = MOD_TYPE_MID; + }else if(!strcmp(type,"dmf")){ + retval = MOD_TYPE_DMF; + }else if(!strcmp(type,"ptm")){ + retval = MOD_TYPE_PTM; + }else if(!strcmp(type,"dbm")){ + retval = MOD_TYPE_DBM; + }else if(!strcmp(type,"mt2")){ + retval = MOD_TYPE_MT2; + }else if(!strcmp(type,"amf0")){ + retval = MOD_TYPE_AMF0; + }else if(!strcmp(type,"psm")){ + retval = MOD_TYPE_PSM; + }else if(!strcmp(type,"j2b")){ + retval = MOD_TYPE_J2B; + }else if(!strcmp(type,"abc")){ + retval = MOD_TYPE_ABC; + }else if(!strcmp(type,"pat")){ + retval = MOD_TYPE_PAT; + }else if(!strcmp(type,"umx")){ + retval = MOD_TYPE_UMX; + }else{ + retval = MOD_TYPE_IT; /* fallback, most complex type */ + } + openmpt_free_string(type); + return retval; +} + +LIBOPENMPT_MODPLUG_API char* ModPlug_GetMessage(ModPlugFile* file) +{ + if(!file) return NULL; + return file->message; +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumInstruments(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_instruments(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumSamples(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_samples(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumPatterns(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_patterns(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_sample_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_instrument_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows) +{ + int c; + int r; + int numr; + int numc; + ModPlugNote note; + if(!file) return NULL; + if(numrows){ + *numrows = openmpt_module_get_pattern_num_rows(file->mod,pattern); + } + if(pattern<0||pattern>=openmpt_module_get_num_patterns(file->mod)){ + return NULL; + } + if(!file->patterns){ + file->patterns = malloc(sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + if(!file->patterns) return NULL; + memset(file->patterns,0,sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + } + if(!file->patterns[pattern]){ + file->patterns[pattern] = malloc(sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + if(!file->patterns[pattern]) return NULL; + memset(file->patterns[pattern],0,sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + } + numr = openmpt_module_get_pattern_num_rows(file->mod,pattern); + numc = openmpt_module_get_num_channels(file->mod); + for(r=0;rmod,pattern,r,c,OPENMPT_MODULE_COMMAND_NOTE); + note.Instrument = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_INSTRUMENT); + note.VolumeEffect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUMEEFFECT); + note.Effect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_EFFECT); + note.Volume = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUME); + note.Parameter = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_PARAMETER); + memcpy(&file->patterns[pattern][r*numc+c],¬e,sizeof(ModPlugNote)); + } + } + return file->patterns[pattern]; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) +{ + if(!file) return; + if(!file->mixerbuf){ + file->mixerbuf = malloc(BUFFER_COUNT*sizeof(signed int)*4); + } + file->mixerproc = proc; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_UnloadMixerCallback(ModPlugFile* file) +{ + if(!file) return; + file->mixerproc = NULL; + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportS3M(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportXM(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportXM(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportMOD(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportIT(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportIT(%s) not implemented.\n",filepath); + return 0; +} + +#endif /* NO_LIBMODPLUG */ diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in new file mode 100644 index 000000000..89c1fee89 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libopenmpt_modplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lopenmpt_modplug +Libs.private: +Cflags: -I${includedir} diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp new file mode 100644 index 000000000..fd5375d3b --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp @@ -0,0 +1,887 @@ +/* + * libopenmpt_modplug_cpp.cpp + * -------------------------- + * Purpose: libopenmpt emulation of the libmodplug c++ interface + * Notes : WARNING! THIS IS A HACK! + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +/* + +*********************************************************************** +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +*********************************************************************** + +This is a dirty hack to emulate just so much of the libmodplug c++ +interface so that the current known users (mainly xmms-modplug itself, +gstreamer modplug, audacious, and stuff based on those) work. This is +neither a complete nor a correct implementation. +Metadata and other state is not provided or updated. + +*/ + +#ifdef UNICODE +#undef UNICODE +#endif +#ifdef _UNICODE +#undef _UNICODE +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#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 +#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) ); +} +} + +//#define mpcpplog() fprintf(stderr, "%s %i\n", __func__, __LINE__) +#define mpcpplog() do{}while(0) + +#define UNUSED(x) (void)((x)) + +union self_t { + CHAR CompressionTable[16]; + openmpt::module * self_; +}; + +static void set_self( CSoundFile * that, openmpt::module * self_ ) { + self_t self_union; + Clear(self_union); + self_union.self_ = self_; + std::memcpy( that->CompressionTable, self_union.CompressionTable, sizeof( self_union.CompressionTable ) ); +} + +static openmpt::module * get_self( const CSoundFile * that ) { + self_t self_union; + Clear(self_union); + std::memcpy( self_union.CompressionTable, that->CompressionTable, sizeof( self_union.CompressionTable ) ); + return self_union.self_; +} + +#define mod ( get_self( this ) ) + +#define update_state() \ + if ( mod ) m_nCurrentPattern = mod->get_current_order(); \ + if ( mod ) m_nPattern = mod->get_current_pattern(); \ + if ( mod ) m_nMusicSpeed = mod->get_current_speed(); \ + if ( mod ) m_nMusicTempo = mod->get_current_tempo(); \ +/**/ + +UINT CSoundFile::m_nXBassDepth = 0; +UINT CSoundFile::m_nXBassRange = 0; +UINT CSoundFile::m_nReverbDepth = 0; +UINT CSoundFile::m_nReverbDelay = 0; +UINT CSoundFile::gnReverbType = 0; +UINT CSoundFile::m_nProLogicDepth = 0; +UINT CSoundFile::m_nProLogicDelay = 0; +UINT CSoundFile::m_nStereoSeparation = 128; +UINT CSoundFile::m_nMaxMixChannels = 256; +LONG CSoundFile::m_nStreamVolume = 0x8000; +DWORD CSoundFile::gdwSysInfo = 0; +DWORD CSoundFile::gdwSoundSetup = 0; +DWORD CSoundFile::gdwMixingFreq = 44100; +DWORD CSoundFile::gnBitsPerSample = 16; +DWORD CSoundFile::gnChannels = 2; +UINT CSoundFile::gnAGC = 0; +UINT CSoundFile::gnVolumeRampSamples = 0; +UINT CSoundFile::gnVUMeter = 0; +UINT CSoundFile::gnCPUUsage = 0; +LPSNDMIXHOOKPROC CSoundFile::gpSndMixHook = 0; +PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = 0; + +CSoundFile::CSoundFile() { + mpcpplog(); + Clear(Chn); + Clear(ChnMix); + Clear(Ins); + Clear(Headers); + Clear(ChnSettings); + Clear(Patterns); + Clear(PatternSize); + Clear(Order); + Clear(m_MidiCfg); + Clear(m_MixPlugins); + Clear(m_nDefaultSpeed); + Clear(m_nDefaultTempo); + Clear(m_nDefaultGlobalVolume); + Clear(m_dwSongFlags); + Clear(m_nChannels); + Clear(m_nMixChannels); + Clear(m_nMixStat); + Clear(m_nBufferCount); + Clear(m_nType); + Clear(m_nSamples); + Clear(m_nInstruments); + Clear(m_nTickCount); + Clear(m_nTotalCount); + Clear(m_nPatternDelay); + Clear(m_nFrameDelay); + Clear(m_nMusicSpeed); + Clear(m_nMusicTempo); + Clear(m_nNextRow); + Clear(m_nRow); + Clear(m_nPattern); + Clear(m_nCurrentPattern); + Clear(m_nNextPattern); + Clear(m_nRestartPos); + Clear(m_nMasterVolume); + Clear(m_nGlobalVolume); + Clear(m_nSongPreAmp); + Clear(m_nFreqFactor); + Clear(m_nTempoFactor); + Clear(m_nOldGlbVolSlide); + Clear(m_nMinPeriod); + Clear(m_nMaxPeriod); + Clear(m_nRepeatCount); + Clear(m_nInitialRepeatCount); + Clear(m_nGlobalFadeSamples); + Clear(m_nGlobalFadeMaxSamples); + Clear(m_nMaxOrderPosition); + Clear(m_nPatternNames); + Clear(m_lpszSongComments); + Clear(m_lpszPatternNames); + Clear(m_szNames); + Clear(CompressionTable); +} + +CSoundFile::~CSoundFile() { + mpcpplog(); + Destroy(); +} + +BOOL CSoundFile::Create( LPCBYTE lpStream, DWORD dwMemLength ) { + mpcpplog(); + try { + openmpt::module * m = new openmpt::module( lpStream, dwMemLength ); + set_self( this, m ); + std::strncpy( m_szNames[0], mod->get_metadata("title").c_str(), sizeof( m_szNames[0] ) - 1 ); + m_szNames[0][ sizeof( m_szNames[0] ) - 1 ] = '\0'; + std::string type = mod->get_metadata("type"); + m_nType = MOD_TYPE_NONE; + if ( type == "mod" ) { + m_nType = MOD_TYPE_MOD; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_S3M; + } else if ( type == "xm" ) { + m_nType = MOD_TYPE_XM; + } else if ( type == "med" ) { + m_nType = MOD_TYPE_MED; + } else if ( type == "mtm" ) { + m_nType = MOD_TYPE_MTM; + } else if ( type == "it" ) { + m_nType = MOD_TYPE_IT; + } else if ( type == "669" ) { + m_nType = MOD_TYPE_669; + } else if ( type == "ult" ) { + m_nType = MOD_TYPE_ULT; + } else if ( type == "stm" ) { + m_nType = MOD_TYPE_STM; + } else if ( type == "far" ) { + m_nType = MOD_TYPE_FAR; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_WAV; + } else if ( type == "amf" ) { + m_nType = MOD_TYPE_AMF; + } else if ( type == "ams" ) { + m_nType = MOD_TYPE_AMS; + } else if ( type == "dsm" ) { + m_nType = MOD_TYPE_DSM; + } else if ( type == "mdl" ) { + m_nType = MOD_TYPE_MDL; + } else if ( type == "okt" ) { + m_nType = MOD_TYPE_OKT; + } else if ( type == "mid" ) { + m_nType = MOD_TYPE_MID; + } else if ( type == "dmf" ) { + m_nType = MOD_TYPE_DMF; + } else if ( type == "ptm" ) { + m_nType = MOD_TYPE_PTM; + } else if ( type == "dbm" ) { + m_nType = MOD_TYPE_DBM; + } else if ( type == "mt2" ) { + m_nType = MOD_TYPE_MT2; + } else if ( type == "amf0" ) { + m_nType = MOD_TYPE_AMF0; + } else if ( type == "psm" ) { + m_nType = MOD_TYPE_PSM; + } else if ( type == "j2b" ) { + m_nType = MOD_TYPE_J2B; + } else if ( type == "abc" ) { + m_nType = MOD_TYPE_ABC; + } else if ( type == "pat" ) { + m_nType = MOD_TYPE_PAT; + } else if ( type == "umx" ) { + m_nType = MOD_TYPE_UMX; + } else { + m_nType = MOD_TYPE_IT; // fallback, most complex type + } + m_nChannels = mod->get_num_channels(); + m_nMasterVolume = 128; + m_nSamples = mod->get_num_samples(); + update_state(); + return TRUE; + } catch ( ... ) { + Destroy(); + return FALSE; + } +} + +BOOL CSoundFile::Destroy() { + mpcpplog(); + if ( mod ) { + delete mod; + set_self( this, 0 ); + } + return TRUE; +} + +UINT CSoundFile::GetNumChannels() const { + mpcpplog(); + return mod->get_num_channels(); +} + +static std::int32_t vol128_To_millibel( unsigned int vol ) { + return static_cast( 2000.0 * std::log10( static_cast( vol ) / 128.0 ) ); +} + +BOOL CSoundFile::SetMasterVolume( UINT vol, BOOL bAdjustAGC ) { + UNUSED(bAdjustAGC); + mpcpplog(); + m_nMasterVolume = vol; + mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, vol128_To_millibel( m_nMasterVolume ) ); + return TRUE; +} + +UINT CSoundFile::GetNumPatterns() const { + mpcpplog(); + return mod->get_num_patterns(); +} + +UINT CSoundFile::GetNumInstruments() const { + mpcpplog(); + return mod->get_num_instruments(); +} + +void CSoundFile::SetCurrentOrder( UINT nOrder ) { + mpcpplog(); + mod->set_position_order_row( nOrder, 0 ); + update_state(); +} + +UINT CSoundFile::GetSampleName( UINT nSample, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_sample_names(); + if ( 1 <= nSample && nSample <= names.size() ) { + std::strncpy( buf, names[ nSample - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +UINT CSoundFile::GetInstrumentName( UINT nInstr, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_instrument_names(); + if ( 1 <= nInstr && nInstr <= names.size() ) { + std::strncpy( buf, names[ nInstr - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +void CSoundFile::LoopPattern( int nPat, int nRow ) { + UNUSED(nPat); + UNUSED(nRow); + mpcpplog(); + // todo +} + +void CSoundFile::CheckCPUUsage( UINT nCPU ) { + UNUSED(nCPU); + mpcpplog(); +} + +BOOL CSoundFile::SetPatternName( UINT nPat, LPCSTR lpszName ) { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName ) { + return FALSE; + } + // todo + return TRUE; +} + +BOOL CSoundFile::GetPatternName( UINT nPat, LPSTR lpszName, UINT cbSize ) const { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName || cbSize <= 0 ) { + return FALSE; + } + std::memset( lpszName, 0, cbSize ); + // todo + return TRUE; +} + +BOOL CSoundFile::ReadXM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadS3M(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMod(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMed(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadSTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadIT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::Read669(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUlt(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadWav(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadFAR(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMDL(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadOKT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDBM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUMX(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } + +#ifndef MODPLUG_NO_FILESAVE + +UINT CSoundFile::WriteSample( FILE * f, MODINSTRUMENT * pins, UINT nFlags, UINT nMaxLen ) { + UNUSED(f); + UNUSED(pins); + UNUSED(nFlags); + UNUSED(nMaxLen); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::SaveXM( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveS3M( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveMod( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveIT( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +#endif + +UINT CSoundFile::GetBestSaveFormat() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +UINT CSoundFile::GetSaveFormats() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +void CSoundFile::ConvertModCommand( MODCOMMAND * ) const { + mpcpplog(); +} + +void CSoundFile::S3MConvert( MODCOMMAND * m, BOOL bIT ) const { + UNUSED(m); + UNUSED(bIT); + mpcpplog(); +} + +void CSoundFile::S3MSaveConvert( UINT * pcmd, UINT * pprm, BOOL bIT ) const { + UNUSED(pcmd); + UNUSED(pprm); + UNUSED(bIT); + mpcpplog(); +} + +WORD CSoundFile::ModSaveCommand( const MODCOMMAND * m, BOOL bXM ) const { + UNUSED(m); + UNUSED(bXM); + mpcpplog(); + return 0; +} + +VOID CSoundFile::ResetChannels() { + mpcpplog(); +} + +UINT CSoundFile::CreateStereoMix( int count ) { + UNUSED(count); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::FadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::GlobalFadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::InitPlayer( BOOL bReset ) { + UNUSED(bReset); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetMixConfig( UINT nStereoSeparation, UINT nMaxMixChannels ) { + UNUSED(nMaxMixChannels); + mpcpplog(); + m_nStereoSeparation = nStereoSeparation; + return TRUE; +} + +DWORD CSoundFile::InitSysInfo() { + mpcpplog(); + return 0; +} + +void CSoundFile::SetAGC( BOOL b ) { + UNUSED(b); + mpcpplog(); +} + +void CSoundFile::ResetAGC() { + mpcpplog(); +} + +void CSoundFile::ProcessAGC( int count ) { + UNUSED(count); + mpcpplog(); +} + +BOOL CSoundFile::SetWaveConfig( UINT nRate, UINT nBits, UINT nChannels, BOOL bMMX ) { + UNUSED(bMMX); + mpcpplog(); + gdwMixingFreq = nRate; + gnBitsPerSample = nBits; + gnChannels = nChannels; + return TRUE; +} + +BOOL CSoundFile::SetWaveConfigEx( BOOL bSurround, BOOL bNoOverSampling, BOOL bReverb, BOOL hqido, BOOL bMegaBass, BOOL bNR, BOOL bEQ ) { + UNUSED(bSurround); + UNUSED(bReverb); + UNUSED(hqido); + UNUSED(bMegaBass); + UNUSED(bEQ); + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + if ( bNoOverSampling ) { + d |= SNDMIX_NORESAMPLING; + } else if ( !hqido ) { + d |= 0; + } else if ( !bNR ) { + d |= SNDMIX_HQRESAMPLER; + } else { + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetResamplingMode( UINT nMode ) { + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + switch ( nMode ) { + case SRCMODE_NEAREST: + d |= SNDMIX_NORESAMPLING; + break; + case SRCMODE_LINEAR: + break; + case SRCMODE_SPLINE: + d |= SNDMIX_HQRESAMPLER; + break; + case SRCMODE_POLYPHASE: + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + break; + default: + return FALSE; + break; + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetReverbParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetXBassParameters( UINT nDepth, UINT nRange ) { + UNUSED(nDepth); + UNUSED(nRange); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetSurroundParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +UINT CSoundFile::GetMaxPosition() const { + mpcpplog(); + // rows in original, just use seconds here + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +DWORD CSoundFile::GetLength( BOOL bAdjust, BOOL bTotal ) { + UNUSED(bAdjust); + UNUSED(bTotal); + mpcpplog(); + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +UINT CSoundFile::GetSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +UINT CSoundFile::GetRawSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message_raw").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +void CSoundFile::SetCurrentPos( UINT nPos ) { + mpcpplog(); + if ( mod ) mod->set_position_seconds( nPos ); + update_state(); +} + +UINT CSoundFile::GetCurrentPos() const { + mpcpplog(); + if ( mod ) return static_cast( mod->get_position_seconds() + 0.5 ); + return 0; +} + +static int get_stereo_separation() { + mpcpplog(); + return CSoundFile::m_nStereoSeparation * 100 / 128; +} + +static int get_filter_length() { + mpcpplog(); + if ( ( CSoundFile::gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) == (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) { + return 8; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_HQRESAMPLER ) == SNDMIX_HQRESAMPLER ) { + return 4; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_NORESAMPLING ) == SNDMIX_NORESAMPLING ) { + return 1; + } else { + return 2; + } +} + +static std::size_t get_sample_size() { + return (CSoundFile::gnBitsPerSample/8); +} + +static std::size_t get_num_channels() { + return CSoundFile::gnChannels; +} + +static std::size_t get_frame_size() { + return get_sample_size() * get_num_channels(); +} + +static int get_samplerate() { + return CSoundFile::gdwMixingFreq; +} + +UINT CSoundFile::Read( LPVOID lpBuffer, UINT cbBuffer ) { + mpcpplog(); + if ( !mod ) { + return 0; + } + mpcpplog(); + if ( !lpBuffer ) { + return 0; + } + mpcpplog(); + if ( cbBuffer <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_samplerate() <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_sample_size() != 1 && get_sample_size() != 2 && get_sample_size() != 4 ) { + return 0; + } + mpcpplog(); + if ( get_num_channels() != 1 && get_num_channels() != 2 && get_num_channels() != 4 ) { + return 0; + } + mpcpplog(); + std::memset( lpBuffer, 0, cbBuffer ); + const std::size_t frames_torender = cbBuffer / get_frame_size(); + short * out = reinterpret_cast( lpBuffer ); + std::vector tmpbuf; + if ( get_sample_size() == 1 || get_sample_size() == 4 ) { + tmpbuf.resize( frames_torender * get_num_channels() ); + out = &tmpbuf[0]; + } + + mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, get_stereo_separation() ); + mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, get_filter_length() ); + std::size_t frames_rendered = 0; + if ( get_num_channels() == 1 ) { + frames_rendered = mod->read( get_samplerate(), frames_torender, out ); + } else if ( get_num_channels() == 4 ) { + frames_rendered = mod->read_interleaved_quad( get_samplerate(), frames_torender, out ); + } else { + frames_rendered = mod->read_interleaved_stereo( get_samplerate(), frames_torender, out ); + } + + if ( get_sample_size() == 1 ) { + unsigned char * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = ( tmpbuf[sample] / 0x100 ) + 0x80; + } + } else if ( get_sample_size() == 4 ) { + int * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = tmpbuf[sample] << (32-16-1-MIXING_ATTENUATION); + } + } + update_state(); + return static_cast( frames_rendered ); +} + + +/* + +gstreamer modplug calls: + +mSoundFile->Create +mSoundFile->Destroy + +mSoundFile->SetWaveConfig +mSoundFile->SetWaveConfigEx +mSoundFile->SetResamplingMode +mSoundFile->SetSurroundParameters +mSoundFile->SetXBassParameters +mSoundFile->SetReverbParameters + +mSoundFile->GetMaxPosition (inline, -> GetLength) +mSoundFile->GetSongTime + +mSoundFile->GetTitle (inline) +mSoundFile->GetSongComments + +mSoundFile->SetCurrentPos +mSoundFile->Read + +mSoundFile->GetCurrentPos +mSoundFile->GetMusicTempo (inline) + +*/ + + +// really very internal symbols, probably nothing calls these directly + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4100) +#endif + +BOOL CSoundFile::ReadNote() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessRow() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessEffects() { mpcpplog(); return 0; } +UINT CSoundFile::GetNNAChannel(UINT nChn) const { mpcpplog(); return 0; } +void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) { mpcpplog(); } +void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta,BOOL bUpdVol,BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::PortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Vibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Tremolo(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Panbrello(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::RetrigNote(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::NoteCut(UINT nChn, UINT nTick) { mpcpplog(); } +void CSoundFile::KeyOff(UINT nChn) { mpcpplog(); } +int CSoundFile::PatternLoop(MODCHANNEL *, UINT param) { mpcpplog(); return 0; } +void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedChannelEffect(MODCHANNEL *, UINT param) { mpcpplog(); } +void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) { mpcpplog(); } +void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const { mpcpplog(); } +void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) { mpcpplog(); } +void CSoundFile::SetTempo(UINT param) { mpcpplog(); } +void CSoundFile::SetSpeed(UINT param) { mpcpplog(); } +void CSoundFile::GlobalVolSlide(UINT param) { mpcpplog(); } +DWORD CSoundFile::IsSongFinished(UINT nOrder, UINT nRow) const { mpcpplog(); return 0; } +BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const { mpcpplog(); return 0; } +UINT CSoundFile::PackSample(int &sample, int next) { mpcpplog(); return 0; } +BOOL CSoundFile::CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result) { mpcpplog(); return 0; } +UINT CSoundFile::ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroySample(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroyInstrument(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::IsSampleUsed(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::IsInstrumentUsed(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveInstrumentSamples(UINT nInstr) { mpcpplog(); return 0; } +UINT CSoundFile::DetectUnusedSamples(BOOL *) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveSelectedSamples(BOOL *) { mpcpplog(); return 0; } +void CSoundFile::AdjustSampleLoop(MODINSTRUMENT *pIns) { mpcpplog(); } +BOOL CSoundFile::ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument) { mpcpplog(); return 0; } +BOOL CSoundFile::ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample) { mpcpplog(); return 0; } +UINT CSoundFile::GetNoteFromPeriod(UINT period) const { mpcpplog(); return 0; } +UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const { mpcpplog(); return 0; } +UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const { mpcpplog(); return 0; } +void CSoundFile::ResetMidiCfg() { mpcpplog(); } +UINT CSoundFile::MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote) { mpcpplog(); return 0; } +BOOL CSoundFile::ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers) { mpcpplog(); return 0; } +UINT CSoundFile::SaveMixPlugins(FILE *f, BOOL bUpdate) { mpcpplog(); return 0; } +UINT CSoundFile::LoadMixPlugins(const void *pData, UINT nLen) { mpcpplog(); return 0; } +#ifndef NO_FILTER +DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const { mpcpplog(); return 0; } +#endif +DWORD CSoundFile::TransposeToFrequency(int transp, int ftune) { mpcpplog(); return 0; } +int CSoundFile::FrequencyToTranspose(DWORD freq) { mpcpplog(); return 0; } +void CSoundFile::FrequencyToTranspose(MODINSTRUMENT *psmp) { mpcpplog(); } +MODCOMMAND *CSoundFile::AllocatePattern(UINT rows, UINT nchns) { mpcpplog(); return 0; } +signed char* CSoundFile::AllocateSample(UINT nbytes) { mpcpplog(); return 0; } +void CSoundFile::FreePattern(LPVOID pat) { mpcpplog(); } +void CSoundFile::FreeSample(LPVOID p) { mpcpplog(); } +UINT CSoundFile::Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc) { mpcpplog(); return 0; } + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // NO_LIBMODPLUG diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..0b6cb3a7d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,972 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 9 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh new file mode 100755 index 000000000..7df7ed3fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh + +./configure +make +make distcheck +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh new file mode 100755 index 000000000..7d2d1a2c2 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# apt install \ +# autoconf \ +# autoconf-archive \ +# autotools \ +# bison \ +# bzip2 \ +# curl \ +# dos2unix \ +# flex \ +# g++ \ +# gcc \ +# gzip \ +# make \ +# make \ +# pkg-config \ +# tar \ +# texinfo \ +# unzip \ +# wget \ +# xz-utils \ +# zlib1g-dev \ +# + +set -e + +MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +cd "${MY_DIR}" + +rm -rf build +rm -rf prefix + +mkdir build +mkdir prefix + +cd build +git clone https://github.com/jwt27/build-gcc build-gcc +cd build-gcc +git checkout 5d5747cc6da983e493a4258ff2009c338b2d6f60 # 2020-05-09 + +CFLAGS_FOR_TARGET="-O2" CXXFLAGS_FOR_TARGET="-O2" ./build-djgpp.sh --batch --prefix="${MY_DIR}/prefix" all diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh new file mode 100755 index 000000000..5acdd5026 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh @@ -0,0 +1 @@ +export PATH="`pwd`/contrib/toolchain-djgpp/prefix/bin":${PATH} diff --git a/Frameworks/OpenMPT/OpenMPT/doc/contributing.md b/Frameworks/OpenMPT/OpenMPT/doc/contributing.md index ee1894088..e3eaa7331 100644 --- a/Frameworks/OpenMPT/OpenMPT/doc/contributing.md +++ b/Frameworks/OpenMPT/OpenMPT/doc/contributing.md @@ -2,8 +2,8 @@ Contributing ============ -OpenMPT, libopenmpt, openmpt123, xmp-openmpt, in_openmpt and foo_openmpt are -developed in the Subversion repository at +OpenMPT, libopenmpt, openmpt123, xmp-openmpt and in_openmpt are developed in +the Subversion repository at [https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/](https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/). Patches can be provided either against this Subversion repository or against our GitHub mirror at diff --git a/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md b/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md index f93c6dc8d..eb1e0b2b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md +++ b/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md @@ -29,15 +29,11 @@ General hints determined beforehand. * Strings can be safely handled using: * `FileReader::ReadString` and friends for reading them directly from a file - * `mpt::String::Read` for reading them from a struct or char array, - * `mpt::String::Copy` for copying between char arrays or `std::string`. + * `mpt::String::ReadBuf` for reading them from a struct or char array - "Read" functions take care of string padding (zero / space padding), so those - should be used when extracting strings from files. "Copy" should only be used - on strings that have previously been read using the "Read" functions. - If the target is a char array rather than a `std::string`, these will take - care of properly null-terminating the target char array, and prevent reading - past the end of a (supposedly null-terminated) source char array. + These functions take care of string padding (zero / space padding) and will + avoid reading past the end of the string if there is no terminating null + character. * Do not use non-const static variables in your loader. Loaders need to be thread-safe for libopenmpt. * `FileReader` instances may be used to treat a portion of another file as its @@ -52,7 +48,8 @@ General hints `MODTYPE`. * Do not rely on hard-coded magic numbers. For example, when comparing if an index is valid for a given array, do not hard-code the array size but rather - use `mpt::size` or, for ensuring that char arrays are null-terminated, + use `std::size` (or `mpt::array_size` in contexts where `std::size` is not + usable) or, for ensuring that char arrays are null-terminated, `mpt::String::SetNullTerminator`. Similarly, do not assume any specific quantities for OpenMPT's constants like MAX_SAMPLES, MAX_PATTERN_ROWS, etc. These may change at any time. diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c index 550f2d030..2dcd13d33 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c @@ -28,6 +28,8 @@ static int16_t left[BUFFERSIZE]; static int16_t right[BUFFERSIZE]; static int16_t * const buffers[2] = { left, right }; +static int16_t interleaved_buffer[BUFFERSIZE * 2]; +static int is_interleaved = 0; static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; @@ -126,6 +128,10 @@ int main( int argc, char * argv[] ) { pa_initialized = 1; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error == paSampleFormatNotSupported ) { + is_interleaved = 1; + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; @@ -144,7 +150,7 @@ int main( int argc, char * argv[] ) { while ( 1 ) { openmpt_module_error_clear( mod ); - count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); mod_err = openmpt_module_error_get_last( mod ); mod_err_str = openmpt_module_error_get_last_message( mod ); if ( mod_err != OPENMPT_ERROR_OK ) { @@ -156,7 +162,7 @@ int main( int argc, char * argv[] ) { break; } - pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count ); + pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); if ( pa_error == paOutputUnderflowed ) { pa_error = paNoError; } diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c index 6a4057505..dda6379dc 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c @@ -27,6 +27,8 @@ static int16_t left[BUFFERSIZE]; static int16_t right[BUFFERSIZE]; static int16_t * const buffers[2] = { left, right }; +static int16_t interleaved_buffer[BUFFERSIZE * 2]; +static int is_interleaved = 0; static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; @@ -214,7 +216,12 @@ int main( int argc, char * argv[] ) { } pa_initialized = 1; + is_interleaved = 0; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error == paSampleFormatNotSupported ) { + is_interleaved = 1; + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; @@ -233,7 +240,7 @@ int main( int argc, char * argv[] ) { while ( 1 ) { openmpt_module_error_clear( mod ); - count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); mod_err = openmpt_module_error_get_last( mod ); mod_err_str = openmpt_module_error_get_last_message( mod ); if ( mod_err != OPENMPT_ERROR_OK ) { @@ -245,7 +252,7 @@ int main( int argc, char * argv[] ) { break; } - pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count ); + pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); if ( pa_error == paOutputUnderflowed ) { pa_error = paNoError; } diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp index b3eb4e70e..199308248 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp @@ -20,22 +20,18 @@ #include -#if (__cplusplus >= 201103L) #if defined(__clang__) #if ((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 40000) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" #endif #endif -#endif #include -#if (__cplusplus >= 201103L) #if defined(__clang__) #if ((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 40000) #pragma clang diagnostic pop #endif #endif -#endif #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) #if defined( __GNUC__ ) @@ -51,26 +47,53 @@ int main( int argc, char * argv[] ) { if ( argc != 2 ) { throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" ); } - const std::size_t buffersize = 480; - const std::int32_t samplerate = 48000; - std::vector left( buffersize ); - std::vector right( buffersize ); - const float * const buffers[2] = { left.data(), right.data() }; + constexpr std::size_t buffersize = 480; + constexpr std::int32_t samplerate = 48000; std::ifstream file( argv[1], std::ios::binary ); openmpt::module mod( file ); portaudio::AutoSystem portaudio_initializer; portaudio::System & portaudio = portaudio::System::instance(); + std::vector left( buffersize ); + std::vector right( buffersize ); + std::vector interleaved_buffer( buffersize * 2 ); + bool is_interleaved = false; +#if defined(_MSC_VER) && defined(_PREFAST_) + // work-around bug in VS2019 MSVC 16.5.5 static analyzer + is_interleaved = false; portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); portaudio::BlockingStream stream( stream_parameters ); +#else + portaudio::BlockingStream stream = [&]() { + try { + is_interleaved = false; + portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); + portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); + return portaudio::BlockingStream( stream_parameters ); + } catch ( const portaudio::PaException & e ) { + if ( e.paError() != paSampleFormatNotSupported ) { + throw; + } + is_interleaved = true; + portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, true, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); + portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); + return portaudio::BlockingStream( stream_parameters ); + } + }(); +#endif stream.start(); while ( true ) { - std::size_t count = mod.read( samplerate, buffersize, left.data(), right.data() ); + std::size_t count = is_interleaved ? mod.read_interleaved_stereo( samplerate, buffersize, interleaved_buffer.data() ) : mod.read( samplerate, buffersize, left.data(), right.data() ); if ( count == 0 ) { break; } try { - stream.write( buffers, static_cast( count ) ); + if ( is_interleaved ) { + stream.write( interleaved_buffer.data(), static_cast( count ) ); + } else { + const float * const buffers[2] = { left.data(), right.data() }; + stream.write( buffers, static_cast( count ) ); + } } catch ( const portaudio::PaException & pa_exception ) { if ( pa_exception.paError() != paOutputUnderflowed ) { throw; diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt index 31b85dc15..2cf182f4d 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt @@ -1,4 +1,4 @@ minimp3 library from https://github.com/lieff/minimp3 -commit 977514a6dfc4960d819a103f43b358e58ac6c28f (2019-07-24) +commit 55da78cbeea5fb6757f8df672567714e1e8ca3e9 (2020-03-04) The following changes have been made: * minimp3.c has been added diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h index 6015bb9fa..6fd645d7e 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h @@ -12,7 +12,7 @@ typedef struct { - int frame_bytes, channels, hz, layer, bitrate_kbps; + int frame_bytes, frame_offset, channels, hz, layer, bitrate_kbps; } mp3dec_frame_info_t; typedef struct @@ -163,6 +163,7 @@ end: } #elif defined(__ARM_NEON) || defined(__aarch64__) #include +#define HAVE_SSE 0 #define HAVE_SIMD 1 #define VSTORE vst1q_f32 #define VLD vld1q_f32 @@ -180,6 +181,7 @@ static int have_simd() return 1; } #else /* SIMD checks... */ +#define HAVE_SSE 0 #define HAVE_SIMD 0 #ifdef MINIMP3_ONLY_SIMD #error MINIMP3_ONLY_SIMD used, but SSE/NEON not enabled @@ -1672,7 +1674,7 @@ static int mp3d_find_frame(const uint8_t *mp3, int mp3_bytes, int *free_format_b } } *ptr_frame_bytes = 0; - return i; + return mp3_bytes; } void mp3dec_init(mp3dec_t *dec) @@ -1709,6 +1711,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s hdr = mp3 + i; memcpy(dec->header, hdr, HDR_SIZE); info->frame_bytes = i + frame_size; + info->frame_offset = i; info->channels = HDR_IS_MONO(hdr) ? 1 : 2; info->hz = hdr_sample_rate_hz(hdr); info->layer = 4 - HDR_GET_LAYER(hdr); @@ -1777,60 +1780,56 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s #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; + 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); + 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); + 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); + 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++) + 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 { - 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; - } + int16_t s = (int16_t)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt index 44eeb833b..6d32ad581 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt @@ -1,15 +1,12 @@ This folder contains the stb_vorbis library from -https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.17 -commit 130f28df68c3fdbf043c4275260ba1c870495b5b (2019-08-09) +https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.20 +commit b42009b3b9d4ca35bc703f5310eedc74f584be58 (2020-07-13) Modifications: * Use of alloca has been replaced with malloc, as alloca is not in C99 and fails to compile. * Macro redefinition of alloca with mingw-w64 has been fixed. * Macro redefinition of STB_VORBIS_NO_STDIO has been fixed. - * The following warnings have been silenced: - include/stb_vorbis/stb_vorbis.c:3928:32: warning: ‘hi’ may be used uninitialized in this function [-Wmaybe-uninitialized] - include/stb_vorbis/stb_vorbis.c:3927:32: warning: ‘low’ may be used uninitialized in this function [-Wmaybe-uninitialized] Modifications are always additions and have been marked with // OpenMPT. For building, premake is used to generate Visual Studio project files. diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c index eb9bf1cfc..75fabf7d6 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.17 - public domain +// Ogg Vorbis audio decoder - v1.20 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -26,20 +26,25 @@ // Terje Mathisen Niklas Frykholm Andy Hill // Casey Muratori John Bolton Gargaj // Laurent Gomila Marc LeBlanc Ronny Chevalier -// Bernhard Wodo Evan Balster alxprd@github +// Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// manxorist@github saga musix github:infatum -// Timur Gagiev Maxwell Koo +// github:manxorist saga musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde // // Partial history: +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. // 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) // 1.16 - 2019-03-04 - fix warnings // 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files -// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.11 - 2017-07-23 - fix MinGW compilation // 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory // 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version // 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame @@ -130,9 +135,20 @@ typedef struct int max_frame_size; } stb_vorbis_info; +typedef struct +{ + char *vendor; + + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + // get general information about the file extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); + // get the last error detected (clears it, too) extern int stb_vorbis_get_error(stb_vorbis *f); @@ -565,7 +581,7 @@ enum STBVorbisError #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT @@ -588,7 +604,9 @@ enum STBVorbisError #endif #define __forceinline #if 0 // OpenMPT + #ifndef alloca #define alloca __builtin_alloca + #endif #endif // OpenMPT #elif !defined(_MSC_VER) #if __GNUC__ @@ -763,6 +781,10 @@ struct stb_vorbis unsigned int temp_memory_required; unsigned int setup_temp_memory_required; + char *vendor; + int comment_list_length; + char **comment_list; + // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; @@ -778,8 +800,11 @@ struct stb_vorbis uint8 push_mode; + // the page to seek to when seeking to start, may be zero uint32 first_audio_page_offset; + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) ProbedPage p_first, p_last; // memory management @@ -828,7 +853,7 @@ struct stb_vorbis int current_loc_valid; // per-blocksize precomputed data - + // twiddle factors float *A[2],*B[2],*C[2]; float *window[2]; @@ -893,10 +918,10 @@ static int error(vorb *f, enum STBVorbisError e) #if 0 // OpenMPT #define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) -#define temp_free(f,p) 0 +#define temp_free(f,p) (void)0 #else // OpenMPT #define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : malloc(size)) // OpenMPT -#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : free(p)) // OpenMPT +#define temp_free(f,p) (f->alloc.alloc_buffer ? (void)0 : free(p)) // OpenMPT #endif // OpenMPT #define temp_alloc_save(f) ((f)->temp_offset) #define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) @@ -918,7 +943,7 @@ static void *make_block_array(void *mem, int count, int size) static void *setup_malloc(vorb *f, int sz) { - sz = (sz+3) & ~3; + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; @@ -937,7 +962,7 @@ static void setup_free(vorb *f, void *p) static void *setup_temp_malloc(vorb *f, int sz) { - sz = (sz+3) & ~3; + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; @@ -949,7 +974,7 @@ static void *setup_temp_malloc(vorb *f, int sz) static void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { - f->temp_offset += (sz+3)&~3; + f->temp_offset += (sz+7)&~7; return; } free(p); @@ -1155,7 +1180,7 @@ static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) if (!c->sparse) { int k = 0; for (i=0; i < c->entries; ++i) - if (include_in_sort(c, lengths[i])) + if (include_in_sort(c, lengths[i])) c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); assert(k == c->sorted_entries); } else { @@ -1338,7 +1363,7 @@ static int getn(vorb *z, uint8 *data, int n) return 1; } - #ifndef STB_VORBIS_NO_STDIO + #ifndef STB_VORBIS_NO_STDIO if (fread(data, n, 1, z->f) == 1) return 1; else { @@ -1413,12 +1438,15 @@ static int capture_pattern(vorb *f) static int start_page_no_capturepattern(vorb *f) { uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } // stream structure version if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag f->page_flag = get8(f); // absolute granule position - loc0 = get32(f); + loc0 = get32(f); loc1 = get32(f); // @TODO: validate loc0,loc1 as valid positions? // stream serial number -- vorbis doesn't interleave, so discard @@ -1449,15 +1477,12 @@ static int start_page_no_capturepattern(vorb *f) } if (f->first_decode) { int i,len; - ProbedPage p; len = 0; for (i=0; i < f->segment_count; ++i) len += f->segments[i]; len += 27 + f->segment_count; - p.page_start = f->first_audio_page_offset; - p.page_end = p.page_start + len; - p.last_decoded_sample = loc0; - f->p_first = p; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; @@ -1548,6 +1573,16 @@ static int get8_packet(vorb *f) return x; } +static int get32_packet(vorb *f) +{ + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32) get8_packet(f) << 24; + return x; +} + static void flush_packet(vorb *f) { while (get8_packet_raw(f) != EOP); @@ -1578,7 +1613,8 @@ static uint32 get_bits(vorb *f, int n) f->valid_bits += 8; } } - if (f->valid_bits < 0) return 0; + + assert(f->valid_bits >= n); z = f->acc & ((1 << n)-1); f->acc >>= n; f->valid_bits -= n; @@ -1903,69 +1939,69 @@ static int predict_point(int x, int x0, int x1, int y0, int y1) // the following table is block-copied from the specification static float inverse_db_table[256] = { - 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, - 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, - 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, - 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, - 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, - 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, - 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, - 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, - 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, - 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, - 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, - 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, - 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, - 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, - 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, - 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, - 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, - 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, - 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, - 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, - 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, - 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, - 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, - 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, - 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, - 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, - 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, - 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, - 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, - 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, - 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, - 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, - 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, - 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, - 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, - 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, - 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, - 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, - 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, - 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, - 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, - 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, - 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, - 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, - 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, - 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, - 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, - 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, - 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, - 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, - 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, - 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, - 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, - 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, - 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, - 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, - 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, - 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, - 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, - 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, - 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, - 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, - 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, 0.82788260f, 0.88168307f, 0.9389798f, 1.0f }; @@ -2139,47 +2175,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int ++class_set; #endif } - } else if (ch == 1) { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = 0, p_inter = z; - if (pass == 0) { - Codebook *c = f->codebooks+r->classbook; - int q; - DECODE(q,f,c); - if (q == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = 0; - p_inter = z; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } else { + } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = z % ch, p_inter = z/ch; @@ -2366,11 +2362,11 @@ void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) #if LIBVORBIS_MDCT // directly call the vorbis MDCT using an interface documented // by Jeff Roberts... useful for performance comparison -typedef struct +typedef struct { int n; int log2n; - + float *trig; int *bitrev; @@ -2389,7 +2385,7 @@ void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) if (M1.n == n) M = &M1; else if (M2.n == n) M = &M2; else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } - else { + else { if (M2.n) __asm int 3; mdct_init(&M2, n); M = &M2; @@ -2802,7 +2798,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d1[0] = u[k4+1]; d0[1] = u[k4+2]; d0[0] = u[k4+3]; - + d0 -= 4; d1 -= 4; bitrev += 2; @@ -2883,7 +2879,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) float p0,p1,p2,p3; p3 = e[6]*B[7] - e[7]*B[6]; - p2 = -e[6]*B[6] - e[7]*B[7]; + p2 = -e[6]*B[6] - e[7]*B[7]; d0[0] = p3; d1[3] = - p3; @@ -2891,7 +2887,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d3[3] = p2; p1 = e[4]*B[5] - e[5]*B[4]; - p0 = -e[4]*B[4] - e[5]*B[5]; + p0 = -e[4]*B[4] - e[5]*B[5]; d0[1] = p1; d1[2] = - p1; @@ -2899,7 +2895,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d3[2] = p0; p3 = e[2]*B[3] - e[3]*B[2]; - p2 = -e[2]*B[2] - e[3]*B[3]; + p2 = -e[2]*B[2] - e[3]*B[3]; d0[2] = p3; d1[1] = - p3; @@ -2907,7 +2903,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d3[1] = p2; p1 = e[0]*B[1] - e[1]*B[0]; - p0 = -e[0]*B[0] - e[1]*B[1]; + p0 = -e[0]*B[0] - e[1]*B[1]; d0[3] = p1; d1[0] = - p1; @@ -3513,7 +3509,7 @@ static int vorbis_pump_first_frame(stb_vorbis *f) } #ifndef STB_VORBIS_NO_PUSHDATA_API -static int is_whole_packet_present(stb_vorbis *f, int end_page) +static int is_whole_packet_present(stb_vorbis *f) { // make sure that we have the packet available before continuing... // this requires a full ogg parse, but we know we can fetch from f->stream @@ -3533,15 +3529,13 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) break; } // either this continues, or it ends it... - if (end_page) - if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); if (s == f->segment_count) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } for (; s == -1;) { - uint8 *q; + uint8 *q; int n; // check that we have the page header ready @@ -3567,8 +3561,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) if (q[s] < 255) break; } - if (end_page) - if (s < n-1) return error(f, VORBIS_invalid_stream); if (s == n) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); @@ -3585,6 +3577,7 @@ static int start_decoder(vorb *f) int longest_floorlist=0; // first page, first packet + f->first_decode = TRUE; if (!start_page(f)) return FALSE; // validate page flag @@ -3642,6 +3635,44 @@ static int start_decoder(vorb *f) if (!start_page(f)) return FALSE; if (!start_packet(f)) return FALSE; + + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + //file vendor + len = get32_packet(f); + f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for(i=0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + //user comments + f->comment_list_length = get32_packet(f); + f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + + for(i=0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for(j=0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; + do { len = next_segment(f); skip(f, len); @@ -3653,7 +3684,7 @@ static int start_decoder(vorb *f) #ifndef STB_VORBIS_NO_PUSHDATA_API if (IS_PUSH_MODE(f)) { - if (!is_whole_packet_present(f, TRUE)) { + if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; @@ -3912,7 +3943,7 @@ static int start_decoder(vorb *f) } else { stbv__floor_ordering p[31*8+2]; Floor1 *g = &f->floor_config[i].floor1; - int max_class = -1; + int max_class = -1; g->partitions = get_bits(f, 5); for (j=0; j < g->partitions; ++j) { g->partition_class_list[j] = get_bits(f, 4); @@ -3956,12 +3987,7 @@ static int start_decoder(vorb *f) g->sorted_order[j] = (uint8) p[j].id; // precompute the neighbors for (j=2; j < g->values; ++j) { -#if 0 // OpenMPT - int low,hi; -#else // OpenMPT - int low=0; // OpenMPT - int hi=0; // OpenMPT -#endif // OpenMPT + int low = 0,hi = 0; neighbors(g->Xlist, j, &low,&hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; @@ -4030,7 +4056,7 @@ static int start_decoder(vorb *f) if (f->mapping == NULL) return error(f, VORBIS_outofmem); memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); for (i=0; i < f->mapping_count; ++i) { - Mapping *m = f->mapping + i; + Mapping *m = f->mapping + i; int mapping_type = get_bits(f,16); if (mapping_type != 0) return error(f, VORBIS_invalid_setup); m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); @@ -4146,7 +4172,6 @@ static int start_decoder(vorb *f) f->temp_memory_required = imdct_mem; } - f->first_decode = TRUE; if (f->alloc.alloc_buffer) { assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); @@ -4155,7 +4180,17 @@ static int start_decoder(vorb *f) return error(f, VORBIS_outofmem); } - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } return TRUE; } @@ -4163,6 +4198,13 @@ static int start_decoder(vorb *f) static void vorbis_deinit(stb_vorbis *p) { int i,j; + + setup_free(p, p->vendor); + for (i=0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); + if (p->residue_config) { for (i=0; i < p->residue_count; ++i) { Residue *r = p->residue_config+i; @@ -4228,7 +4270,7 @@ static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; @@ -4262,6 +4304,15 @@ stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) return d; } +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) +{ + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} + int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; @@ -4403,7 +4454,7 @@ int stb_vorbis_decode_frame_pushdata( f->error = VORBIS__no_error; // check that we have the entire packet in memory - if (!is_whole_packet_present(f, FALSE)) { + if (!is_whole_packet_present(f)) { *samples = 0; return 0; } @@ -4639,8 +4690,8 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { ProbedPage left, right, mid; int i, start_seg_with_known_loc, end_pos, page_start; - uint32 delta, stream_length, padding; - double offset, bytes_per_sample; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; // find the last page and validate the target sample @@ -4653,9 +4704,9 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) // indicates should be the granule position (give or take one)). padding = ((f->blocksize_1 - f->blocksize_0) >> 2); if (sample_number < padding) - sample_number = 0; + last_sample_limit = 0; else - sample_number -= padding; + last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { @@ -4668,9 +4719,12 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) assert(right.last_decoded_sample != ~0U); // starting from the start is handled differently - if (sample_number <= left.last_decoded_sample) { - if (stb_vorbis_seek_start(f)) + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); return 1; + } return 0; } @@ -4687,10 +4741,10 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) // first probe (interpolate) double data_bytes = right.page_end - left.page_start; bytes_per_sample = data_bytes / right.last_decoded_sample; - offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); } else { // second probe (try to bound the other side) - double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; offset += error * 2; @@ -4721,14 +4775,16 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) } // if we've just found the last page again then we're in a tricky file, - // and we're close enough. - if (mid.page_start == right.page_start) - break; - - if (sample_number < mid.last_decoded_sample) - right = mid; - else - left = mid; + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } ++probe; } @@ -4844,8 +4900,8 @@ int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) flush_packet(f); } } - // the next frame will start with the sample - assert(f->current_loc == sample_number); + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); return 1; } @@ -5022,7 +5078,7 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st #else f = fopen(filename, "rb"); #endif - if (f) + if (f) return stb_vorbis_open_file(f, TRUE, error, alloc); if (error) *error = VORBIS_file_open_failure; return NULL; @@ -5085,7 +5141,7 @@ static int8 channel_position[7][6] = #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) - #define check_endianness() + #define check_endianness() #else #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) #define check_endianness() @@ -5187,7 +5243,7 @@ static void convert_samples_short(int buf_c, short **buffer, int b_offset, int d int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { - float **output; + float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) @@ -5410,20 +5466,20 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in #endif // STB_VORBIS_NO_PULLDATA_API /* Version history - 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13223, -13223 + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 found with Mayhem by ForAllSecure 1.16 - 2019-03-04 - fix warnings 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found 1.14 - 2018-02-11 - delete bogus dealloca usage 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files - 1.11 - 2017-07-23 - fix MinGW compilation + 1.11 - 2017-07-23 - fix MinGW compilation 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; avoid discarding last frame of audio data 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API - some more crash fixes when out of memory or with corrupt files + some more crash fixes when out of memory or with corrupt files 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) some crash fixes when out of memory or with corrupt files 1.05 - 2015-04-19 - don't define __forceinline if it's redundant @@ -5479,38 +5535,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/.clang-format b/Frameworks/OpenMPT/OpenMPT/libopenmpt/.clang-format new file mode 100644 index 000000000..a50eb55a0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/.clang-format @@ -0,0 +1,45 @@ +Language: Cpp +AccessModifierOffset: -2 +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: Empty +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakConstructorInitializersBeforeComma: true +BreakStringLiterals: false +ColumnLimit: 0 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentWidth: 2 +MaxEmptyLinesToKeep: 3 +PointerAlignment: Middle +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 2 +SpacesInContainerLiterals: false +SpacesInParentheses: true +SpacesInSquareBrackets: true +Standard: Cpp11 +TabWidth: 2 +UseTab: ForIndentation diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi index 059e0ae5b..6e83441b9 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi @@ -480,7 +480,7 @@ Const OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR = -255 /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it - \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param filesize Full size of the file data on disk. @@ -506,7 +506,7 @@ Declare Function openmpt_probe_file_header(ByVal flags As ULongInt, ByVal Data A /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it - \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param logfunc Logging function where warning and errors are written. May be NULL. @@ -532,7 +532,7 @@ Declare Function openmpt_probe_file_header_without_filesize(ByVal flags As ULong /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it - \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param stream_callbacks Input stream callback operations. \param stream Input stream to scan. \param logfunc Logging function where warning and errors are written. May be NULL. @@ -1055,6 +1055,15 @@ Declare Function openmpt_module_get_metadata_keys_ Alias "openmpt_module_get_met '/ Declare Function openmpt_module_get_metadata_ Alias "openmpt_module_get_metadata" (ByVal module As openmpt_module Ptr, ByVal key As Const ZString Ptr) As Const ZString Ptr +/'* \brief Get the current estimated beats per minute (BPM). + + \param module The module handle to work on. + \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. + \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. + \return The current estimated BPM. +'/ +Declare Function openmpt_module_get_current_estimated_bpm(ByVal module As openmpt_module Ptr) As Double + /'* \brief Get the current speed \param module The module handle to work on. @@ -1356,6 +1365,11 @@ Declare Function openmpt_module_highlight_pattern_row_channel_ Alias "openmpt_mo - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + - render.resampler.emulate_amiga_type: Configures the filter type to use for the Amiga resampler. Supported values are: + - "auto": Filter type is chosen by the library and might change. This is the default. + - "a500": Amiga A500 filter. + - "a1200": Amiga A1200 filter. + - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: - 0: No dithering. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt deleted file mode 100644 index 327dd1fa2..000000000 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt +++ /dev/null @@ -1,9 +0,0 @@ - -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/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index 5b98fcf60..29103f028 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,131 +5,189 @@ Changelog {#changelog} For fully detailed change log, please see the source repository directly. This is just a high-level summary. -### libopenmpt 0.4.10 (2019-10-30) +### libopenmpt 0.5.2 (2020-08-30) - * The "date" metadata could contain a bogus date for some older IT files. - * Do not apply global volume ramping from initial global volume when seeking. + * [**Change**] `Makefile` `CONFIG=emscripten` now supports + `EMSCRIPTEN_TARGET=all` which provides WebAssembly as well as fallback to + JavaScript in a single build. - * MTM: Sample loop length was off by one. - * PSM: Sample loop length was off by one in most files. + * [**Regression**] `Makefile` `CONFIG=emscripten` does not support + `EMSCRIPTEN_TARGET=asmjs` or `EMSCRIPTEN_TARGET=asmjs128m` any more because + support has been removed from current Emscripten versions. + * [**Regression**] Support for Emscripten versions older than 1.39.7 has been + removed. - * mpg123: Update to v1.25.13 (2019-10-26). + * PP20: The first few bytes of some files were not decompressed properly, + making some files unplayable (depending on the original format). -### libopenmpt 0.4.9 (2019-10-02) +### libopenmpt 0.5.1 (2020-07-26) - * [**Sec**] libmodplug: C API: Limit the length of strings copied to the - output buffer of `ModPlug_InstrumentName()` and `ModPlug_SampleName()` to 32 - bytes (including terminating null) as is done by original libmodplug. This - avoids potential buffer overflows in software relying on this limit instead - of querying the required buffer size beforehand. libopenmpt can return - strings longer than 32 bytes here beacuse the internal limit of 32 bytes - applies to strings encoded in arbitrary character encodings but the API - returns them converted to UTF-8, which can be longer. (reported by Antonio - Morales Maldonado of Semmle Security Research Team) (r12129) - ([CVE-2019-17113](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17113)) - * [**Sec**] libmodplug: C++ API: Do not return 0 in - `CSoundFile::GetSampleName()` and `CSoundFile::GetInstrumentName()` when a - null output pointer is provided. This behaviour differed from libmodplug and - made it impossible to determine the required buffer size. (r12130) + * [**Bug**] `libopenmpt/libopenmpt.h` failed to compile with + `LIBOPENMPT_NO_DEPRECATE` defined. -### libopenmpt 0.4.8 (2019-09-30) + * MPTM: Qxy now retriggers OPL notes if new compatibility flag is set in file. + * MPTM: Bring back old OPL note end-of-envelope behaviour for files made with + OpenMPT 1.28. + * IT: Global volume slides with both nibbles set preferred the "slide up" + nibble over the "slide down" nibble in old OpenMPT versions, unlike other + slides. Such old files are now imported correctly again. + * IT: Fixed an edge case where, if the filter hit full cutoff / no resonance + on the first tick of a row where a new delayed note would be triggered, the + filter would be disabled even though it should stay active. Fixes trace.it + by maddie. + * OXM: Some sample loops were not imported correctly. + * XM: Out-of-range arpeggio clamping behaviour broke in OpenMPT 1.23.05.00. + The arpeggios in Binary World by Dakota now play correctly again. + * S3M: Support old-style sample pre-amp value in very early S3M files. + * S3M: Only force-enable fast slides for files ST 3.00. Previously, any S3M + file made with an ST3 version older than 3.20 enabled them. + * S3M: Only apply volume and middle-C speed on instrument change if the new + sample slot has sample data. + * MOD: Fix an infinite loop in GamerMan by MrGamer by playing non-ProTracker + MODs more like FT2 would. + * M15: Improve tracker detection heuristics to never assume SoundTracker 2.0 + if there is a huge number of Dxx commands, as that is a definite hint that + they should be treated as volume slides. Fixes Monty On The Run by + Master Blaster. + * MO3: Support OPL patches in MO3 files created from MPTM and S3M. + * DBM: If a global pattern command would be lost because both effect commands + in a cell would have to go into the regular effect column (e.g. a speed and + a tempo command), the lost command is now attempted to be written into a + different cell on the same row. Fixes "Party-Question V" by grogon. - * [**Sec**] Possible crash due to out-of-bounds read when playing an OPL note - with active filter in S3M or MPTM files (r12118). + * mpg123: Update to v1.26.3 (2020-07-16). + * stb_vorbis: Update v1.20 commit b42009b3b9d4ca35bc703f5310eedc74f584be58 + (2020-07-13). -### libopenmpt 0.4.7 (2019-09-23) +### libopenmpt 0.5.0 (2020-05-24) - * [**Bug**] Compilation fix for various platforms that do not provide - `std::aligned_alloc` in C++17 mode. The problematic dependency has been - removed. This should fix build problems on MinGW, OpenBSD, Haiku, and others - for good. + * [**New**] OggMod compressed FastTracker 2 XM (OXM) modules are now + supported. + * [**New**] The emulated Amiga type when Amiga resampler emulation is enabled + can now be selected via ctl `render.resampler.emulate_amiga_type`. Possible + values are: `"auto"`, `"a500"`, `"a1200"`, and an experimental option + `"unfiltered"`. + * [**New**] libopenmpt: New API `openmpt::module::get_current_estimated_bpm()` + (C++), and `openmpt_module_get_current_estimated_bpm()` (C) which provides + accurate beats per minute information for module formats with time signature + and an educated guess based on speed and tempo for others. + * [**New**] libopenmpt: New type-aware ctl APIs that do not require memory + allocations and are thus realtime-safe: + `openmpt::module::ctl_get_boolean()`, `openmpt::module::ctl_get_integer()`, + `openmpt::module::ctl_get_floatingpoint()`, + `openmpt::module::ctl_get_text()`, `openmpt::module::ctl_set_boolean()`, + `openmpt::module::ctl_set_integer()`, + `openmpt::module::ctl_set_floatingpoint()` (C++), and + `openmpt_module_ctl_get_boolean()`, `openmpt_module_ctl_get_integer()`, + `openmpt_module_ctl_get_floatingpoint()`, `openmpt_module_ctl_get_text()`, + `openmpt_module_ctl_set_boolean()`, `openmpt_module_ctl_set_integer()`, + `openmpt_module_ctl_set_floatingpoint()` (C). + * [**New**] libopenmpt C++ New API `openmpt::is_extension_supported2()` which + takes a `std::string_view` parameter instead of `std::string`. + * [**New**] libopenmpt C++: New API + `openmpt::module::module(std::vector data)`, + `openmpt::module::module(const std::byte * data, std::size_t size)`, + `openmpt::module::module(const std::byte * beg, const std::byte * end)`. + * [**New**] libopenmpt C++: New API + `openmpt::probe_file_header(flags, const std::byte * data, std::size_t size, filesize)`, + `openmpt::probe_file_header(flags, const std::byte * data, std::size_t size)`. + * [**New**] libopenmpt_ext C++: New API + `openmpt::module_ext::module_ext(std::vector data)`, + `openmpt::module_ext::module_ext(const std::byte * data, std::size_t size)`, + `openmpt::module_ext::module_ext(std::vector data)`, + `openmpt::module_ext::module_ext(const std::uint8_t * data, std::size_t size)`. - * J2B: Ignore notes with non-existing instrument (fixes Ending.j2b). + * [**Change**] std::istream based file I/O has been speed up. + * [**Change**] Dependency on iconv on Linux has been removed. - * mpg123: Update to v1.25.12 (2019-08-24). - * ogg: Update to v1.3.4. (2019-08-31). - * flac: Update to v1.3.3. (2019-08-04). + * [**Regression**] libmodplug: The libmodplug emulation layer has been removed + from the libopenmpt tree. Please use the separate `libopenmpt-modplug` + package instead. + * [**Regression**] foo_openmpt: foo_openmpt is discontinued. Please use + Kode54's fork foo_openmpt54: + . + * [**Regression**] Support for building with C++11 or C++14 has been removed. + C++17 is now required to build libopenmpt. + * [**Regression**] Support for client code using C++11 or C++ 14 has been + removed. C++17 is now required to build libopenmpt client applications. + * [**Regression**] Support for Visual Studio 2015 has been removed. + * [**Regression**] Support for GCC 4.8, 4.9, 5, 6 has been removed. + * [**Regression**] Support for Clang 3.6, 3.7, 3.8, 3.9, 4 has been removed. + * [**Regression**] Support for Emscripten versions older than 1.39.1 has been + removed. + * [**Regression**] Building with Android NDK older than NDK r18b is not + supported any more. + * [**Regression**] openmpt123: Support for SDL1 (but not SDL2) output has been + removed. + * [**Regression**] openmpt123: Support for SDL2 older than 2.0.4 has been + removed. + * [**Regression**] Windows XP and Windows Vista are no longer supported. + * [**Regression**] It is no longer possible to optionally use iconv for + character set conversions. -### libopenmpt 0.4.6 (2019-08-10) + * [**Bug**] openmpt123: openmpt123 now honors the current locale and outputs + text appropriately. + * [**Bug**] openmpt123: Piping text output to other than console window + targets on Windows has been fixed. - * [**Bug**] Compilation fix for OpenBSD. - * [**Bug**] Compilation fix for NO_PLUGINS being defined. + * Greatly improved MED import. Synthesized instruments are still not supported + but support was added for: Multisampled instruments, delta samples, more + pattern commands, Hold and Decay, multiple songs and many other small + changes. + * Improved OPL channel allocation when more than 18 notes are active, so that + channels that have completely faded out are prioritized over channels that + have already been released but have not faded out yet. + * Interactively triggering an OPL instrument could cause the first pattern + channel to no longer be played back correctly. + * Fix some inaccuracies in OPL emulator. + * Fix overflow of OPL amplification happening at a synth volume level of 510. + * End-of-sample pop reduction of surround channels was applied to front + channels instead, causing a pop on the front channels instead of removing it + on the back channels. + * IT: Disable retrigger with short notes quirk for modules saved with + Chibi Tracker, as it does not implement that quirk. + * IT: Instrument and sample panning should not override channel panning for + following notes. + * IT: SBx is now prioritized over Bxx commands that are to the left of it. + * IT: Duplicate Check Type "Sample" should only be applied if the instruments + match, too. + * IT: Duplicate Check Type "Note" should compare pattern notes, but it was + comparing the new pattern note against the old translated note. + * IT: Various fixes for envelope resetting. + * IT / S3M: When combining SBx and EEx effects, don't skip the first row of + the loop like in FastTracker 2. + * S3M: Empty pattern commands now affect effect memory as well. + * S3M: Offset beyond loop end wraps around to loop start like in + Scream Tracker 3 + GUS (previously it just keep playing from the loop start, + which is neither what GUS nor Sound Blaster drivers do). + * S3M: Notes cannot be retriggered after they have been cut. + * S3M: Fix portamento after note cut (fixes antediluvian_song.s3m). + * S3M / MOD: Previous note offset is no longer used for retriggered notes if + there was no instrument number next to the Qxy effect. + * MOD: Sample swapping now also works if the sample that is being swapped from + does not loop. Swapping to a non-looped sample now stops playback once the + swapped-from sample reaches its (loop) end. + * MOD: Fix early song ending due to ProTracker pattern jump quirk + (EEx + Dxx on same row) if infinite looping is disabled. + Fixes Haunted Tracks.mod by Triace. + * MOD: Previous note offset is no longer used for retriggered notes if there + was no instrument number next to the E9x effect. + * MOD: Vibrato type "ramp down" was upside down. + * XM: If a file contains patterns longer than 1024 rows, they are now clamped + to 1024 rows instead of 64 rows. + * XM: Do not reset note-off status on portamento if there is no instrument + number. - * in_openmpt: Correct documentation. `openmpt-mpg123.dll` must be placed into - the Winamp directory. - - * Detect IT files unpacked with early UNMO3 versions. - - * mpg123: Update to v1.25.11 (2019-07-18). - * minimp3: Update to commit 977514a6dfc4960d819a103f43b358e58ac6c28f - (2019-07-24). - * miniz: Update to v2.1.0 (2019-05-05). - * stb_vorbis: Update to v1.17 (2019-08-09). - -### libopenmpt 0.4.5 (2019-05-27) - - * [**Sec**] Possible crash during playback due out-of-bounds read in XM and - MT2 files (r11608). - ([CVE-2019-14380](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14380)) - - * Breaking out of a sustain loop through Note-Off sometimes didn't continue in - the regular sample loop. - * Seeking did not stop notes playing with XM Key Off (Kxx) effect. - -### libopenmpt 0.4.4 (2019-04-07) - - * [**Bug**] Channel VU meters were swapped. - - * Startrekker: Clamp speed to 31 ticks per row. - * MTM: Ignore unused Exy commands on import. Command E5x (Set Finetune) is now - applied correctly. - * MOD: Sample swapping was always enabled since it has been separated from the - ProTracker 1/2 compatibility flag. Now it is always enabled for Amiga-style - modules and otherwise the old heuristic is used again. - - * stb_vorbis: Update to v1.16 (2019-03-05). - -### libopenmpt 0.4.3 (2019-02-11) - - * [**Sec**] Possible crash due to null-pointer access when doing a portamento - from an OPL instrument to an empty instrument note map slot (r11348). - ([CVE-2019-14381](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14381)) - - * [**Bug**] libopenmpt did not compile on Apple platforms in C++17 mode. - - * IT: Various fixes for note-off + instrument number in Old Effects mode. - * MO3: Import IT row highlights as written by MO3 2.4.1.2 or newer. Required - for modules using modern tempo mode. - - * miniz: Update to v2.0.8 (2018-09-19). - * stb_vorbis: Update to v1.15 (2019-02-07). - -### libopenmpt 0.4.2 (2019-01-22) - - * [**Sec**] DSM: Assertion failure during file parsing with debug STLs - (r11209). - ([CVE-2019-14382](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14382)) - * [**Sec**] J2B: Assertion failure during file parsing with debug STLs - (r11216). - ([CVE-2019-14383](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14383)) - - * S3M: Allow volume change of OPL instruments after Note Cut. - -### libopenmpt 0.4.1 (2019-01-06) - - * [**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. - - * MO3: Apply playback changes based on "ModPlug-made" header flag. - - * minimp3: Update to commit e9df0760e94044caded36a55d70ab4152134adc5 - (2018-12-23). + * mpg123: v1.26rc3. + * ogg: v1.3.4. + * vorbis: v1.3.6. + * zlib: v1.2.11. + * minimp3: commit 55da78cbeea5fb6757f8df672567714e1e8ca3e9 (2020-03-04). + * stb_vorbis: v1.19 commit 37b9b20fdec06c75a0493e0bb59e2d0f288bfb51 + (2020-02-05). + * miniz: v2.1.0. + * FLAC: v1.3.3. + * PortAudio: commit 799a6834a58592eadc5712cba73b35956dc51579 (2020-03-26). ### libopenmpt 0.4.0 (2018-12-23) diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md index 936619017..d6eed5c61 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md @@ -9,17 +9,16 @@ Dependencies ### libopenmpt * Supported compilers for building libopenmpt: - * **Microsoft Visual Studio 2015** or higher, running on a x86-64 build + * **Microsoft Visual Studio 2017** or higher, running on a x86-64 build system (other target systems are supported) - * **GCC 4.8** or higher - * **Clang 3.6** or higher - * **MinGW-W64 4.8** or higher (it is recommended to preferably use + * **GCC 7.1** or higher + * **Clang 5** or higher + * **MinGW-W64 7.1** or higher (it is recommended to preferably use posix threading model as opposed to win32 threading model, or at least have mingw-std-threads available otherwise) - * **emscripten 1.38.5** or higher + * **emscripten 1.39.1** 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) + * any other **C++17 compliant** compiler libopenmpt makes the following assumptions about the C++ implementation used for building: @@ -27,8 +26,8 @@ Dependencies 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) + * in C++20 mode, `std::endian::little != std::endian::big` (enforced + by static_assert) * `wchar_t` encoding is either UTF-16 or UTF-32 (implicitly assumed) * representation of basic source character set is ASCII (implicitly assumed) @@ -48,8 +47,7 @@ Dependencies * Required compilers to use libopenmpt: * Any **C89** / **C99** / **C11** compatible compiler should work with the C API as long as a **C99** compatible **stdint.h** is available. - * Any **C++11** / **C++14** / **C++17** compatible compiler should work - with the C++ API. + * Any **C++17** compatible compiler should work with the C++ API. * **J2B** support requires an inflate (deflate decompression) implementation: * **zlib** * **miniz** can be used internally if no zlib is available. @@ -73,17 +71,16 @@ Dependencies ### openmpt123 * Supported compilers for building openmpt123: - * **Microsoft Visual Studio 2015** or higher, running on a x86-64 build + * **Microsoft Visual Studio 2017** or higher, running on a x86-64 build system (other target systems are supported) - * **GCC 4.8** or higher - * **Clang 3.6** or higher - * **MinGW-W64 4.8** or higher + * **GCC 7.1** or higher + * **Clang 5** or higher + * **MinGW-W64 7.1** or higher * **DJGPP GCC 7.2** or higher - * any **C++11 compliant** compiler + * any **C++17 compliant** compiler * Live sound output requires one of: * **PulseAudio** * **SDL 2** - * **SDL 1.2** * **PortAudio v19** * **Win32** * **liballegro 4.2** on DJGPP/DOS @@ -94,11 +91,6 @@ Optional dependencies ### libopenmpt - * Character set conversion can use one of: - * **Win32** - * **iconv** - * **C++11** codecvt_utf8 - instead of the internal conversion tables and functions. * **doxygen 1.8** or higher is required to build the documentation. ### openmpt123 @@ -119,6 +111,8 @@ Building the source packages additionally requires: * autoconf * autoconf-archive * automake + * awk (mawk) + * git * gzip * help2man * libtool diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md index bc7563604..428138459 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md @@ -7,7 +7,7 @@ Quick Start {#quickstart} 1. Grab a `libopenmpt-VERSION.autotools.tar.gz` tarball. 2. Get dependencies: - - **gcc >= 4.8** or **clang >= 3.6** + - **gcc >= 7.1** or **clang >= 5** - **pkg-config >= 0.24** - **zlib** - **libogg**, **libvorbis**, **libvorbisfile** @@ -18,7 +18,7 @@ Quick Start {#quickstart} - **libFLAC** (required only by openmpt123) - **libsndfile** (required only by openmpt123) 3. *Optional*: - - **libSDL >= 1.2.x** (required only by openmpt123) + - **libSDL2 >= 2.0.4** (required only by openmpt123) 4. Run: ./configure @@ -29,20 +29,20 @@ Quick Start {#quickstart} ### Windows 1. Get dependencies: - - **Microsoft Visual Studio >= 2015** + - **Microsoft Visual Studio >= 2017** 2. *Optionally* get dependencies: - **Winamp SDK** - **XMPlay SDK** 3. Checkout `https://source.openmpt.org/svn/openmpt/trunk/OpenMPT/` . - 4. Open `build\vs2015\openmpt123.sln` or `build\vs2015\libopenmpt.sln` or `build\vs2015\xmp-openmpt.sln` or `build\vs2015\in_openmpt.sln` or `build\vs2015\foo_openmpt.sln` in *Microsoft Visual Studio 2015*. + 4. Open `build\vs2017\openmpt123.sln` or `build\vs2017\libopenmpt.sln` or `build\vs2017\xmp-openmpt.sln` or `build\vs2017\in_openmpt.sln` in *Microsoft Visual Studio 2017*. 5. Select appropriate configuration and build. Binaries are generated in `bin\` - 6. Drag a module onto `openmpt123.exe` or copy the player plugin DLLs (`in_openmpt.dll`, `xmp-openmpt.dll` or `foo_openmpt.dll`) into the respective player directory. + 6. Drag a module onto `openmpt123.exe` or copy the player plugin DLLs (`in_openmpt.dll` or `xmp-openmpt.dll`) into the respective player directory. ### Unix-like 1. Get dependencies: - **GNU make** - - **gcc >= 4.8** or **clang >= 3.6** + - **gcc >= 7.1** or **clang >= 5** - **pkg-config** - **zlib** - **libogg**, **libvorbis**, **libvorbisfile** @@ -52,7 +52,7 @@ Quick Start {#quickstart} - **libFLAC** (required only by openmpt123) - **libsndfile** (required only by openmpt123) 2. *Optional*: - - **libSDL >= 1.2.x** (required only by openmpt123) + - **libSDL2 >= 2.0.4** (required only by openmpt123) - **doxygen >= 1.8** - **help2man** 3. Run: diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp deleted file mode 100644 index 0172bdd0d..000000000 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp +++ /dev/null @@ -1,323 +0,0 @@ - -#if defined(_MSC_VER) -#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" - -#include -#include -#include -#include - - - -// Declaration of your component's version information -// Since foobar2000 v1.0 having at least one of these in your DLL is mandatory to let the troubleshooter tell different versions of your component apart. -// Note that it is possible to declare multiple components within one DLL, but it's strongly recommended to keep only one declaration per DLL. -// As for 1.1, the version numbers are used by the component update finder to find updates; for that to work, you must have ONLY ONE declaration per DLL. If there are multiple declarations, the component is assumed to be outdated and a version number of "0" is assumed, to overwrite the component with whatever is currently on the site assuming that it comes with proper version numbers. -DECLARE_COMPONENT_VERSION("OpenMPT component", OPENMPT_API_VERSION_STRING ,"libopenmpt based module file player"); - - - -// This will prevent users from renaming your component around (important for proper troubleshooter behaviors) or loading multiple instances of it. -VALIDATE_COMPONENT_FILENAME("foo_openmpt.dll"); - - -// settings - - -// {FFD659CA-6AEA-479f-8E60-F03B297286FE} -static const GUID guid_openmpt_root = -{ 0xffd659ca, 0x6aea, 0x479f, { 0x8e, 0x60, 0xf0, 0x3b, 0x29, 0x72, 0x86, 0xfe } }; - - -// {E698E101-FD93-4e6c-AF02-AEC7E8631D8E} -static const GUID guid_openmpt_samplerate = -{ 0xe698e101, 0xfd93, 0x4e6c, { 0xaf, 0x2, 0xae, 0xc7, 0xe8, 0x63, 0x1d, 0x8e } }; - -// {E4026686-02F9-4805-A3FD-2EECA655A92C} -static const GUID guid_openmpt_channels = -{ 0xe4026686, 0x2f9, 0x4805, { 0xa3, 0xfd, 0x2e, 0xec, 0xa6, 0x55, 0xa9, 0x2c } }; - -// {7C845F81-9BA3-4a9a-9C94-C7056DFD1B57} -static const GUID guid_openmpt_gain = -{ 0x7c845f81, 0x9ba3, 0x4a9a, { 0x9c, 0x94, 0xc7, 0x5, 0x6d, 0xfd, 0x1b, 0x57 } }; - -// {EDB0E1E5-BD2E-475c-B2FB-8448C92F6F29} -static const GUID guid_openmpt_stereo = -{ 0xedb0e1e5, 0xbd2e, 0x475c, { 0xb2, 0xfb, 0x84, 0x48, 0xc9, 0x2f, 0x6f, 0x29 } }; - -// {9115A820-67F5-4d0a-B0FB-D312F7FBBAFF} -static const GUID guid_openmpt_repeat = -{ 0x9115a820, 0x67f5, 0x4d0a, { 0xb0, 0xfb, 0xd3, 0x12, 0xf7, 0xfb, 0xba, 0xff } }; - -// {EAAD5E60-F75B-4071-B308-9956362ECB69} -static const GUID guid_openmpt_filter = -{ 0xeaad5e60, 0xf75b, 0x4071, { 0xb3, 0x8, 0x99, 0x56, 0x36, 0x2e, 0xcb, 0x69 } }; - -// {0CF7E156-44E3-4587-A727-864B045BEDDD} -static const GUID guid_openmpt_amiga = -{ 0x0cf7e156, 0x44e3, 0x4587,{ 0xa7, 027, 0x86, 0x4b, 0x04, 0x5b, 0xed, 0xdd } }; - -// {497BF503-D825-4A02-BE5C-02DB8911B1DC} -static const GUID guid_openmpt_ramping = -{ 0x497bf503, 0xd825, 0x4a02, { 0xbe, 0x5c, 0x2, 0xdb, 0x89, 0x11, 0xb1, 0xdc } }; - - -static advconfig_branch_factory g_advconfigBranch("OpenMPT Component", guid_openmpt_root, advconfig_branch::guid_branch_decoding, 0); - -static advconfig_integer_factory cfg_samplerate("Samplerate [6000..96000] (Hz)" , guid_openmpt_samplerate, guid_openmpt_root, 0, 48000, 6000, 96000); -static advconfig_integer_factory cfg_channels ("Channels [1=mono, 2=stereo, 4=quad]" , guid_openmpt_channels , guid_openmpt_root, 0, 2, 1, 4); -static advconfig_string_factory_MT cfg_gain ("Gain [-12...12] (dB)" , guid_openmpt_gain , guid_openmpt_root, 0, "0.0"); -static advconfig_integer_factory cfg_stereo ("Stereo separation [0...200] (%)" , guid_openmpt_stereo , guid_openmpt_root, 0, 100, 0, 200); -static advconfig_string_factory_MT cfg_repeat ("Repeat [0=never, -1=forever, 1..] (#)" , guid_openmpt_repeat , guid_openmpt_root, 0, "0"); -static advconfig_integer_factory cfg_filter ("Interpolation filter length [1=nearest, 2=linear, 4=cubic, 8=sinc]", guid_openmpt_filter , guid_openmpt_root, 0, 8, 1, 8); -static advconfig_checkbox_factory cfg_amiga ("Use Amiga Resampler for Amiga modules" , guid_openmpt_amiga, guid_openmpt_root, 0, false); -static advconfig_string_factory_MT cfg_ramping ("Volume ramping [-1=default, 0=off, 1..10] (ms)" , guid_openmpt_ramping , guid_openmpt_root, 0, "-1"); - -struct foo_openmpt_settings { - int samplerate; - int channels; - int mastergain_millibel; - int stereoseparation; - int repeatcount; - int interpolationfilterlength; - int ramping; - bool use_amiga_resampler; - foo_openmpt_settings() { - - /* - samplerate = 48000; - channels = 2; - mastergain_millibel = 0; - stereoseparation = 100; - repeatcount = 0; - interpolationfilterlength = 8; - use_amiga_resampler = false; - ramping = -1; - */ - - pfc::string8 tmp; - - samplerate = static_cast( cfg_samplerate.get() ); - - channels = static_cast( cfg_channels.get() ); - if ( channels == 3 ) { - channels = 2; - } - - cfg_gain.get( tmp ); - mastergain_millibel = static_cast( pfc::string_to_float( tmp ) * 100.0 ); - - stereoseparation = static_cast( cfg_stereo.get() ); - - cfg_repeat.get( tmp ); - repeatcount = static_cast( pfc::atoi64_ex( tmp, ~0 ) ); - if ( repeatcount < -1 ) { - repeatcount = 0; - } - - interpolationfilterlength = static_cast( cfg_filter.get() ); - - use_amiga_resampler = cfg_amiga.get(); - - cfg_ramping.get( tmp ); - ramping = static_cast( pfc::atoi64_ex( tmp, ~0 ) ); - if ( ramping < -1 ) { - ramping = -1; - } - - } -}; - - - -// 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 ) { - throw exception_io_unsupported_format(); // our input does not support retagging. - } - m_file = p_filehint; // p_filehint may be null, hence next line - input_open_file_helper(m_file,p_path,p_reason,p_abort); // if m_file is null, opens file with appropriate privileges for our operation (read/write for writing tags, read-only otherwise). - if ( m_file->get_size( p_abort ) >= (std::numeric_limits::max)() ) { - throw exception_io_unsupported_format(); - } - std::vector data( static_cast( m_file->get_size( p_abort ) ) ); - m_file->read( data.data(), data.size(), p_abort ); - try { - std::map< std::string, std::string > ctls; - ctls["seek.sync_samples"] = "1"; - mod = new openmpt::module( data, std::clog, ctls ); - } catch ( std::exception & /*e*/ ) { - throw exception_io_data(); - } - settings = foo_openmpt_settings(); - mod->set_repeat_count( settings.repeatcount ); - mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, settings.mastergain_millibel ); - mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, settings.stereoseparation ); - mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, settings.interpolationfilterlength ); - mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, settings.ramping ); - mod->ctl_set( "render.resampler.emulate_amiga", settings.use_amiga_resampler ? "1" : "0" ); - } - void get_info(file_info & p_info,abort_callback & p_abort) { - p_info.set_length( mod->get_duration_seconds() ); - p_info.info_set_int( "samplerate", settings.samplerate ); - p_info.info_set_int( "channels", settings.channels ); - p_info.info_set_int( "bitspersample", 32 ); - std::vector keys = mod->get_metadata_keys(); - for ( std::vector::iterator key = keys.begin(); key != keys.end(); ++key ) { - if ( *key == "message_raw" ) { - continue; - } - p_info.meta_set( (*key).c_str(), mod->get_metadata( *key ).c_str() ); - } - } - t_filestats get_file_stats(abort_callback & p_abort) { - return m_file->get_stats(p_abort); - } - void decode_initialize(unsigned p_flags,abort_callback & p_abort) { - m_file->reopen(p_abort); // equivalent to seek to zero, except it also works on nonseekable streams - } - bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) { - if ( settings.channels == 1 ) { - - std::size_t count = mod->read( settings.samplerate, buffersize, left.data() ); - if ( count == 0 ) { - return false; - } - for ( std::size_t frame = 0; frame < count; frame++ ) { - buffer[frame*1+0] = left[frame]; - } - p_chunk.set_data_32( buffer.data(), count, settings.channels, settings.samplerate ); - return true; - - } else if ( settings.channels == 2 ) { - - std::size_t count = mod->read( settings.samplerate, buffersize, left.data(), right.data() ); - if ( count == 0 ) { - return false; - } - for ( std::size_t frame = 0; frame < count; frame++ ) { - buffer[frame*2+0] = left[frame]; - buffer[frame*2+1] = right[frame]; - } - p_chunk.set_data_32( buffer.data(), count, settings.channels, settings.samplerate ); - return true; - - } else if ( settings.channels == 4 ) { - - std::size_t count = mod->read( settings.samplerate, buffersize, left.data(), right.data(), rear_left.data(), rear_right.data() ); - if ( count == 0 ) { - return false; - } - for ( std::size_t frame = 0; frame < count; frame++ ) { - buffer[frame*4+0] = left[frame]; - buffer[frame*4+1] = right[frame]; - buffer[frame*4+2] = rear_left[frame]; - buffer[frame*4+3] = rear_right[frame]; - } - p_chunk.set_data_32( buffer.data(), count, settings.channels, settings.samplerate ); - return true; - - } else { - return false; - } - - } - void decode_seek(double p_seconds,abort_callback & p_abort) { - mod->set_position_seconds( p_seconds ); - } - bool decode_can_seek() { - return true; - } - bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { // deals with dynamic information such as VBR bitrates - return false; - } - bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) { // deals with dynamic information such as track changes in live streams - return false; - } - void decode_on_idle(abort_callback & p_abort) { - m_file->on_idle( p_abort ); - } - void retag(const file_info & p_info,abort_callback & p_abort) { - throw exception_io_unsupported_format(); - } - static bool g_is_our_content_type(const char * p_content_type) { // match against supported mime types here - return false; - } - static bool g_is_our_path(const char * p_path,const char * p_extension) { - if ( !p_extension ) { - return false; - } - std::vector extensions = openmpt::get_supported_extensions(); - std::string ext = p_extension; - std::transform( ext.begin(), ext.end(), ext.begin(), tolower ); - return std::find( extensions.begin(), extensions.end(), ext ) != extensions.end(); - } - 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; - openmpt::module * mod; - std::vector left; - std::vector right; - 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; } -}; - -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; -}; - -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 f939860a7..1b752106c 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp @@ -165,7 +165,21 @@ static void apply_options() { self->mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, self->settings.stereoseparation ); self->mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, self->settings.interpolationfilterlength ); self->mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, self->settings.ramping ); - self->mod->ctl_set( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? "1" : "0" ); + self->mod->ctl_set_boolean( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? true : false ); + switch ( self->settings.amiga_filter_type ) { + case 0: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "auto" ); + break; + case 1: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "unfiltered" ); + break; + case 0xA500: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a500" ); + break; + case 0xA1200: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a1200" ); + break; + } } self->settings.save(); } @@ -196,7 +210,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-2019 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2020 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; @@ -225,7 +239,7 @@ static void quit() { } } -static int isourfile( const in_char * fn ) { +static int isourfile( const in_char * /* fn */ ) { return 0; } @@ -358,7 +372,7 @@ static void getfileinfo( const in_char * filename, in_char * title, int * length } } -static void eq_set( int on, char data[10], int preamp ) { +static void eq_set( int /* on */ , char /* data */ [10], int /* preamp */ ) { return; } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h index d41324885..5451483e9 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h @@ -579,7 +579,7 @@ LIBOPENMPT_API size_t openmpt_probe_file_header_get_recommended_size(void); /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * - * \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param data Beginning of the file data. * \param size Size of the beginning of the file data. * \param filesize Full size of the file data on disk. @@ -604,7 +604,7 @@ LIBOPENMPT_API size_t openmpt_probe_file_header_get_recommended_size(void); LIBOPENMPT_API int openmpt_probe_file_header( uint64_t flags, const void * data, size_t size, uint64_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ); /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * - * \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param data Beginning of the file data. * \param size Size of the beginning of the file data. * \param logfunc Logging function where warning and errors are written. May be NULL. @@ -630,7 +630,7 @@ LIBOPENMPT_API int openmpt_probe_file_header_without_filesize( uint64_t flags, c /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * - * \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param stream_callbacks Input stream callback operations. * \param stream Input stream to scan. * \param logfunc Logging function where warning and errors are written. May be NULL. @@ -1140,6 +1140,14 @@ LIBOPENMPT_API const char * openmpt_module_get_metadata_keys( openmpt_module * m */ LIBOPENMPT_API const char * openmpt_module_get_metadata( openmpt_module * mod, const char * key ); +/*! Get the current estimated beats per minute (BPM). + * + * \param mod The module handle to work on. + * \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. + * \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. + * \return The current estimated BPM. + */ +LIBOPENMPT_API double openmpt_module_get_current_estimated_bpm( openmpt_module * mod ); /*! \brief Get the current speed * * \param mod The module handle to work on. @@ -1396,35 +1404,79 @@ LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmp * \param mod The module handle to work on. * \return A semicolon-separated list containing all supported ctl keys. * \remarks Currently supported ctl values are: - * - load.skip_samples: Set to "1" to avoid loading samples into memory - * - load.skip_patterns: Set to "1" to avoid loading patterns into memory - * - load.skip_plugins: Set to "1" to avoid loading plugins - * - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - * - seek.sync_samples: Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. - * - subsong: The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. - * - play.at_end: Chooses the behaviour when the end of song is reached: + * - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory + * - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory + * - load.skip_plugins (boolean): Set to "1" to avoid loading plugins + * - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. + * - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. + * - subsong (integer): The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. + * - play.at_end (text): Chooses the behaviour when the end of song is reached: * - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. * - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. * - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - * - play.tempo_factor: 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: + * - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. + * - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. + * - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + * - render.resampler.emulate_amiga_type (string): Configures the filter type to use for the Amiga resampler. Supported values are: + * - "auto": Filter type is chosen by the library and might change. This is the default. + * - "a500": Amiga A500 filter. + * - "a1200": Amiga A1200 filter. + * - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. + * - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. + * - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: * - 0: No dithering. * - 1: Default mode. Chosen by OpenMPT code, might change. * - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker). * - 3: Rectangular, 1 bit depth, simple 1st order noise shaping */ LIBOPENMPT_API const char * openmpt_module_get_ctls( openmpt_module * mod ); + /*! \brief Get current ctl value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be retrieved. * \return The associated ctl value, or NULL on failure. * \sa openmpt_module_get_ctls + * \deprecated Please use openmpt_module_ctl_get_boolean(), openmpt_module_ctl_get_integer(), openmpt_module_ctl_get_floatingpoint(), or openmpt_module_ctl_get_text(). */ -LIBOPENMPT_API const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ); +LIBOPENMPT_API LIBOPENMPT_DEPRECATED const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl boolean value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl integer value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl floatingpoint value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl string value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl ); + /*! \brief Set ctl value * * \param mod The module handle to work on. @@ -1432,8 +1484,49 @@ LIBOPENMPT_API const char * openmpt_module_ctl_get( openmpt_module * mod, const * \param value The value that should be set. * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. * \sa openmpt_module_get_ctls + * \deprecated Please use openmpt_module_ctl_set_boolean(), openmpt_module_ctl_set_integer(), openmpt_module_ctl_set_floatingpoint(), or openmpt_module_ctl_set_text(). */ -LIBOPENMPT_API int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value ); +LIBOPENMPT_API LIBOPENMPT_DEPRECATED int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value ); +/*! \brief Set ctl boolean value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value ); +/*! \brief Set ctl integer value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value ); +/*! \brief Set ctl floatingpoint value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value ); +/*! \brief Set ctl string value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value ); /* remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp index f16ccd6e5..0e5bff0b1 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp @@ -17,8 +17,10 @@ #include #include #include +#include #include +#include #include /*! @@ -174,19 +176,19 @@ LIBOPENMPT_CXX_API std::uint32_t get_core_version(); namespace string { //! Return a verbose library version string from openmpt::string::get(). \deprecated Please use `"library_version"` directly. -LIBOPENMPT_DEPRECATED static const char library_version LIBOPENMPT_ATTR_DEPRECATED [] = "library_version"; +static const char library_version LIBOPENMPT_ATTR_DEPRECATED [] = "library_version"; //! Return a verbose library features string from openmpt::string::get(). \deprecated Please use `"library_features"` directly. -LIBOPENMPT_DEPRECATED static const char library_features LIBOPENMPT_ATTR_DEPRECATED [] = "library_features"; +static const char library_features LIBOPENMPT_ATTR_DEPRECATED [] = "library_features"; //! Return a verbose OpenMPT core version string from openmpt::string::get(). \deprecated Please use `"core_version"` directly. -LIBOPENMPT_DEPRECATED static const char core_version LIBOPENMPT_ATTR_DEPRECATED [] = "core_version"; +static const char core_version LIBOPENMPT_ATTR_DEPRECATED [] = "core_version"; //! Return information about the current build (e.g. the build date or compiler used) from openmpt::string::get(). \deprecated Please use `"build"` directly. -LIBOPENMPT_DEPRECATED static const char build LIBOPENMPT_ATTR_DEPRECATED [] = "build"; +static const char build LIBOPENMPT_ATTR_DEPRECATED [] = "build"; //! Return all contributors from openmpt::string::get(). \deprecated Please use `"credits"` directly. -LIBOPENMPT_DEPRECATED static const char credits LIBOPENMPT_ATTR_DEPRECATED [] = "credits"; +static const char credits LIBOPENMPT_ATTR_DEPRECATED [] = "credits"; //! Return contact information about libopenmpt from openmpt::string::get(). \deprecated Please use `"contact"` directly. -LIBOPENMPT_DEPRECATED static const char contact LIBOPENMPT_ATTR_DEPRECATED [] = "contact"; +static const char contact LIBOPENMPT_ATTR_DEPRECATED [] = "contact"; //! Return the libopenmpt license from openmpt::string::get(). \deprecated Please use `"license"` directly. -LIBOPENMPT_DEPRECATED static const char license LIBOPENMPT_ATTR_DEPRECATED [] = "license"; +static const char license LIBOPENMPT_ATTR_DEPRECATED [] = "license"; //! Get library related metadata. /*! @@ -231,8 +233,16 @@ LIBOPENMPT_CXX_API std::vector get_supported_extensions(); /*! \param extension file extension to query without a leading dot. The case is ignored. \return true if the extension is supported by libopenmpt, false otherwise. + \deprecated Please use openmpt::is_extension_supported2(). */ -LIBOPENMPT_CXX_API bool is_extension_supported( const std::string & extension ); +LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API bool is_extension_supported( const std::string & extension ); +//! Query whether a file extension is supported +/*! + \param extension file extension to query without a leading dot. The case is ignored. + \return true if the extension is supported by libopenmpt, false otherwise. + \since 0.5.0 +*/ +LIBOPENMPT_CXX_API bool is_extension_supported2( std::string_view extension ); //! Roughly scan the input stream to find out whether libopenmpt might be able to open it /*! @@ -253,7 +263,7 @@ LIBOPENMPT_CXX_API double could_open_probability( std::istream & stream, double /*! \deprecated Please use openmpt::could_open_probability(). */ -LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API LIBOPENMPT_DEPRECATED double could_open_propability( std::istream & stream, double effort = 1.0, std::ostream & log = std::clog ); +LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API double could_open_propability( std::istream & stream, double effort = 1.0, std::ostream & log = std::clog ); //! Get recommended header size for successfull format probing /*! @@ -283,7 +293,23 @@ enum probe_file_header_result { //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! - \param flags Ored mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param data Beginning of the file data. + \param size Size of the beginning of the file data. + \param filesize Full size of the file data on disk. + \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. + \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. + \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. + \retval probe_file_header_result_failure The file is not supported by libopenmpt. + \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. + \sa openmpt::probe_file_header_get_recommended_size() + \sa openmpt::could_open_probability() + \since 0.5.0 +*/ +LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ); +//! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it +/*! + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param filesize Full size of the file data on disk. @@ -300,7 +326,23 @@ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::uint8_ //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! - \param flags Ored mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param data Beginning of the file data. + \param size Size of the beginning of the file data. + \remarks It is recommended to use the overload of this function that also takes the filesize as parameter if at all possile. libopenmpt can provide more accurate answers if the filesize is known. + \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size to the file's size. + \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. + \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. + \retval probe_file_header_result_failure The file is not supported by libopenmpt. + \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. + \sa openmpt::probe_file_header_get_recommended_size() + \sa openmpt::could_open_probability() + \since 0.5.0 +*/ +LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ); +//! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it +/*! + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. \param data Beginning of the file data. \param size Size of the beginning of the file data. \remarks It is recommended to use the overload of this function that also takes the filesize as parameter if at all possile. libopenmpt can provide more accurate answers if the filesize is known. @@ -317,7 +359,7 @@ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::uint8_ //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! - \param flags Ored mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. \param stream Input stream to scan. \remarks stream is left in an unspecified state when this function returns. \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. @@ -417,6 +459,38 @@ public: \sa \ref libopenmpt_cpp_fileio */ module( std::istream & stream, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + /*! + \param data Data to load the module from. + \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. + \param ctls A map of initial ctl values, see openmpt::module::get_ctls. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. + \remarks The input data can be discarded after an openmpt::module has been constructed successfully. + \sa \ref libopenmpt_cpp_fileio + \since 0.5.0 + */ + module( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + /*! + \param beg Begin of data to load the module from. + \param end End of data to load the module from. + \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. + \param ctls A map of initial ctl values, see openmpt::module::get_ctls. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. + \remarks The input data can be discarded after an openmpt::module has been constructed successfully. + \sa \ref libopenmpt_cpp_fileio + \since 0.5.0 + */ + module( const std::byte * beg, const std::byte * end, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + /*! + \param data Data to load the module from. + \param size Amount of data available. + \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. + \param ctls A map of initial ctl values, see openmpt::module::get_ctls. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. + \remarks The input data can be discarded after an openmpt::module has been constructed successfully. + \sa \ref libopenmpt_cpp_fileio + \since 0.5.0 + */ + module( const std::byte * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. @@ -741,6 +815,13 @@ public: */ std::string get_metadata( const std::string & key ) const; + //! Get the current estimated beats per minute (BPM). + /*! + \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. + \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. + \return The current estimated BPM. + */ + double get_current_estimated_bpm() const; //! Get the current speed /*! \return The current speed in ticks per row. @@ -962,21 +1043,26 @@ public: /*! \return A vector containing all supported ctl keys. \remarks Currently supported ctl values are: - - load.skip_samples: Set to "1" to avoid loading samples into memory - - load.skip_patterns: Set to "1" to avoid loading patterns into memory - - load.skip_plugins: Set to "1" to avoid loading plugins - - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - - seek.sync_samples: Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row. - - subsong: The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong. - - play.at_end: Chooses the behaviour when the end of song is reached: + - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory + - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory + - load.skip_plugins (boolean): Set to "1" to avoid loading plugins + - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. + - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row. + - subsong (integer): The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong. + - play.at_end (text): Chooses the behaviour when the end of song is reached: - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - - play.tempo_factor: 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: + - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. + - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. + - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + - render.resampler.emulate_amiga_type (string): Configures the filter type to use for the Amiga resampler. Supported values are: + - "auto": Filter type is chosen by the library and might change. This is the default. + - "a500": Amiga A500 filter. + - "a1200": Amiga A1200 filter. + - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. + - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. + - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt::module::read. Supported values are: - 0: No dithering. - 1: Default mode. Chosen by OpenMPT code, might change. - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker). @@ -991,16 +1077,87 @@ public: \param ctl The ctl key whose value should be retrieved. \return The associated ctl value. \sa openmpt::module::get_ctls + \deprecated Please use openmpt::module::ctl_get_boolean(), openmpt::module::ctl_get_integer(), openmpt::module::ctl_get_floatingpoint(), or openmpt::module::ctl_get_text(). */ - std::string ctl_get( const std::string & ctl ) const; + LIBOPENMPT_ATTR_DEPRECATED std::string ctl_get( const std::string & ctl ) const; + //! Get current ctl boolean value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + bool ctl_get_boolean( std::string_view ctl ) const; + //! Get current ctl integer value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + std::int64_t ctl_get_integer( std::string_view ctl ) const; + //! Get current ctl floatingpoint value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + double ctl_get_floatingpoint( std::string_view ctl ) const; + //! Get current ctl text value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + std::string ctl_get_text( std::string_view ctl ) const; + //! Set ctl value /*! \param ctl The ctl key whose value should be set. \param value The value that should be set. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \sa openmpt::module::get_ctls + \deprecated Please use openmpt::module::ctl_set_bool(), openmpt::module::ctl_set_int(), openmpt::module::ctl_set_float(), or openmpt::module::ctl_set_string(). */ - void ctl_set( const std::string & ctl, const std::string & value ); + LIBOPENMPT_ATTR_DEPRECATED void ctl_set( const std::string & ctl, const std::string & value ); + //! Set ctl boolean value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_boolean( std::string_view ctl, bool value ); + //! Set ctl integer value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_integer( std::string_view ctl, std::int64_t value ); + //! Set ctl floatingpoint value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_floatingpoint( std::string_view ctl, double value ); + //! Set ctl text value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_text( std::string_view ctl, std::string_view value ); // remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp index 845e643bf..07f3d079e 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp @@ -285,7 +285,7 @@ uint32_t openmpt_get_library_version(void) { try { return openmpt::get_library_version(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -294,7 +294,7 @@ uint32_t openmpt_get_core_version(void) { try { return openmpt::get_core_version(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -303,7 +303,7 @@ void openmpt_free_string( const char * str ) { try { std::free( const_cast< char * >( str ) ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return; } @@ -315,7 +315,7 @@ const char * openmpt_get_string( const char * key ) { } return openmpt::strdup( openmpt::string::get( key ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return NULL; } @@ -335,7 +335,7 @@ const char * openmpt_get_supported_extensions(void) { } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return NULL; } @@ -347,7 +347,7 @@ int openmpt_is_extension_supported( const char * extension ) { } return openmpt::module_impl::is_extension_supported( extension ) ? 1 : 0; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -468,7 +468,7 @@ double openmpt_could_open_probability2( openmpt_stream_callbacks stream_callback openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; return openmpt::module_impl::could_open_probability( istream, effort, openmpt::helper::make_unique( logfunc ? logfunc : openmpt_log_func_default, loguser ) ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return 0.0; } @@ -477,7 +477,7 @@ size_t openmpt_probe_file_header_get_recommended_size(void) { try { return openmpt::module_impl::probe_file_header_get_recommended_size(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -486,7 +486,7 @@ int openmpt_probe_file_header( uint64_t flags, const void * data, size_t size, u try { return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } @@ -495,7 +495,7 @@ int openmpt_probe_file_header_without_filesize( uint64_t flags, const void * dat try { return openmpt::module_impl::probe_file_header( flags, data, size ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } @@ -505,7 +505,7 @@ int openmpt_probe_file_header_from_stream( uint64_t flags, openmpt_stream_callba openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; return openmpt::module_impl::probe_file_header( flags, istream ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } @@ -543,7 +543,14 @@ openmpt_module * openmpt_module_create2( openmpt_stream_callbacks stream_callbac mod->impl = new openmpt::module_impl( istream, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); return mod; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod'. + #endif // _MSC_VER + openmpt::report_exception( __func__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER } delete mod->impl; mod->impl = 0; @@ -554,7 +561,7 @@ openmpt_module * openmpt_module_create2( openmpt_stream_callbacks stream_callbac std::free( (void*)mod ); mod = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -591,7 +598,14 @@ openmpt_module * openmpt_module_create_from_memory2( const void * filedata, size mod->impl = new openmpt::module_impl( filedata, filesize, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); return mod; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod'. + #endif // _MSC_VER + openmpt::report_exception( __func__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER } delete mod->impl; mod->impl = 0; @@ -602,7 +616,7 @@ openmpt_module * openmpt_module_create_from_memory2( const void * filedata, size std::free( (void*)mod ); mod = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -620,7 +634,7 @@ void openmpt_module_destroy( openmpt_module * mod ) { mod = NULL; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -632,7 +646,7 @@ void openmpt_module_set_log_func( openmpt_module * mod, openmpt_log_func logfunc mod->loguser = loguser; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -645,7 +659,7 @@ void openmpt_module_set_error_func( openmpt_module * mod, openmpt_error_func err mod->error = OPENMPT_ERROR_OK; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -655,7 +669,7 @@ int openmpt_module_error_get_last( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->error; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return -1; } @@ -665,7 +679,7 @@ const char * openmpt_module_error_get_last_message( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->error_message ? openmpt::strdup( mod->error_message ) : openmpt::strdup( "" ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -680,7 +694,7 @@ void openmpt_module_error_set_last( openmpt_module * mod, int error ) { } return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -695,7 +709,7 @@ void openmpt_module_error_clear( openmpt_module * mod ) { } return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -706,7 +720,7 @@ int openmpt_module_select_subsong( openmpt_module * mod, int32_t subsong ) { mod->impl->select_subsong( subsong ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -716,7 +730,7 @@ int32_t openmpt_module_get_selected_subsong( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_selected_subsong(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return -1; } @@ -727,7 +741,7 @@ int openmpt_module_set_repeat_count( openmpt_module * mod, int32_t repeat_count mod->impl->set_repeat_count( repeat_count ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -736,7 +750,7 @@ int32_t openmpt_module_get_repeat_count( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_repeat_count(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -746,7 +760,7 @@ double openmpt_module_get_duration_seconds( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_duration_seconds(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -756,7 +770,7 @@ double openmpt_module_set_position_seconds( openmpt_module * mod, double seconds openmpt::interface::check_soundfile( mod ); return mod->impl->set_position_seconds( seconds ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -765,7 +779,7 @@ double openmpt_module_get_position_seconds( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_position_seconds(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -775,7 +789,7 @@ double openmpt_module_set_position_order_row( openmpt_module * mod, int32_t orde openmpt::interface::check_soundfile( mod ); return mod->impl->set_position_order_row( order, row ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -787,7 +801,7 @@ int openmpt_module_get_render_param( openmpt_module * mod, int param, int32_t * *value = mod->impl->get_render_param( (openmpt::module::render_param)param ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -797,7 +811,7 @@ int openmpt_module_set_render_param( openmpt_module * mod, int param, int32_t va mod->impl->set_render_param( (openmpt::module::render_param)param, value ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -807,7 +821,7 @@ size_t openmpt_module_read_mono( openmpt_module * mod, int32_t samplerate, size_ openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, mono ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -816,7 +830,7 @@ size_t openmpt_module_read_stereo( openmpt_module * mod, int32_t samplerate, siz openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -825,7 +839,7 @@ size_t openmpt_module_read_quad( openmpt_module * mod, int32_t samplerate, size_ openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right, rear_left, rear_right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -834,7 +848,7 @@ size_t openmpt_module_read_float_mono( openmpt_module * mod, int32_t samplerate, openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, mono ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -843,7 +857,7 @@ size_t openmpt_module_read_float_stereo( openmpt_module * mod, int32_t samplerat openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -852,7 +866,7 @@ size_t openmpt_module_read_float_quad( openmpt_module * mod, int32_t samplerate, openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right, rear_left, rear_right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -861,7 +875,7 @@ size_t openmpt_module_read_interleaved_stereo( openmpt_module * mod, int32_t sam openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -870,7 +884,7 @@ size_t openmpt_module_read_interleaved_quad( openmpt_module * mod, int32_t sampl openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -879,7 +893,7 @@ size_t openmpt_module_read_interleaved_float_stereo( openmpt_module * mod, int32 openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -888,7 +902,7 @@ size_t openmpt_module_read_interleaved_float_quad( openmpt_module * mod, int32_t openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -909,7 +923,7 @@ const char * openmpt_module_get_metadata_keys( openmpt_module * mod ) { } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -919,168 +933,177 @@ const char * openmpt_module_get_metadata( openmpt_module * mod, const char * key openmpt::interface::check_pointer( key ); return openmpt::strdup( mod->impl->get_metadata( key ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API int32_t openmpt_module_get_current_speed( openmpt_module * mod ) { +double openmpt_module_get_current_estimated_bpm( openmpt_module * mod ) { + try { + openmpt::interface::check_soundfile( mod ); + return mod->impl->get_current_estimated_bpm(); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0.0; +} +int32_t openmpt_module_get_current_speed( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_speed(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_tempo( openmpt_module * mod ) { +int32_t openmpt_module_get_current_tempo( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_tempo(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_order( openmpt_module * mod ) { +int32_t openmpt_module_get_current_order( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_order(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_pattern( openmpt_module * mod ) { +int32_t openmpt_module_get_current_pattern( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_pattern(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_row( openmpt_module * mod ) { +int32_t openmpt_module_get_current_row( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_row(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_playing_channels( openmpt_module * mod ) { +int32_t openmpt_module_get_current_playing_channels( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_playing_channels(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_mono( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_mono( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_mono( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_left( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_left( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_left( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_right( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_right( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_right( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_rear_left( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_rear_left( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_rear_left( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_rear_right( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_rear_right( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_rear_right( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_subsongs( openmpt_module * mod ) { +int32_t openmpt_module_get_num_subsongs( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_subsongs(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_channels( openmpt_module * mod ) { +int32_t openmpt_module_get_num_channels( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_channels(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_orders( openmpt_module * mod ) { +int32_t openmpt_module_get_num_orders( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_orders(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_patterns( openmpt_module * mod ) { +int32_t openmpt_module_get_num_patterns( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_patterns(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_instruments( openmpt_module * mod ) { +int32_t openmpt_module_get_num_instruments( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_instruments(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_samples( openmpt_module * mod ) { +int32_t openmpt_module_get_num_samples( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_samples(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_get_subsong_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_subsong_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_subsong_names(); @@ -1092,11 +1115,11 @@ LIBOPENMPT_API const char * openmpt_module_get_subsong_name( openmpt_module * mo } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_channel_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_channel_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_channel_names(); @@ -1108,11 +1131,11 @@ LIBOPENMPT_API const char * openmpt_module_get_channel_name( openmpt_module * mo } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_order_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_order_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_order_names(); @@ -1124,11 +1147,11 @@ LIBOPENMPT_API const char * openmpt_module_get_order_name( openmpt_module * mod, } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_pattern_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_pattern_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_pattern_names(); @@ -1140,11 +1163,11 @@ LIBOPENMPT_API const char * openmpt_module_get_pattern_name( openmpt_module * mo } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_instrument_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_instrument_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_instrument_names(); @@ -1156,11 +1179,11 @@ LIBOPENMPT_API const char * openmpt_module_get_instrument_name( openmpt_module * } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_sample_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_sample_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_sample_names(); @@ -1172,77 +1195,77 @@ LIBOPENMPT_API const char * openmpt_module_get_sample_name( openmpt_module * mod } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API int32_t openmpt_module_get_order_pattern( openmpt_module * mod, int32_t order ) { +int32_t openmpt_module_get_order_pattern( openmpt_module * mod, int32_t order ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_order_pattern( order ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_pattern_num_rows( openmpt_module * mod, int32_t pattern ) { +int32_t openmpt_module_get_pattern_num_rows( openmpt_module * mod, int32_t pattern ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_pattern_num_rows( pattern ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { +uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_pattern_row_channel_command( pattern, row, channel, command ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { +const char * openmpt_module_format_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->format_pattern_row_channel_command( pattern, row, channel, command ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { +const char * openmpt_module_highlight_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->highlight_pattern_row_channel_command( pattern, row, channel, command ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { +const char * openmpt_module_format_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->format_pattern_row_channel( pattern, row, channel, width, pad ? true : false ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { +const char * openmpt_module_highlight_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->highlight_pattern_row_channel( pattern, row, channel, width, pad ? true : false ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -1263,7 +1286,7 @@ const char * openmpt_module_get_ctls( openmpt_module * mod ) { } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -1274,7 +1297,47 @@ const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ) { openmpt::interface::check_pointer( ctl ); return openmpt::strdup( mod->impl->ctl_get( ctl ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); + } + return NULL; +} +int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return mod->impl->ctl_get_boolean( ctl ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return mod->impl->ctl_get_integer( ctl ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return mod->impl->ctl_get_floatingpoint( ctl ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0.0; +} +const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return openmpt::strdup( mod->impl->ctl_get_text( ctl ).c_str() ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -1287,11 +1350,55 @@ int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * mod->impl->ctl_set( ctl, value ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + mod->impl->ctl_set_boolean( ctl, value ? true : false ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + mod->impl->ctl_set_integer( ctl, value ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + mod->impl->ctl_set_floatingpoint( ctl, value ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + openmpt::interface::check_pointer( value ); + mod->impl->ctl_set_text( ctl, value ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); } return 0; } - openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ) { try { @@ -1326,9 +1433,16 @@ openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_ mod->impl = mod_ext->impl; return mod_ext; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + openmpt::report_exception( __func__, mod, error, error_message ); } - delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod_ext'. + #endif // _MSC_VER + delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER mod_ext->impl = 0; mod->impl = 0; if ( mod->error_message ) { @@ -1338,7 +1452,7 @@ openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_ std::free( (void*)mod_ext ); mod_ext = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -1375,9 +1489,16 @@ openmpt_module_ext * openmpt_module_ext_create_from_memory( const void * filedat mod->impl = mod_ext->impl; return mod_ext; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + openmpt::report_exception( __func__, mod, error, error_message ); } - delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod_ext'. + #endif // _MSC_VER + delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER mod_ext->impl = 0; mod->impl = 0; if ( mod->error_message ) { @@ -1387,7 +1508,7 @@ openmpt_module_ext * openmpt_module_ext_create_from_memory( const void * filedat std::free( (void*)mod_ext ); mod_ext = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -1407,7 +1528,7 @@ void openmpt_module_ext_destroy( openmpt_module_ext * mod_ext ) { mod_ext = NULL; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return; } @@ -1418,7 +1539,7 @@ openmpt_module * openmpt_module_ext_get_module( openmpt_module_ext * mod_ext ) { openmpt_module * mod = &mod_ext->mod; return mod; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return NULL; } @@ -1430,7 +1551,7 @@ static int get_pattern_row_channel_volume_effect_type( openmpt_module_ext * mod_ openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pattern_row_channel_volume_effect_type( pattern, row, channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } @@ -1439,7 +1560,7 @@ static int get_pattern_row_channel_effect_type( openmpt_module_ext * mod_ext, in openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pattern_row_channel_effect_type( pattern, row, channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } @@ -1452,7 +1573,7 @@ int set_current_speed( openmpt_module_ext * mod_ext, int32_t speed ) { mod_ext->impl->set_current_speed( speed ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1462,7 +1583,7 @@ int set_current_tempo( openmpt_module_ext * mod_ext, int32_t tempo ) { mod_ext->impl->set_current_tempo( tempo ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1472,7 +1593,7 @@ int set_tempo_factor( openmpt_module_ext * mod_ext, double factor ) { mod_ext->impl->set_tempo_factor( factor ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1481,7 +1602,7 @@ double get_tempo_factor( openmpt_module_ext * mod_ext ) { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_tempo_factor(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } @@ -1491,7 +1612,7 @@ int set_pitch_factor( openmpt_module_ext * mod_ext, double factor ) { mod_ext->impl->set_pitch_factor( factor ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1500,7 +1621,7 @@ double get_pitch_factor( openmpt_module_ext * mod_ext ) { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pitch_factor(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } @@ -1510,7 +1631,7 @@ int set_global_volume( openmpt_module_ext * mod_ext, double volume ) { mod_ext->impl->set_global_volume( volume ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1519,7 +1640,7 @@ double get_global_volume( openmpt_module_ext * mod_ext ) { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_global_volume(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } @@ -1529,7 +1650,7 @@ int set_channel_volume( openmpt_module_ext * mod_ext, int32_t channel, double vo mod_ext->impl->set_channel_volume( channel, volume ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1538,7 +1659,7 @@ double get_channel_volume( openmpt_module_ext * mod_ext, int32_t channel ) { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_channel_volume( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } @@ -1548,7 +1669,7 @@ int set_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel, int mod_ext->impl->set_channel_mute_status( channel, mute ? true : false ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1557,7 +1678,7 @@ int get_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel ) { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_channel_mute_status( channel ) ? 1 : 0; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } @@ -1567,7 +1688,7 @@ int set_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument mod_ext->impl->set_instrument_mute_status( instrument, mute ? true : false ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1576,7 +1697,7 @@ int get_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_instrument_mute_status( instrument ) ? 1 : 0; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } @@ -1585,7 +1706,7 @@ int32_t play_note( openmpt_module_ext * mod_ext, int32_t instrument, int32_t not openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->play_note( instrument, note, volume, panning ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } @@ -1595,7 +1716,7 @@ int stop_note( openmpt_module_ext * mod_ext, int32_t channel ) { mod_ext->impl->stop_note( channel ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1657,7 +1778,7 @@ int openmpt_module_ext_get_interface( openmpt_module_ext * mod_ext, const char * } return result; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h index 0323ab1e0..01104732b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h @@ -148,6 +148,8 @@ #else #define LIBOPENMPT_DEPRECATED #endif +#else +#define LIBOPENMPT_DEPRECATED #endif #ifndef __cplusplus @@ -164,37 +166,14 @@ LIBOPENMPT_DEPRECATED static const int LIBOPENMPT_DEPRECATED_STRING_CONSTANT = 0 #ifdef __cplusplus -#ifndef LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED -/* handle known broken compilers here by defining LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED appropriately */ -#endif - #if defined(LIBOPENMPT_ASSUME_CPLUSPLUS) -#ifndef LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED -#define LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED LIBOPENMPT_ASSUME_CPLUSPLUS -#endif #endif #if !defined(LIBOPENMPT_NO_DEPRECATE) -#if defined(LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED) -#if (LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED >= 201402L) #define LIBOPENMPT_ATTR_DEPRECATED [[deprecated]] -#undef LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_DEPRECATED #else #define LIBOPENMPT_ATTR_DEPRECATED #endif -#elif (__cplusplus >= 201402L) -#define LIBOPENMPT_ATTR_DEPRECATED [[deprecated]] -#undef LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_DEPRECATED -#else -#define LIBOPENMPT_ATTR_DEPRECATED -#endif -#else -#undef LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_ATTR_DEPRECATED -#endif #endif diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp index e2eac37fc..6f52ec5a6 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp @@ -126,6 +126,9 @@ std::vector get_supported_extensions() { bool is_extension_supported( const std::string & extension ) { return openmpt::module_impl::is_extension_supported( extension ); } +bool is_extension_supported( std::string_view extension ) { + return openmpt::module_impl::is_extension_supported( extension ); +} double could_open_probability( std::istream & stream, double effort, std::ostream & log ) { return openmpt::module_impl::could_open_probability( stream, effort, openmpt::helper::make_unique( log ) ); @@ -137,9 +140,15 @@ double could_open_propability( std::istream & stream, double effort, std::ostrea std::size_t probe_file_header_get_recommended_size() { return openmpt::module_impl::probe_file_header_get_recommended_size(); } +int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ) { + return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); +} int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ) { return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); } +int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ) { + return openmpt::module_impl::probe_file_header( flags, data, size ); +} int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ) { return openmpt::module_impl::probe_file_header( flags, data, size ); } @@ -177,6 +186,18 @@ module::module( std::istream & stream, std::ostream & log, const std::map< std:: impl = new module_impl( stream, openmpt::helper::make_unique( log ), ctls ); } +module::module( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { + impl = new module_impl( data, openmpt::helper::make_unique( log ), ctls ); +} + +module::module( const std::byte * beg, const std::byte * end, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { + impl = new module_impl( beg, end - beg, openmpt::helper::make_unique( log ), ctls ); +} + +module::module( const std::byte * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { + impl = new module_impl( data, size, openmpt::helper::make_unique( log ), ctls ); +} + module::module( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, openmpt::helper::make_unique( log ), ctls ); } @@ -284,6 +305,9 @@ std::string module::get_metadata( const std::string & key ) const { return impl->get_metadata( key ); } +double module::get_current_estimated_bpm() const { + return impl->get_current_estimated_bpm(); +} std::int32_t module::get_current_speed() const { return impl->get_current_speed(); } @@ -385,25 +409,67 @@ std::string module::highlight_pattern_row_channel( std::int32_t pattern, std::in std::vector module::get_ctls() const { return impl->get_ctls(); } + std::string module::ctl_get( const std::string & ctl ) const { return impl->ctl_get( ctl ); } +bool module::ctl_get_boolean( std::string_view ctl ) const { + return impl->ctl_get_boolean( ctl ); +} +std::int64_t module::ctl_get_integer( std::string_view ctl ) const { + return impl->ctl_get_integer( ctl ); +} +double module::ctl_get_floatingpoint( std::string_view ctl ) const { + return impl->ctl_get_floatingpoint( ctl ); +} +std::string module::ctl_get_text( std::string_view ctl ) const { + return impl->ctl_get_text( ctl ); +} + void module::ctl_set( const std::string & ctl, const std::string & value ) { impl->ctl_set( ctl, value ); } +void module::ctl_set_boolean( std::string_view ctl, bool value ) { + impl->ctl_set_boolean( ctl, value ); +} +void module::ctl_set_integer( std::string_view ctl, std::int64_t value ) { + impl->ctl_set_integer( ctl, value ); +} +void module::ctl_set_floatingpoint( std::string_view ctl, double value ) { + impl->ctl_set_floatingpoint( ctl, value ); +} +void module::ctl_set_text( std::string_view ctl, std::string_view value ) { + impl->ctl_set_text( ctl, value ); +} module_ext::module_ext( std::istream & stream, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( stream, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } +module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } +module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} +module_ext::module_ext( const std::uint8_t * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} module_ext::module_ext( const char * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } +module_ext::module_ext( const std::byte * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} module_ext::module_ext( const void * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp index 686eaeeed..87772cf0e 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp @@ -44,7 +44,11 @@ private: void operator = ( const module_ext & ); public: module_ext( std::istream & stream, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::byte * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::uint8_t * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const char * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const void * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); virtual ~module_ext(); diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp index 9301579c2..a231a108c 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp @@ -28,12 +28,18 @@ namespace openmpt { module_ext_impl::module_ext_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( stream, std::move(log), ctls ) { ctor(); } + module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { + ctor(); + } module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { ctor(); } + module_ext_impl::module_ext_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { + ctor(); + } module_ext_impl::module_ext_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { ctor(); } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp index 68fad85cf..7cc0a3a22 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp @@ -33,8 +33,10 @@ class module_ext_impl public: module_ext_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_ext_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp index 1f74f1c7b..3968625e6 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp @@ -41,15 +41,7 @@ OPENMPT_NAMESPACE_BEGIN #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 -#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. -// Try to provoke a warning with an unused variable. -static int Warning_libopenmpt_for_WinRT_is_built_with_reduced_functionality_Please_define_WIN32_WINNT_0x0602; -#endif +MPT_WARNING("Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602.") #endif // _WIN32_WINNT #endif // _WIN32_WINNT #endif // MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT @@ -67,29 +59,17 @@ static int Warning_libopenmpt_for_WinRT_is_built_with_reduced_functionality_Plea #endif // MPT_BUILD_MSVC #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 -#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. -// Try to provoke a warning with an unused variable. -static int Warning_libopenmpt_built_in_non_thread_safe_mode_because_mutexes_are_not_supported_by_the_CPlusPlus_standard_library_available; -#endif +MPT_WARNING("Warning: libopenmpt built in non thread-safe mode because mutexes are not supported by the C++ standard library available.") #endif // MPT_MUTEX_NONE #if (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(MPT_WITH_MINGWSTDTHREADS) -#if MPT_COMPILER_MSVC -#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_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 +MPT_WARNING("Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads.") #endif // MINGW +#if MPT_CLANG_AT_LEAST(5,0,0) && defined(__powerpc__) && !defined(__powerpc64__) +MPT_WARNING("Warning: libopenmpt is known to trigger bad code generation with Clang 5 or later on powerpc (32bit) when using -O3. See .") +#endif + #endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS #if defined(MPT_ASSERT_HANDLER_NEEDED) && !defined(ENABLE_TESTS) @@ -99,12 +79,12 @@ MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *exp if(msg) { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_(")") + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetSource, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetSource, expr) + U_(")") ); } else { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetSource, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) @@ -161,19 +141,19 @@ static std::string get_library_version_string() { } static std::string get_library_features_string() { - return mpt::ToCharset(mpt::CharsetUTF8, mpt::String::Trim(Build::GetBuildFeaturesString())); + return mpt::ToCharset(mpt::Charset::UTF8, mpt::String::Trim(Build::GetBuildFeaturesString())); } static std::string get_core_version_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetVersionStringExtended()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetVersionStringExtended()); } static std::string get_source_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, SourceInfo::Current().GetUrlWithRevision()); + return mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().GetUrlWithRevision()); } static std::string get_source_date_string() { - return mpt::ToCharset(mpt::CharsetUTF8, SourceInfo::Current().Date()); + return mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().Date()); } static std::string get_source_revision_string() { @@ -182,35 +162,35 @@ static std::string get_source_revision_string() { } static std::string get_build_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetBuildDateString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetBuildDateString()); } static std::string get_build_compiler_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetBuildCompilerString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetBuildCompilerString()); } static std::string get_credits_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetFullCreditsString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetFullCreditsString()); } static std::string get_contact_string() { - return mpt::ToCharset(mpt::CharsetUTF8, U_("Forum: ") + Build::GetURL(Build::Url::Forum)); + return mpt::ToCharset(mpt::Charset::UTF8, U_("Forum: ") + Build::GetURL(Build::Url::Forum)); } static std::string get_license_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetLicenseString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetLicenseString()); } static std::string get_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Website)); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetURL(Build::Url::Website)); } static std::string get_support_forum_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Forum)); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetURL(Build::Url::Forum)); } static std::string get_bugtracker_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Bugtracker)); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetURL(Build::Url::Bugtracker)); } std::string get_string( const std::string & key ) { @@ -295,7 +275,7 @@ public: } private: void AddToLog( LogLevel level, const mpt::ustring & text ) const override { - destination.log( mpt::ToCharset( mpt::CharsetUTF8, LogLevelToString( level ) + U_(": ") + text ) ); + destination.log( mpt::ToCharset( mpt::Charset::UTF8, LogLevelToString( level ) + U_(": ") + text ) ); } }; // class log_forwarder @@ -312,14 +292,14 @@ std::vector > loader_log::GetMessages() const { return m_Messages; } void loader_log::AddToLog( LogLevel level, const mpt::ustring & text ) const { - m_Messages.push_back( std::make_pair( level, mpt::ToCharset( mpt::CharsetUTF8, text ) ) ); + m_Messages.push_back( std::make_pair( level, mpt::ToCharset( mpt::Charset::UTF8, text ) ) ); } void module_impl::PushToCSoundFileLog( const std::string & text ) const { - m_sndFile->AddToLog( LogError, mpt::ToUnicode( mpt::CharsetUTF8, text ) ); + m_sndFile->AddToLog( LogError, mpt::ToUnicode( mpt::Charset::UTF8, text ) ); } void module_impl::PushToCSoundFileLog( int loglevel, const std::string & text ) const { - m_sndFile->AddToLog( static_cast( loglevel ), mpt::ToUnicode( mpt::CharsetUTF8, text ) ); + m_sndFile->AddToLog( static_cast( loglevel ), mpt::ToUnicode( mpt::Charset::UTF8, text ) ); } module_impl::subsong_data::subsong_data( double duration, std::int32_t start_row, std::int32_t start_order, std::int32_t sequence ) @@ -369,6 +349,19 @@ static std::int32_t resamplingmode_to_filterlength(ResamplingMode mode) { } } +static Resampling::AmigaFilter translate_amiga_filter_type( module_impl::amiga_filter_type amiga_type ) { + switch (amiga_type ) { + case module_impl::amiga_filter_type::a500: + return Resampling::AmigaFilter::A500; + case module_impl::amiga_filter_type::a1200: + case module_impl::amiga_filter_type::auto_filter: + default: + return Resampling::AmigaFilter::A1200; + case module_impl::amiga_filter_type::unfiltered: + return Resampling::AmigaFilter::Unfiltered; + } +} + static void ramping_to_mixersettings( MixerSettings & settings, int ramping ) { if ( ramping == -1 ) { settings.SetVolumeRampUpMicroseconds( MixerSettings().GetVolumeRampUpMicroseconds() ); @@ -382,7 +375,7 @@ static void ramping_to_mixersettings( MixerSettings & settings, int ramping ) { } } static void mixersettings_to_ramping( int & ramping, const MixerSettings & settings ) { - std::int32_t ramp_us = std::max( settings.GetVolumeRampUpMicroseconds(), settings.GetVolumeRampDownMicroseconds() ); + std::int32_t ramp_us = std::max( settings.GetVolumeRampUpMicroseconds(), settings.GetVolumeRampDownMicroseconds() ); if ( ( settings.GetVolumeRampUpMicroseconds() == MixerSettings().GetVolumeRampUpMicroseconds() ) && ( settings.GetVolumeRampDownMicroseconds() == MixerSettings().GetVolumeRampDownMicroseconds() ) ) { ramping = -1; } else if ( ramp_us <= 0 ) { @@ -393,7 +386,7 @@ static void mixersettings_to_ramping( int & ramping, const MixerSettings & setti } std::string module_impl::mod_string_to_utf8( const std::string & encoded ) const { - return mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetCharsetInternal(), encoded ); + return mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetCharsetInternal(), encoded ); } void module_impl::apply_mixer_settings( std::int32_t samplerate, int channels ) { bool samplerate_changed = static_cast( m_sndFile->m_MixerSettings.gdwMixingFreq ) != samplerate; @@ -440,11 +433,11 @@ bool module_impl::has_subsongs_inited() const { return !m_subsongs.empty(); } void module_impl::ctor( const std::map< std::string, std::string > & ctls ) { - m_sndFile = mpt::make_unique(); + m_sndFile = std::make_unique(); m_loaded = false; m_mixer_initialized = false; - m_Dither = mpt::make_unique( mpt::global_prng() ); - m_LogForwarder = mpt::make_unique( *m_Log ); + m_Dither = std::make_unique( mpt::global_prng() ); + m_LogForwarder = std::make_unique( *m_Log ); m_sndFile->SetCustomLog( m_LogForwarder.get() ); m_current_subsong = 0; m_currentPositionSeconds = 0.0; @@ -486,7 +479,7 @@ void module_impl::load( const FileReader & file, const std::map< std::string, st std::vector > loaderMessages = loaderlog.GetMessages(); 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 ); + m_loaderMessages.push_back( mpt::ToCharset( mpt::Charset::UTF8, LogLevelToString( msg.first ) ) + std::string(": ") + msg.second ); } // init CSoundFile state that corresponds to ctls for ( const auto & ctl : ctls ) { @@ -500,11 +493,11 @@ std::size_t module_impl::read_wrapper( std::size_t count, std::int16_t * left, s m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + std::int16_t * const buffers[4] = { left, right, rear_left, rear_right }; + AudioReadTargetGainBuffer> target( audio_buffer_planar( buffers, planar_audio_buffer_valid_channels( buffers, std::size( buffers) ), count ), *m_Dither, m_Gain ); while ( count > 0 ) { - std::int16_t * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; - AudioReadTargetGainBuffer target(*m_Dither, 0, buffers, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -523,11 +516,11 @@ std::size_t module_impl::read_wrapper( std::size_t count, float * left, float * m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + float * const buffers[4] = { left, right, rear_left, rear_right }; + AudioReadTargetGainBuffer> target( audio_buffer_planar( buffers, planar_audio_buffer_valid_channels( buffers, std::size( buffers) ), count ), *m_Dither, m_Gain ); while ( count > 0 ) { - float * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; - AudioReadTargetGainBuffer target(*m_Dither, 0, buffers, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -546,10 +539,10 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_ m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + AudioReadTargetGainBuffer> target( audio_buffer_interleaved( interleaved, channels, count ), *m_Dither, m_Gain ); while ( count > 0 ) { - AudioReadTargetGainBuffer target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -568,10 +561,10 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_ m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + AudioReadTargetGainBuffer> target( audio_buffer_interleaved( interleaved, channels, count ), *m_Dither, m_Gain ); while ( count > 0 ) { - AudioReadTargetGainBuffer target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -593,17 +586,14 @@ std::vector module_impl::get_supported_extensions() { std::copy( extensions.begin(), extensions.end(), std::back_insert_iterator >( retval ) ); return retval; } -bool module_impl::is_extension_supported( const char * extension ) { +bool module_impl::is_extension_supported( std::string_view extension ) { return CSoundFile::IsExtensionSupported( extension ); } -bool module_impl::is_extension_supported( const std::string & extension ) { - return CSoundFile::IsExtensionSupported( extension.c_str() ); -} double module_impl::could_open_probability( const OpenMPT::FileReader & file, double effort, std::unique_ptr log ) { try { if ( effort >= 0.8 ) { - std::unique_ptr sndFile = mpt::make_unique(); - std::unique_ptr logForwarder = mpt::make_unique( *log ); + std::unique_ptr sndFile = std::make_unique(); + std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, CSoundFile::loadCompleteModule ) ) { return 0.0; @@ -611,8 +601,8 @@ double module_impl::could_open_probability( const OpenMPT::FileReader & file, do sndFile->Destroy(); return 1.0; } else if ( effort >= 0.6 ) { - std::unique_ptr sndFile = mpt::make_unique(); - std::unique_ptr logForwarder = mpt::make_unique( *log ); + std::unique_ptr sndFile = std::make_unique(); + std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, CSoundFile::loadNoPatternOrPluginData ) ) { return 0.0; @@ -620,8 +610,8 @@ double module_impl::could_open_probability( const OpenMPT::FileReader & file, do sndFile->Destroy(); return 0.8; } else if ( effort >= 0.2 ) { - std::unique_ptr sndFile = mpt::make_unique(); - std::unique_ptr logForwarder = mpt::make_unique( *log ); + std::unique_ptr sndFile = std::make_unique(); + std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, CSoundFile::onlyVerifyHeader ) ) { return 0.0; @@ -669,9 +659,27 @@ double module_impl::could_open_probability( std::istream & stream, double effort std::size_t module_impl::probe_file_header_get_recommended_size() { return CSoundFile::ProbeRecommendedSize; } +int module_impl::probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ) { + int result = 0; + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( data, size ), &filesize ) ) { + case CSoundFile::ProbeSuccess: + result = probe_file_header_result_success; + break; + case CSoundFile::ProbeFailure: + result = probe_file_header_result_failure; + break; + case CSoundFile::ProbeWantMoreData: + result = probe_file_header_result_wantmoredata; + break; + default: + throw exception("internal error"); + break; + } + return result; +} int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), &filesize ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), &filesize ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -689,7 +697,25 @@ int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * da } int module_impl::probe_file_header( std::uint64_t flags, const void * data, std::size_t size, std::uint64_t filesize ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), &filesize ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), &filesize ) ) { + case CSoundFile::ProbeSuccess: + result = probe_file_header_result_success; + break; + case CSoundFile::ProbeFailure: + result = probe_file_header_result_failure; + break; + case CSoundFile::ProbeWantMoreData: + result = probe_file_header_result_wantmoredata; + break; + default: + throw exception("internal error"); + break; + } + return result; +} +int module_impl::probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ) { + int result = 0; + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( data, size ), nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -707,7 +733,7 @@ int module_impl::probe_file_header( std::uint64_t flags, const void * data, std: } int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -725,7 +751,7 @@ int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * da } int module_impl::probe_file_header( std::uint64_t flags, const void * data, std::size_t size ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -767,7 +793,7 @@ int module_impl::probe_file_header( std::uint64_t flags, std::istream & stream ) size_read += read_count; size_toread -= read_count; } - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -807,7 +833,7 @@ int module_impl::probe_file_header( std::uint64_t flags, callback_stream_wrapper break; } } - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -838,6 +864,11 @@ module_impl::module_impl( std::istream & stream, std::unique_ptr load( make_FileReader( &stream ), ctls ); apply_libopenmpt_defaults(); } +module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { + ctor( ctls ); + load( make_FileReader( mpt::as_span( data ) ), ctls ); + apply_libopenmpt_defaults(); +} module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( make_FileReader( mpt::as_span( data ) ), ctls ); @@ -845,7 +876,12 @@ module_impl::module_impl( const std::vector & data, std::unique_pt } module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); - load( make_FileReader( mpt::byte_cast< mpt::span< const mpt::byte > >( mpt::as_span( data ) ) ), ctls ); + load( make_FileReader( mpt::byte_cast< mpt::span< const std::byte > >( mpt::as_span( data ) ) ), ctls ); + apply_libopenmpt_defaults(); +} +module_impl::module_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { + ctor( ctls ); + load( make_FileReader( mpt::as_span( data, size ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { @@ -855,12 +891,12 @@ module_impl::module_impl( const std::uint8_t * data, std::size_t size, std::uniq } module_impl::module_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); - load( make_FileReader( mpt::byte_cast< mpt::span< const mpt::byte > >( mpt::as_span( data, size ) ) ), ctls ); + load( make_FileReader( mpt::byte_cast< mpt::span< const std::byte > >( mpt::as_span( data, size ) ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); - load( make_FileReader( mpt::as_span( mpt::void_cast< const mpt::byte * >( data ), size ) ), ctls ); + load( make_FileReader( mpt::as_span( mpt::void_cast< const std::byte * >( data ), size ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::~module_impl() { @@ -924,7 +960,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, std:: throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 1 ); - count = read_wrapper( count, mono, 0, 0, 0 ); + count = read_wrapper( count, mono, nullptr, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -933,7 +969,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, std:: throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); - count = read_wrapper( count, left, right, 0, 0 ); + count = read_wrapper( count, left, right, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -951,7 +987,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, float throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 1 ); - count = read_wrapper( count, mono, 0, 0, 0 ); + count = read_wrapper( count, mono, nullptr, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -960,7 +996,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, float throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); - count = read_wrapper( count, left, right, 0, 0 ); + count = read_wrapper( count, left, right, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -1012,7 +1048,7 @@ std::size_t module_impl::read_interleaved_quad( std::int32_t samplerate, std::si double module_impl::get_duration_seconds() const { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; if ( m_current_subsong == all_subsongs ) { // Play all subsongs consecutively. @@ -1025,7 +1061,7 @@ double module_impl::get_duration_seconds() const { return subsongs[m_current_subsong].duration; } void module_impl::select_subsong( std::int32_t subsong ) { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; if ( subsong != all_subsongs && ( subsong < 0 || subsong >= static_cast( subsongs.size() ) ) ) { throw openmpt::exception("invalid subsong"); @@ -1052,7 +1088,7 @@ double module_impl::get_position_seconds() const { return m_currentPositionSeconds; } double module_impl::set_position_seconds( double seconds ) { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; const subsong_data * subsong = 0; double base_seconds = 0.0; @@ -1113,60 +1149,114 @@ std::vector module_impl::get_metadata_keys() const { "warnings", }; } +std::string module_impl::get_message_instruments() const { + std::string retval; + std::string tmp; + bool valid = false; + for ( INSTRUMENTINDEX i = 1; i <= m_sndFile->GetNumInstruments(); ++i ) { + std::string instname = m_sndFile->GetInstrumentName( i ); + if ( !instname.empty() ) { + valid = true; + } + tmp += instname; + tmp += "\n"; + } + if ( valid ) { + retval = tmp; + } + return retval; +} +std::string module_impl::get_message_samples() const { + std::string retval; + std::string tmp; + bool valid = false; + for ( SAMPLEINDEX i = 1; i <= m_sndFile->GetNumSamples(); ++i ) { + std::string samplename = m_sndFile->GetSampleName( i ); + if ( !samplename.empty() ) { + valid = true; + } + tmp += samplename; + tmp += "\n"; + } + if ( valid ) { + retval = tmp; + } + return retval; +} std::string module_impl::get_metadata( const std::string & key ) const { if ( key == std::string("type") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.type ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.type ); } else if ( key == std::string("type_long") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.formatName ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.formatName ); } else if ( key == std::string("originaltype") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.originalType ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.originalType ); } else if ( key == std::string("originaltype_long") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.originalFormatName ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.originalFormatName ); } else if ( key == std::string("container") ) { - return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModContainerTypeToString( m_sndFile->GetContainerType() ) ); + return mpt::ToCharset(mpt::Charset::UTF8, CSoundFile::ModContainerTypeToString( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("container_long") ) { - return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModContainerTypeToTracker( m_sndFile->GetContainerType() ) ); + return mpt::ToCharset(mpt::Charset::UTF8, CSoundFile::ModContainerTypeToTracker( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("tracker") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.madeWithTracker ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.madeWithTracker ); } else if ( key == std::string("artist") ) { - return mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->m_songArtist ); + return mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->m_songArtist ); } else if ( key == std::string("title") ) { return mod_string_to_utf8( m_sndFile->GetTitle() ); } else if ( key == std::string("date") ) { if ( m_sndFile->GetFileHistory().empty() || !m_sndFile->GetFileHistory().back().HasValidDate() ) { return std::string(); } - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->GetFileHistory().back().AsISO8601() ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->GetFileHistory().back().AsISO8601() ); } else if ( key == std::string("message") ) { std::string retval = m_sndFile->m_songMessage.GetFormatted( SongMessage::leLF ); if ( retval.empty() ) { - std::string tmp; - bool valid = false; - for ( INSTRUMENTINDEX i = 1; i <= m_sndFile->GetNumInstruments(); ++i ) { - std::string instname = m_sndFile->GetInstrumentName( i ); - if ( !instname.empty() ) { - valid = true; - } - tmp += instname; - tmp += "\n"; - } - if ( valid ) { - retval = tmp; - } - } - if ( retval.empty() ) { - std::string tmp; - bool valid = false; - for ( SAMPLEINDEX i = 1; i <= m_sndFile->GetNumSamples(); ++i ) { - std::string samplename = m_sndFile->GetSampleName( i ); - if ( !samplename.empty() ) { - valid = true; - } - tmp += samplename; - tmp += "\n"; - } - if ( valid ) { - retval = tmp; + switch ( m_sndFile->GetMessageHeuristic() ) { + case ModMessageHeuristicOrder::Instruments: + retval = get_message_instruments(); + break; + case ModMessageHeuristicOrder::Samples: + retval = get_message_samples(); + break; + case ModMessageHeuristicOrder::InstrumentsSamples: + if ( retval.empty() ) { + retval = get_message_instruments(); + } + if ( retval.empty() ) { + retval = get_message_samples(); + } + break; + case ModMessageHeuristicOrder::SamplesInstruments: + if ( retval.empty() ) { + retval = get_message_samples(); + } + if ( retval.empty() ) { + retval = get_message_instruments(); + } + break; + case ModMessageHeuristicOrder::BothInstrumentsSamples: + { + std::string message_instruments = get_message_instruments(); + std::string message_samples = get_message_samples(); + if ( !message_instruments.empty() ) { + retval += std::move( message_instruments ); + } + if ( !message_samples.empty() ) { + retval += std::move( message_samples ); + } + } + break; + case ModMessageHeuristicOrder::BothSamplesInstruments: + { + std::string message_instruments = get_message_instruments(); + std::string message_samples = get_message_samples(); + if ( !message_samples.empty() ) { + retval += std::move( message_samples ); + } + if ( !message_instruments.empty() ) { + retval += std::move( message_instruments ); + } + } + break; } } return mod_string_to_utf8( retval ); @@ -1189,6 +1279,9 @@ std::string module_impl::get_metadata( const std::string & key ) const { return ""; } +double module_impl::get_current_estimated_bpm() const { + return m_sndFile->GetCurrentBPM(); +} std::int32_t module_impl::get_current_speed() const { return m_sndFile->m_PlayState.m_nMusicSpeed; } @@ -1250,7 +1343,7 @@ float module_impl::get_current_channel_vu_rear_right( std::int32_t channel ) con } std::int32_t module_impl::get_num_subsongs() const { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; return static_cast( subsongs.size() ); } @@ -1272,10 +1365,10 @@ std::int32_t module_impl::get_num_samples() const { 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() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; for ( const auto & subsong : subsongs ) { - retval.push_back( mod_string_to_utf8( m_sndFile->Order( static_cast( subsong.sequence ) ).GetName() ) ); + retval.push_back( mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->Order( static_cast( subsong.sequence ) ).GetName() ) ); } return retval; } @@ -1404,7 +1497,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() ) ? mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("...") + ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("...") , ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("...") ); @@ -1473,7 +1566,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() ) ? mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("..."); + text += ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("..."); high += ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("..."); if ( ( width == 0 ) || ( width >= 6 ) ) { text += std::string(" "); @@ -1510,25 +1603,39 @@ std::string module_impl::highlight_pattern_row_channel( std::int32_t p, std::int return format_and_highlight_pattern_row_channel( p, r, c, width, pad ).second; } -std::vector module_impl::get_ctls() const { - 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::pair module_impl::get_ctl_infos() const { + static constexpr ctl_info ctl_infos[] = { + { "load.skip_samples", ctl_type::boolean }, + { "load.skip_patterns", ctl_type::boolean }, + { "load.skip_plugins", ctl_type::boolean }, + { "load.skip_subsongs_init", ctl_type::boolean }, + { "seek.sync_samples", ctl_type::boolean }, + { "subsong", ctl_type::integer }, + { "play.tempo_factor", ctl_type::floatingpoint }, + { "play.pitch_factor", ctl_type::floatingpoint }, + { "play.at_end", ctl_type::text }, + { "render.resampler.emulate_amiga", ctl_type::boolean }, + { "render.resampler.emulate_amiga_type", ctl_type::text }, + { "render.opl.volume_factor", ctl_type::floatingpoint }, + { "dither", ctl_type::integer } }; + return std::make_pair(std::begin(ctl_infos), std::end(ctl_infos)); } + +std::vector module_impl::get_ctls() const { + std::vector result; + auto ctl_infos = get_ctl_infos(); + result.reserve(std::distance(ctl_infos.first, ctl_infos.second)); + for ( std::ptrdiff_t i = 0; i < std::distance(ctl_infos.first, ctl_infos.second); ++i ) { + result.push_back(ctl_infos.first[i].name); + } + return result; +} + std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { @@ -1539,20 +1646,189 @@ std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const ctl = ctl.substr( 0, ctl.length() - 1 ); } } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + ctl); + } else { + return std::string(); + } + } + std::string result; + switch ( found_ctl->type ) { + case ctl_type::boolean: + return mpt::fmt::val( ctl_get_boolean( ctl, throw_if_unknown ) ); + break; + case ctl_type::integer: + return mpt::fmt::val( ctl_get_integer( ctl, throw_if_unknown ) ); + break; + case ctl_type::floatingpoint: + return mpt::fmt::val( ctl_get_floatingpoint( ctl, throw_if_unknown ) ); + break; + case ctl_type::text: + return ctl_get_text( ctl, throw_if_unknown ); + break; + } + return result; +} +bool module_impl::ctl_get_boolean( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return false; + } + } + if ( found_ctl->type != ctl_type::boolean ) { + throw openmpt::exception("wrong ctl value type"); + } if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { - return mpt::fmt::val( m_ctl_load_skip_samples ); + return m_ctl_load_skip_samples; } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { - return mpt::fmt::val( m_ctl_load_skip_patterns ); + return m_ctl_load_skip_patterns; } else if ( ctl == "load.skip_plugins" ) { - return mpt::fmt::val( m_ctl_load_skip_plugins ); + return m_ctl_load_skip_plugins; } else if ( ctl == "load.skip_subsongs_init" ) { - return mpt::fmt::val( m_ctl_load_skip_subsongs_init ); + return m_ctl_load_skip_subsongs_init; } else if ( ctl == "seek.sync_samples" ) { - return mpt::fmt::val( m_ctl_seek_sync_samples ); + return m_ctl_seek_sync_samples; + } else if ( ctl == "render.resampler.emulate_amiga" ) { + return ( m_sndFile->m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off ); + } else { + MPT_ASSERT_NOTREACHED(); + return false; + } +} +std::int64_t module_impl::ctl_get_integer( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return 0; + } + } + if ( found_ctl->type != ctl_type::integer ) { + throw openmpt::exception("wrong ctl value type"); + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); } else if ( ctl == "subsong" ) { - return mpt::fmt::val( get_selected_subsong() ); + return get_selected_subsong(); + } else if ( ctl == "dither" ) { + return static_cast( m_Dither->GetMode() ); + } else { + MPT_ASSERT_NOTREACHED(); + return 0; + } +} +double module_impl::ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return 0.0; + } + } + if ( found_ctl->type != ctl_type::floatingpoint ) { + throw openmpt::exception("wrong ctl value type"); + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( ctl == "play.tempo_factor" ) { + if ( !is_loaded() ) { + return 1.0; + } + return 65536.0 / m_sndFile->m_nTempoFactor; + } else if ( ctl == "play.pitch_factor" ) { + if ( !is_loaded() ) { + return 1.0; + } + return m_sndFile->m_nFreqFactor / 65536.0; + } else if ( ctl == "render.opl.volume_factor" ) { + return static_cast( m_sndFile->m_OPLVolumeFactor ) / static_cast( m_sndFile->m_OPLVolumeFactorScale ); + } else { + MPT_ASSERT_NOTREACHED(); + return 0.0; + } +} +std::string module_impl::ctl_get_text( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return std::string(); + } + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); } else if ( ctl == "play.at_end" ) { switch ( m_ctl_play_at_end ) { @@ -1565,32 +1841,29 @@ std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const default: return std::string(); } - } else if ( ctl == "play.tempo_factor" ) { - if ( !is_loaded() ) { - return "1.0"; + } else if ( ctl == "render.resampler.emulate_amiga_type" ) { + switch ( m_ctl_render_resampler_emulate_amiga_type ) { + case amiga_filter_type::a500: + return "a500"; + case amiga_filter_type::a1200: + return "a1200"; + case amiga_filter_type::unfiltered: + return "unfiltered"; + case amiga_filter_type::auto_filter: + return "auto"; + default: + return std::string(); } - return mpt::fmt::val( 65536.0 / m_sndFile->m_nTempoFactor ); - } else if ( ctl == "play.pitch_factor" ) { - if ( !is_loaded() ) { - return "1.0"; - } - 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 { - if ( throw_if_unknown ) { - throw openmpt::exception("unknown ctl: " + ctl); - } else { - return std::string(); - } + MPT_ASSERT_NOTREACHED(); + return std::string(); } } + void module_impl::ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown ) { if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { @@ -1601,35 +1874,152 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro ctl = ctl.substr( 0, ctl.length() - 1 ); } } - if ( ctl == "" ) { - throw openmpt::exception("empty ctl: := " + value); - } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { - m_ctl_load_skip_samples = ConvertStrTo( value ); - } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { - m_ctl_load_skip_patterns = ConvertStrTo( value ); - } else if ( ctl == "load.skip_plugins" ) { - m_ctl_load_skip_plugins = ConvertStrTo( value ); - } else if ( ctl == "load.skip_subsongs_init" ) { - m_ctl_load_skip_subsongs_init = ConvertStrTo( value ); - } else if ( ctl == "seek.sync_samples" ) { - 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; + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + value); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + ctl + " := " + value); } else { - throw openmpt::exception("unknown song end action:" + value); + return; } + } + switch ( found_ctl->type ) { + case ctl_type::boolean: + ctl_set_boolean( ctl, ConvertStrTo( value ), throw_if_unknown ); + break; + case ctl_type::integer: + ctl_set_integer( ctl, ConvertStrTo( value ), throw_if_unknown ); + break; + case ctl_type::floatingpoint: + ctl_set_floatingpoint( ctl, ConvertStrTo( value ), throw_if_unknown ); + break; + case ctl_type::text: + ctl_set_text( ctl, value, throw_if_unknown ); + break; + } +} +void module_impl::ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value)); + } else { + return; + } + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { + m_ctl_load_skip_samples = value; + } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { + m_ctl_load_skip_patterns = value; + } else if ( ctl == "load.skip_plugins" ) { + m_ctl_load_skip_plugins = value; + } else if ( ctl == "load.skip_subsongs_init" ) { + m_ctl_load_skip_subsongs_init = value; + } else if ( ctl == "seek.sync_samples" ) { + m_ctl_seek_sync_samples = value; + } else if ( ctl == "render.resampler.emulate_amiga" ) { + CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; + const bool enabled = value; + if ( enabled ) + newsettings.emulateAmiga = translate_amiga_filter_type( m_ctl_render_resampler_emulate_amiga_type ); + else + newsettings.emulateAmiga = Resampling::AmigaFilter::Off; + if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { + m_sndFile->SetResamplerSettings( newsettings ); + } + } else { + MPT_ASSERT_NOTREACHED(); + } +} +void module_impl::ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value)); + } else { + return; + } + } + + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( ctl == "subsong" ) { + select_subsong( mpt::saturate_cast( value ) ); + } else if ( ctl == "dither" ) { + int dither = mpt::saturate_cast( value ); + if ( dither < 0 || dither >= NumDitherModes ) { + dither = DitherDefault; + } + m_Dither->SetMode( static_cast( dither ) ); + } else { + MPT_ASSERT_NOTREACHED(); + } +} +void module_impl::ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value)); + } else { + return; + } + } + + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); } else if ( ctl == "play.tempo_factor" ) { if ( !is_loaded() ) { return; } - double factor = ConvertStrTo( value ); + double factor = value; if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid tempo factor"); } @@ -1639,33 +2029,77 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro if ( !is_loaded() ) { return; } - double factor = ConvertStrTo( value ); + double factor = value; if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid pitch factor"); } m_sndFile->m_nFreqFactor = mpt::saturate_round( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); - } else if ( ctl == "render.resampler.emulate_amiga" ) { - CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; - newsettings.emulateAmiga = ConvertStrTo( value ); - 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 ) { - dither = DitherDefault; - } - m_Dither->SetMode( static_cast( dither ) ); + m_sndFile->m_OPLVolumeFactor = mpt::saturate_round( value * static_cast( m_sndFile->m_OPLVolumeFactorScale ) ); } else { - if ( throw_if_unknown ) { - throw openmpt::exception("unknown ctl: " + ctl + " := " + value); - } else { - // ignore + MPT_ASSERT_NOTREACHED(); + } +} +void module_impl::ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); } } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + std::string( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + std::string(value)); + } else { + return; + } + } + + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + std::string( value ) ); + } else if ( ctl == "play.at_end" ) { + if ( value == "fadeout" ) { + m_ctl_play_at_end = song_end_action::fadeout_song; + } else if(value == "continue") { + m_ctl_play_at_end = song_end_action::continue_song; + } else if(value == "stop") { + m_ctl_play_at_end = song_end_action::stop_song; + } else { + throw openmpt::exception("unknown song end action:" + std::string(value)); + } + } else if ( ctl == "render.resampler.emulate_amiga_type" ) { + if ( value == "a500" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::a500; + } else if ( value == "a1200" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::a1200; + } else if ( value == "unfiltered" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::unfiltered; + } else if ( value == "auto" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::auto_filter; + } else { + throw openmpt::exception( "invalid amiga filter type" ); + } + if ( m_sndFile->m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off ) { + CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; + newsettings.emulateAmiga = translate_amiga_filter_type( m_ctl_render_resampler_emulate_amiga_type ); + if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { + m_sndFile->SetResamplerSettings( newsettings ); + } + } + } else { + MPT_ASSERT_NOTREACHED(); + } } } // namespace openmpt diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp index 299417be3..1e440a848 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp @@ -15,6 +15,7 @@ #include #include +#include #if defined(_MSC_VER) #pragma warning(push) @@ -31,7 +32,8 @@ class FileReader; } // namespace detail typedef detail::FileReader FileReader; class CSoundFile; -class Dither; +template class DitherChannels; +using Dither = DitherChannels<4>; } // namespace OpenMPT namespace openmpt { @@ -71,6 +73,14 @@ struct callback_stream_wrapper { }; // struct callback_stream_wrapper class module_impl { +public: + enum class amiga_filter_type { + a500, + a1200, + unfiltered, + auto_filter, + }; + protected: struct subsong_data { double duration; @@ -88,7 +98,18 @@ protected: stop_song, }; - static const std::int32_t all_subsongs = -1; + static constexpr std::int32_t all_subsongs = -1; + + enum class ctl_type { + boolean, + integer, + floatingpoint, + text, + }; + struct ctl_info { + const char * name; + ctl_type type; + }; std::unique_ptr m_Log; std::unique_ptr m_LogForwarder; @@ -101,6 +122,7 @@ protected: subsongs_type m_subsongs; float m_Gain; song_end_action m_ctl_play_at_end; + amiga_filter_type m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::auto_filter; bool m_ctl_load_skip_samples; bool m_ctl_load_skip_patterns; bool m_ctl_load_skip_plugins; @@ -124,26 +146,31 @@ protected: std::size_t read_wrapper( std::size_t count, float * left, float * right, float * rear_left, float * rear_right ); std::size_t read_interleaved_wrapper( std::size_t count, std::size_t channels, std::int16_t * interleaved ); std::size_t read_interleaved_wrapper( std::size_t count, std::size_t channels, float * interleaved ); + std::string get_message_instruments() const; + std::string get_message_samples() const; std::pair< std::string, std::string > format_and_highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int command ) const; std::pair< std::string, std::string > format_and_highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; static double could_open_probability( const OpenMPT::FileReader & file, double effort, std::unique_ptr log ); public: static std::vector get_supported_extensions(); - static bool is_extension_supported( const char * extension ); - static bool is_extension_supported( const std::string & extension ); + static bool is_extension_supported( std::string_view extension ); static double could_open_probability( callback_stream_wrapper stream, double effort, std::unique_ptr log ); static double could_open_probability( std::istream & stream, double effort, std::unique_ptr log ); static std::size_t probe_file_header_get_recommended_size(); + static int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ); static int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ); static int probe_file_header( std::uint64_t flags, const void * data, std::size_t size, std::uint64_t filesize ); + static int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, const void * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, std::istream & stream ); static int probe_file_header( std::uint64_t flags, callback_stream_wrapper stream ); module_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); @@ -171,6 +198,7 @@ public: std::size_t read_interleaved_quad( std::int32_t samplerate, std::size_t count, float * interleaved_quad ); std::vector get_metadata_keys() const; std::string get_metadata( const std::string & key ) const; + double get_current_estimated_bpm() const; std::int32_t get_current_speed() const; std::int32_t get_current_tempo() const; std::int32_t get_current_order() const; @@ -201,9 +229,18 @@ public: std::string highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const; std::string format_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; std::string highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; + std::pair get_ctl_infos() const; std::vector get_ctls() const; std::string ctl_get( std::string ctl, bool throw_if_unknown = true ) const; + bool ctl_get_boolean( std::string_view ctl, bool throw_if_unknown = true ) const; + std::int64_t ctl_get_integer( std::string_view ctl, bool throw_if_unknown = true ) const; + double ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown = true ) const; + std::string ctl_get_text( std::string_view ctl, bool throw_if_unknown = true ) const; void ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown = true ); + void ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown = true ); + void ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown = true ); + void ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown = true ); + void ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown = true ); }; // class module_impl namespace helper { diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp index 780d494ef..59a4930f4 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp @@ -67,13 +67,14 @@ protected: CSliderCtrl m_SliderCtrlGain; CComboBox m_ComboBoxInterpolation; CButton m_CheckBoxAmigaResampler; + CComboBox m_ComboBoxAmigaFilter; CComboBox m_ComboBoxRepeat; CSliderCtrl m_SliderCtrlStereoSeparation; CComboBox m_ComboBoxRamping; public: - CSettingsDialog( libopenmpt_settings * s_, CString title, CWnd * parent = NULL ) + CSettingsDialog( libopenmpt_settings * s_, CString title, CWnd * parent = nullptr ) : CDialog( IDD_SETTINGS, parent ) , s( s_ ) , m_Title( title ) @@ -91,6 +92,7 @@ protected: DDX_Control( pDX, IDC_SLIDER_GAIN, m_SliderCtrlGain ); DDX_Control( pDX, IDC_COMBO_INTERPOLATION, m_ComboBoxInterpolation ); DDX_Control( pDX, IDC_CHECK_AMIGA_RESAMPLER, m_CheckBoxAmigaResampler ); + DDX_Control( pDX, IDC_COMBO_AMIGA_FILTER, m_ComboBoxAmigaFilter ); DDX_Control( pDX, IDC_COMBO_REPEAT, m_ComboBoxRepeat ); DDX_Control( pDX, IDC_SLIDER_STEREOSEPARATION, m_SliderCtrlStereoSeparation ); DDX_Control( pDX, IDC_COMBO_RAMPING, m_ComboBoxRamping ); @@ -98,9 +100,7 @@ protected: afx_msg BOOL OnInitDialog() override { - if ( !CDialog::OnInitDialog() ) { - return false; - } + CDialog::OnInitDialog(); SetWindowText( m_Title ); EnableToolTips(); @@ -125,7 +125,7 @@ protected: m_ComboBoxSamplerate.SelectString( 0, L"Default" ); } for ( int index = 0; index < m_ComboBoxSamplerate.GetCount(); ++index ) { - if ( m_ComboBoxSamplerate.GetItemData( index ) == s->samplerate ) { + if ( static_cast( m_ComboBoxSamplerate.GetItemData( index ) ) == s->samplerate ) { m_ComboBoxSamplerate.SetCurSel( index ); selected = true; } @@ -145,7 +145,7 @@ protected: m_ComboBoxChannels.SelectString( 0, L"Default" ); } for ( int index = 0; index < m_ComboBoxChannels.GetCount(); ++index ) { - if ( m_ComboBoxChannels.GetItemData( index ) == s->channels ) { + if ( static_cast( m_ComboBoxChannels.GetItemData( index ) ) == s->channels ) { m_ComboBoxChannels.SetCurSel( index ); selected = true; } @@ -166,7 +166,7 @@ protected: 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 ) { + if ( static_cast( m_ComboBoxInterpolation.GetItemData( index ) ) == s->interpolationfilterlength ) { m_ComboBoxInterpolation.SetCurSel( index ); selected = true; } @@ -176,13 +176,28 @@ protected: } m_CheckBoxAmigaResampler.SetCheck( s->use_amiga_resampler ? BST_CHECKED : BST_UNCHECKED ); + selected = false; + m_ComboBoxAmigaFilter.EnableWindow( s->use_amiga_resampler ? TRUE : FALSE ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"Default" ), 0 ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"A500 Filter" ), 0xA500 ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"A1200 Filter" ), 0xA1200 ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"Unfiltered" ), 1 ); + for ( int index = 0; index < m_ComboBoxAmigaFilter.GetCount(); ++index ) { + if ( static_cast( m_ComboBoxAmigaFilter.GetItemData( index ) ) == s->amiga_filter_type ) { + m_ComboBoxAmigaFilter.SetCurSel( index ); + selected = true; + } + } + if ( !selected ) { + m_ComboBoxAmigaFilter.SelectString( 0, L"Default" ); + } selected = false; - m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Forever" ), -1 ); + m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Forever" ), static_cast( -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 ) { + if ( static_cast( m_ComboBoxRepeat.GetItemData( index ) ) == s->repeatcount ) { m_ComboBoxRepeat.SetCurSel( index ); selected = true; } @@ -198,7 +213,7 @@ protected: m_SliderCtrlStereoSeparation.SetPos( s->stereoseparation ); selected = false; - m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"Default" ), -1 ); + m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"Default" ), static_cast( -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 ); @@ -206,7 +221,7 @@ protected: m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"5 ms" ), 5 ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"10 ms" ), 10 ); for ( int index = 0; index < m_ComboBoxRamping.GetCount(); ++index ) { - if ( m_ComboBoxRamping.GetItemData( index ) == s->ramping ) { + if ( static_cast( m_ComboBoxRamping.GetItemData( index ) ) == s->ramping ) { m_ComboBoxRamping.SetCurSel( index ); selected = true; } @@ -230,6 +245,7 @@ protected: s->interpolationfilterlength = m_ComboBoxInterpolation.GetItemData( m_ComboBoxInterpolation.GetCurSel() ); s->use_amiga_resampler = ( m_CheckBoxAmigaResampler.GetCheck() != BST_UNCHECKED ) ? 1 : 0; + s->amiga_filter_type = m_ComboBoxAmigaFilter.GetItemData( m_ComboBoxAmigaFilter.GetCurSel() ); s->repeatcount = m_ComboBoxRepeat.GetItemData( m_ComboBoxRepeat.GetCurSel() ); @@ -270,10 +286,15 @@ protected: return TRUE; } + void OnAmigaResamplerChanged() { + m_ComboBoxAmigaFilter.EnableWindow( IsDlgButtonChecked( IDC_CHECK_AMIGA_RESAMPLER ) != BST_UNCHECKED ? TRUE : FALSE ); + } + }; BEGIN_MESSAGE_MAP(CSettingsDialog, CDialog) ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CSettingsDialog::OnToolTipText) + ON_COMMAND( IDC_CHECK_AMIGA_RESAMPLER, &CSettingsDialog::OnAmigaResamplerChanged ) END_MESSAGE_MAP() diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc index ebd7bbe4b..a618fd3be 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc @@ -50,12 +50,12 @@ END // Dialog // -IDD_SETTINGS DIALOGEX 0, 0, 201, 183 +IDD_SETTINGS DIALOGEX 0, 0, 201, 200 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,78,162,54,14 - PUSHBUTTON "&Cancel",IDCANCEL,138,162,54,14 + DEFPUSHBUTTON "&OK",IDOK,78,180,54,14 + PUSHBUTTON "&Cancel",IDCANCEL,138,180,54,14 LTEXT "&Samplerate",IDC_STATIC,6,6,60,12,SS_CENTERIMAGE 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 @@ -66,13 +66,15 @@ BEGIN 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,186,12 - LTEXT "&Repeat",IDC_STATIC,6,96,60,12,SS_CENTERIMAGE - 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 | 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,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,6,156,186,1 + LTEXT "Amiga &Filter Type:",IDC_STATIC,6,96,60,12,SS_CENTERIMAGE + COMBOBOX IDC_COMBO_AMIGA_FILTER,72,96,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "&Repeat",IDC_STATIC,6,114,60,12,SS_CENTERIMAGE + COMBOBOX IDC_COMBO_REPEAT,72,114,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "S&tereo Separation",IDC_STATIC,6,132,60,12,SS_CENTERIMAGE + CONTROL "",IDC_SLIDER_STEREOSEPARATION,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,72,132,126,15 + LTEXT "&Volume Ramping",IDC_STATIC,6,151,60,12,SS_CENTERIMAGE + COMBOBOX IDC_COMBO_RAMPING,72,151,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,6,175,186,1 END IDD_FILEINFO DIALOGEX 0, 0, 310, 174 @@ -97,7 +99,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 194 TOPMARGIN, 7 - BOTTOMMARGIN, 176 + BOTTOMMARGIN, 193 END IDD_FILEINFO, DIALOG diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp index 6ef9744a2..b1cbf5b17 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp @@ -29,6 +29,7 @@ struct libopenmpt_settings { int mastergain_millibel; int stereoseparation; int use_amiga_resampler; + int amiga_filter_type; int repeatcount; int interpolationfilterlength; int ramping; @@ -55,7 +56,7 @@ protected: regkey = HKEY(); } } - virtual void write_setting( const std::string & /* key */ , const std::wstring & keyW, int val ) { + virtual void write_setting( const std::string & /* key */, const std::wstring & keyW, int val ) { HKEY regkey = HKEY(); if ( RegCreateKeyEx( HKEY_CURRENT_USER, ( L"Software\\libopenmpt\\" + subkey ).c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®key, NULL ) == ERROR_SUCCESS ) { DWORD v = val; @@ -81,6 +82,7 @@ public: repeatcount = 0; interpolationfilterlength = 8; use_amiga_resampler = 0; + amiga_filter_type = 0; ramping = -1; vis_allow_scroll = 1; changed = 0; @@ -94,7 +96,8 @@ public: read_setting( subkey, "StereoSeparation_Percent", stereoseparation ); read_setting( subkey, "RepeatCount", repeatcount ); read_setting( subkey, "InterpolationFilterLength", interpolationfilterlength ); - read_setting( subkey, "UseAmigaResampler", use_amiga_resampler); + read_setting( subkey, "UseAmigaResampler", use_amiga_resampler ); + read_setting( subkey, "AmigaFilterType", amiga_filter_type ); read_setting( subkey, "VolumeRampingStrength", ramping ); read_setting( subkey, "VisAllowScroll", vis_allow_scroll ); #undef read_setting @@ -108,7 +111,8 @@ public: write_setting( subkey, "StereoSeparation_Percent", stereoseparation ); write_setting( subkey, "RepeatCount", repeatcount ); write_setting( subkey, "InterpolationFilterLength", interpolationfilterlength ); - write_setting( subkey, "UseAmigaResampler", use_amiga_resampler); + write_setting( subkey, "UseAmigaResampler", use_amiga_resampler ); + write_setting( subkey, "AmigaFilterType", amiga_filter_type ); write_setting( subkey, "VolumeRampingStrength", ramping ); write_setting( subkey, "VisAllowScroll", vis_allow_scroll ); #undef write_setting diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index ad3941ed7..1e9407adc 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 4 +#define OPENMPT_API_VERSION_MINOR 5 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 10 +#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 1dc902690..b062362ec 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=4 -LIBOPENMPT_VERSION_PATCH=10 +LIBOPENMPT_VERSION_MINOR=5 +LIBOPENMPT_VERSION_PATCH=2 LIBOPENMPT_VERSION_PREREL= -LIBOPENMPT_LTVER_CURRENT=1 -LIBOPENMPT_LTVER_REVISION=10 -LIBOPENMPT_LTVER_AGE=1 +LIBOPENMPT_LTVER_CURRENT=2 +LIBOPENMPT_LTVER_REVISION=2 +LIBOPENMPT_LTVER_AGE=2 diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc index 52fca64be..4f122945c 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-2019 OpenMPT contributors, Copyright © 1997-2003 Olivier Lapicque" + VALUE "LegalCopyright", "Copyright © 2004-2020 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 c4d4a2121..980115f2a 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h @@ -12,6 +12,7 @@ #define IDC_SLIDER_STEREOSEPARATION 1006 #define IDC_COMBO_RAMPING 1007 #define IDC_FILEINFO 1008 +#define IDC_COMBO_AMIGA_FILTER 1008 #define IDC_CHECK_AMIGA_RESAMPLER 1009 // Next default values for new objects diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp index c210d7f7f..56334e4bc 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp @@ -219,7 +219,7 @@ static inline Tstring StringReplace( Tstring str, const Tstring2 & oldStr_, cons } static std::string StringUpperCase( std::string str ) { - std::transform( str.begin(), str.end(), str.begin(), std::toupper ); + std::transform( str.begin(), str.end(), str.begin(), []( char c ) { return static_cast( std::toupper( c ) ); } ); return str; } @@ -247,6 +247,7 @@ static void save_settings_to_map( std::map & result, const libo result[ "RepeatCount" ] = s.repeatcount; result[ "InterpolationFilterLength" ] = s.interpolationfilterlength; result[ "UseAmigaResampler" ] = s.use_amiga_resampler; + result[ "AmigaFilterType" ] = s.amiga_filter_type; result[ "VolumeRampingStrength" ] = s.ramping; } @@ -265,6 +266,7 @@ static void load_settings_from_map( libopenmpt::plugin::settings & s, const std: load_map_setting( map, "RepeatCount", s.repeatcount ); load_map_setting( map, "InterpolationFilterLength", s.interpolationfilterlength ); load_map_setting( map, "UseAmigaResampler", s.use_amiga_resampler ); + load_map_setting( map, "AmigaFilterType", s.amiga_filter_type ); load_map_setting( map, "VolumeRampingStrength", s.ramping ); } @@ -305,7 +307,21 @@ static void apply_options() { self->mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, self->settings.stereoseparation ); self->mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, self->settings.interpolationfilterlength ); self->mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, self->settings.ramping ); - self->mod->ctl_set( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? "1" : "0" ); + self->mod->ctl_set_boolean( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? true : false ); + switch ( self->settings.amiga_filter_type ) { + case 0: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "auto" ); + break; + case 1: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "unfiltered" ); + break; + case 0xA500: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a500" ); + break; + case 0xA1200: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a1200" ); + break; + } } } @@ -374,8 +390,8 @@ static void WINAPI ShortcutHandler( DWORD id ) { break; } - self->tempo_factor = std::min ( 48, std::max( -48, self->tempo_factor ) ); - self->pitch_factor = std::min ( 48, std::max( -48, self->pitch_factor ) ); + self->tempo_factor = std::min( 48, std::max( -48, self->tempo_factor ) ); + self->pitch_factor = std::min( 48, std::max( -48, self->pitch_factor ) ); const double tempo_factor = std::pow( 2.0, self->tempo_factor / 24.0 ); const double pitch_factor = std::pow( 2.0, self->pitch_factor / 24.0 ); @@ -449,7 +465,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-2019 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2020 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; @@ -502,7 +518,7 @@ std::streambuf::int_type xmplay_streambuf::underflow() { char * base = &buffer.front(); char * start = base; if ( eback() == base ) { - std::size_t put_back_count = std::min( put_back, egptr() - base ); + std::size_t put_back_count = std::min( put_back, static_cast( egptr() - base ) ); std::memmove( base, egptr() - put_back_count, put_back_count ); start += put_back_count; } @@ -673,7 +689,7 @@ static char * build_xmplay_tags( const openmpt::module & mod ) { return result; } -static float * build_xmplay_length( 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 nullptr; @@ -749,6 +765,7 @@ static std::string sanitize_xmplay_multiline_string( const std::string & str ) { // check if a file is playable by this plugin // more thorough checks can be saved for the GetFileInfo and Open functions static BOOL WINAPI openmpt_CheckFile( const char * filename, XMPFILE file ) { + static_cast( filename ); try { #ifdef USE_XMPLAY_FILE_IO #ifdef USE_XMPLAY_ISTREAM @@ -791,6 +808,7 @@ static BOOL WINAPI openmpt_CheckFile( const char * filename, XMPFILE file ) { } static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, float * * length, char * * tags ) { + static_cast( filename ); try { std::map< std::string, std::string > ctls { @@ -867,6 +885,7 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl // open a file for playback // return: 0=failed, 1=success, 2=success and XMPlay can close the file static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) { + static_cast( filename ); xmpopenmpt_lock guard; reset_options(); try { @@ -1134,7 +1153,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 + 99 ) / 100 ); // 100 Hz timing info update interval + std::size_t frames_chunk = std::min( frames_to_render, static_cast( ( self->samplerate + 99 ) / 100 ) ); // 100 Hz timing info update interval switch ( self->num_channels ) { case 1: { @@ -1294,18 +1313,18 @@ static BOOL WINAPI VisOpen(DWORD colors[3]) { viscolors[col_global] = invert_color( viscolors[col_background] ); const int r = viscolors[col_text].r, g = viscolors[col_text].g, b = viscolors[col_text].b; - viscolors[col_empty].r = (r + viscolors[col_background].r) / 2; - viscolors[col_empty].g = (g + viscolors[col_background].g) / 2; - viscolors[col_empty].b = (b + viscolors[col_background].b) / 2; + viscolors[col_empty].r = static_cast( (r + viscolors[col_background].r) / 2 ); + viscolors[col_empty].g = static_cast( (g + viscolors[col_background].g) / 2 ); + viscolors[col_empty].b = static_cast( (b + viscolors[col_background].b) / 2 ); viscolors[col_empty].a = 0; #define MIXCOLOR(col, c1, c2, c3) { \ viscolors[col] = viscolors[col_text]; \ int mix = viscolors[col].c1 + 0xA0; \ - viscolors[col].c1 = mix; \ + viscolors[col].c1 = static_cast( mix ); \ if ( mix > 0xFF ) { \ - viscolors[col].c2 = std::max( c2 - viscolors[col].c1 / 2, 0 ); \ - viscolors[col].c3 = std::max( c3 - viscolors[col].c1 / 2, 0 ); \ + viscolors[col].c2 = std::max( static_cast( c2 - viscolors[col].c1 / 2 ), std::uint8_t(0) ); \ + viscolors[col].c3 = std::max( static_cast( c3 - viscolors[col].c1 / 2 ), std::uint8_t(0) ); \ viscolors[col].c1 = 0xFF; \ } } @@ -1336,13 +1355,15 @@ static void WINAPI VisClose() { DeleteFont( visfont ); DeleteBitmap( visbitmap ); - DeleteDC( visDC ); + if ( visDC ) { + DeleteDC( visDC ); + } } -static void WINAPI VisSize(HDC dc, SIZE *size) { +static void WINAPI VisSize( HDC /* dc */ , SIZE * /* size */ ) { xmpopenmpt_lock guard; last_pattern = -1; // Force redraw } -static BOOL WINAPI VisRender(DWORD *buf, SIZE size, DWORD flags) { +static BOOL WINAPI VisRender( DWORD * /* buf */ , SIZE /* size */ , DWORD /* flags */ ) { xmpopenmpt_lock guard; return FALSE; } @@ -1392,7 +1413,7 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { const std::size_t channels = self->mod->get_num_channels(); const std::size_t rows = self->mod->get_pattern_num_rows( pattern ); - const std::size_t num_half_chars = std::max( 2 * size.cx / text_size.cx, 8 ) - 8; + const std::size_t num_half_chars = std::max( static_cast( 2 * size.cx / text_size.cx ), std::size_t(8) ) - 8; const std::size_t num_rows = size.cy / text_size.cy; // Spaces between pattern components are half width, full space at channel end @@ -1428,7 +1449,9 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { if ( !visDC || last_pattern != pattern ) { DeleteBitmap( visbitmap ); - DeleteDC( visDC ); + if ( visDC ) { + DeleteDC( visDC ); + } visDC = CreateCompatibleDC( dc ); visbitmap = CreateCompatibleBitmap( dc, pattern_width, pattern_height ); @@ -1565,13 +1588,13 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { if ( offset_x < 0 ) { src_offset_x -= offset_x; - pattern_width = std::min( pattern_width + offset_x, size.cx ); + pattern_width = std::min( static_cast( pattern_width + offset_x ), static_cast( size.cx ) ); offset_x = 0; } if ( offset_y < 0 ) { src_offset_y -= offset_y; - pattern_height = std::min( pattern_height + offset_y, size.cy ); + pattern_height = std::min( static_cast( pattern_height + offset_y ), static_cast( size.cy ) ); offset_y = 0; } @@ -1589,7 +1612,7 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { return TRUE; } -static void WINAPI VisButton(DWORD x, DWORD y) { +static void WINAPI VisButton( DWORD /* x */ , DWORD /* y */ ) { //xmpopenmpt_lock guard; } @@ -1643,7 +1666,14 @@ static char * file_formats; static void xmp_openmpt_on_dll_load() { ZeroMemory( &xmpopenmpt_mutex, sizeof( xmpopenmpt_mutex ) ); - InitializeCriticalSection( &xmpopenmpt_mutex ); + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:28125) // The function 'InitializeCriticalSection' must be called from within a try/except block: The requirement might be conditional. + #endif // _MSC_VER + InitializeCriticalSection( &xmpopenmpt_mutex ); + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER std::vector extensions = openmpt::get_supported_extensions(); std::string filetypes_string = "OpenMPT"; filetypes_string.push_back('\0'); diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/.clang-format b/Frameworks/OpenMPT/OpenMPT/openmpt123/.clang-format new file mode 100644 index 000000000..a50eb55a0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/.clang-format @@ -0,0 +1,45 @@ +Language: Cpp +AccessModifierOffset: -2 +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: Empty +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakConstructorInitializersBeforeComma: true +BreakStringLiterals: false +ColumnLimit: 0 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentWidth: 2 +MaxEmptyLinesToKeep: 3 +PointerAlignment: Middle +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 2 +SpacesInContainerLiterals: false +SpacesInParentheses: true +SpacesInSquareBrackets: true +Standard: Cpp11 +TabWidth: 2 +UseTab: ForIndentation diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index b5b4b516d..faae040d1 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -8,7 +8,7 @@ */ static const char * const license = -"Copyright (c) 2004-2019, OpenMPT contributors" "\n" +"Copyright (c) 2004-2020, OpenMPT contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" @@ -44,13 +44,16 @@ static const char * const license = #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -60,6 +63,7 @@ static const char * const license = #if defined(__DJGPP__) #include +#include #include #include #include @@ -76,9 +80,6 @@ static const char * const license = #include #include #else -#if defined(MPT_NEEDS_THREADS) -#include -#endif #include #include #include @@ -99,7 +100,6 @@ static const char * const license = #include "openmpt123_allegro42.hpp" #include "openmpt123_portaudio.hpp" #include "openmpt123_pulseaudio.hpp" -#include "openmpt123_sdl.hpp" #include "openmpt123_sdl2.hpp" #include "openmpt123_waveout.hpp" @@ -129,12 +129,33 @@ struct show_version_number_exception : public std::exception { struct show_long_version_number_exception : public std::exception { }; +#if defined( WIN32 ) +bool IsConsole( DWORD stdHandle ) { + HANDLE hStd = GetStdHandle( stdHandle ); + if ( ( hStd != NULL ) && ( hStd != INVALID_HANDLE_VALUE ) ) { + DWORD mode = 0; + if ( GetConsoleMode( hStd, &mode ) != FALSE ) { + return true; + } + } + return false; +} +#endif + bool IsTerminal( int fd ) { #if defined( WIN32 ) - return true - && ( _isatty( fd ) ? true : false ) - && GetConsoleWindow() != NULL - ; + if ( !_isatty( fd ) ) { + return false; + } + DWORD stdHandle = 0; + if ( fd == 0 ) { + stdHandle = STD_INPUT_HANDLE; + } else if ( fd == 1 ) { + stdHandle = STD_OUTPUT_HANDLE; + } else if ( fd == 2 ) { + stdHandle = STD_ERROR_HANDLE; + } + return IsConsole( stdHandle ); #else return isatty( fd ) ? true : false; #endif @@ -166,10 +187,10 @@ static void set_input_mode() { class file_audio_stream_raii : public file_audio_stream_base { private: - file_audio_stream_base * impl; + std::unique_ptr impl; public: file_audio_stream_raii( const commandlineflags & flags, const std::string & filename, std::ostream & log ) - : impl(0) + : impl(nullptr) { if ( !flags.force_overwrite ) { std::ifstream testfile( filename, std::ios::binary ); @@ -180,18 +201,18 @@ public: if ( false ) { // nothing } else if ( flags.output_extension == "raw" ) { - impl = new raw_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #ifdef MPT_WITH_MMIO } else if ( flags.output_extension == "wav" ) { - impl = new mmio_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #endif #ifdef MPT_WITH_FLAC } else if ( flags.output_extension == "flac" ) { - impl = new flac_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #endif #ifdef MPT_WITH_SNDFILE } else { - impl = new sndfile_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #endif } if ( !impl ) { @@ -199,10 +220,7 @@ public: } } virtual ~file_audio_stream_raii() { - if ( impl ) { - delete impl; - impl = 0; - } + return; } void write_metadata( std::map metadata ) override { impl->write_metadata( metadata ); @@ -441,7 +459,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-2019 OpenMPT developers " << std::endl; + log << "Copyright (c) 2013-2020 OpenMPT developers " << std::endl; if ( !verbose ) { log << std::endl; return; @@ -496,18 +514,6 @@ static void show_info( std::ostream & log, bool verbose ) { log << "API: " << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ) << ""; log << " " << std::endl; #endif -#ifdef MPT_WITH_SDL - const SDL_version * linked_sdlver = SDL_Linked_Version(); - log << " libSDL "; - if ( linked_sdlver ) { - log << static_cast( linked_sdlver->major ) << "." << static_cast( linked_sdlver->minor ) << "." << static_cast( linked_sdlver->patch ) << " "; - } - SDL_version sdlver; - std::memset( &sdlver, 0, sizeof( SDL_version ) ); - SDL_VERSION( &sdlver ); - log << "(API: " << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ) << ")"; - log << " " << std::endl; -#endif #ifdef MPT_WITH_PULSEAUDIO log << " " << "libpulse, libpulse-simple" << " (headers " << pa_get_headers_version() << ", API " << PA_API_VERSION << ", PROTOCOL " << PA_PROTOCOL_VERSION << ", library " << ( pa_get_library_version() ? pa_get_library_version() : "unknown" ) << ") " << std::endl; #endif @@ -530,7 +536,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-2019 OpenMPT developers " << std::endl; + log << "Copyright (c) 2013-2020 OpenMPT developers " << std::endl; } static void show_short_version( textout & log ) { @@ -711,33 +717,6 @@ static void show_help_keyboard( textout & log ) { } -template < typename T, typename Tmod > -T ctl_get( Tmod & mod, const std::string & ctl ) { - T result = T(); - try { - std::istringstream str; - str.imbue( std::locale::classic() ); - str.str( mod.ctl_get( ctl ) ); - str >> std::fixed >> std::setprecision(16) >> result; - } catch ( const openmpt::exception & ) { - // ignore - } - return result; -} - -template < typename T, typename Tmod > -void ctl_set( Tmod & mod, const std::string & ctl, const T & val ) { - try { - std::ostringstream str; - str.imbue( std::locale::classic() ); - str << std::fixed << std::setprecision(16) << val; - mod.ctl_set( ctl, str.str() ); - } catch ( const openmpt::exception & ) { - // ignore - } - return; -} - template < typename Tmod > static void apply_mod_settings( commandlineflags & flags, Tmod & mod ) { flags.separation = std::max( flags.separation, std::int32_t( 0 ) ); @@ -753,12 +732,17 @@ static void apply_mod_settings( commandlineflags & flags, Tmod & mod ) { mod.set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, flags.separation ); mod.set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, flags.filtertaps ); mod.set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, flags.ramping ); - ctl_set( mod, "play.tempo_factor", tempo_flag_to_double( flags.tempo ) ); - ctl_set( mod, "play.pitch_factor", pitch_flag_to_double( flags.pitch ) ); - std::ostringstream dither_str; - dither_str.imbue( std::locale::classic() ); - dither_str << flags.dither; - mod.ctl_set( "dither", dither_str.str() ); + try { + mod.ctl_set_floatingpoint( "play.tempo_factor", tempo_flag_to_double( flags.tempo ) ); + } catch ( const openmpt::exception & ) { + // ignore + } + try { + mod.ctl_set_floatingpoint( "play.pitch_factor", pitch_flag_to_double( flags.pitch ) ); + } catch ( const openmpt::exception & ) { + // ignore + } + mod.ctl_set_integer( "dither", flags.dither ); } struct prev_file { int count; prev_file( int c ) : count(c) { } }; @@ -1036,9 +1020,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto log.writeout(); std::size_t bufsize; - if ( flags.mode == ModeUI ) { + if ( flags.mode == Mode::UI ) { bufsize = std::min( flags.ui_redraw_interval, flags.period ) * flags.samplerate / 1000; - } else if ( flags.mode == ModeBatch ) { + } else if ( flags.mode == Mode::Batch ) { bufsize = flags.period * flags.samplerate / 1000; } else { bufsize = 1024; @@ -1101,28 +1085,11 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto log.writeout(); -#if defined( WIN32 ) - HANDLE hStdErr = NULL; - COORD coord_cursor = COORD(); - if ( multiline ) { - log.flush(); - hStdErr = GetStdHandle( STD_ERROR_HANDLE ); - if ( hStdErr ) { - CONSOLE_SCREEN_BUFFER_INFO csbi; - ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); - GetConsoleScreenBufferInfo( hStdErr, &csbi ); - coord_cursor = csbi.dwCursorPosition; - coord_cursor.X = 1; - coord_cursor.Y -= lines; - } - } -#endif - double cpu_smooth = 0.0; while ( true ) { - if ( flags.mode == ModeUI ) { + if ( flags.mode == Mode::UI ) { #if defined( __DJGPP__ ) @@ -1133,6 +1100,15 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto } } +#elif defined( WIN32 ) && defined( UNICODE ) + + while ( _kbhit() ) { + wint_t c = _getwch(); + if ( !handle_keypress( c, flags, mod, audio_stream ) ) { + return; + } + } + #elif defined( WIN32 ) while ( _kbhit() ) { @@ -1215,16 +1191,7 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto } if ( multiline ) { -#if defined( WIN32 ) - log.flush(); - if ( hStdErr ) { - SetConsoleCursorPosition( hStdErr, coord_cursor ); - } -#else - for ( int line = 0; line < lines; ++line ) { - log << "\x1b[1A"; - } -#endif + log.cursor_up( lines ); log << std::endl; if ( flags.show_meters ) { draw_meters( log, meter, flags ); @@ -1506,7 +1473,7 @@ void render_mod_file( commandlineflags & flags, const std::string & filename, st log.writeout(); - if ( flags.mode != ModeProbe && flags.mode != ModeInfo ) { + if ( flags.mode != Mode::Probe && flags.mode != Mode::Info ) { mod.set_repeat_count( flags.repeatcount ); apply_mod_settings( flags, mod ); } @@ -1576,13 +1543,13 @@ void render_mod_file( commandlineflags & flags, const std::string & filename, st log.writeout(); - if ( flags.filenames.size() == 1 || flags.mode == ModeRender ) { + if ( flags.filenames.size() == 1 || flags.mode == Mode::Render ) { audio_stream.write_metadata( get_metadata( mod ) ); } else { audio_stream.write_updated_metadata( get_metadata( mod ) ); } - if ( flags.mode == ModeProbe || flags.mode == ModeInfo ) { + if ( flags.mode == Mode::Probe || flags.mode == Mode::Info ) { return; } @@ -2018,15 +1985,15 @@ static commandlineflags parse_openmpt123( const std::vector & args, } else if ( arg == "--license" ) { throw show_license_exception(); } else if ( arg == "--probe" ) { - flags.mode = ModeProbe; + flags.mode = Mode::Probe; } else if ( arg == "--info" ) { - flags.mode = ModeInfo; + flags.mode = Mode::Info; } else if ( arg == "--ui" ) { - flags.mode = ModeUI; + flags.mode = Mode::UI; } else if ( arg == "--batch" ) { - flags.mode = ModeBatch; + flags.mode = Mode::Batch; } else if ( arg == "--render" ) { - flags.mode = ModeRender; + flags.mode = Mode::Render; } else if ( arg == "--terminal-width" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.terminal_width; @@ -2072,9 +2039,6 @@ static commandlineflags parse_openmpt123( const std::vector & args, #if defined( MPT_WITH_SDL2 ) drivers << " " << "sdl2" << std::endl; #endif -#if defined( MPT_WITH_SDL ) - drivers << " " << "sdl" << std::endl; -#endif #if defined( MPT_WITH_PORTAUDIO ) drivers << " " << "portaudio" << std::endl; #endif @@ -2245,21 +2209,33 @@ static commandlineflags parse_openmpt123( const std::vector & args, #if defined(WIN32) -class ConsoleCP_utf8_raii { +class FD_utf8_raii { private: - const UINT oldCP; - const UINT oldOutputCP; + FILE * file; + int old_mode; public: - ConsoleCP_utf8_raii() - : oldCP(GetConsoleCP()) - , oldOutputCP(GetConsoleOutputCP()) + FD_utf8_raii( FILE * file, bool set_utf8 ) + : file(file) + , old_mode(-1) { - SetConsoleCP( 65001 ); // UTF-8 - SetConsoleOutputCP( 65001 ); // UTF-8 + if ( set_utf8 ) { + fflush( file ); + #if defined(UNICODE) + old_mode = _setmode( _fileno( file ), _O_U8TEXT ); + #else + old_mode = _setmode( _fileno( file ), _O_TEXT ); + #endif + if ( old_mode == -1 ) { + throw exception( "failed to set TEXT mode on file descriptor" ); + } + } } - ~ConsoleCP_utf8_raii() { - SetConsoleCP( oldCP ); - SetConsoleOutputCP( oldOutputCP ); + ~FD_utf8_raii() + { + if ( old_mode != -1 ) { + fflush( file ); + old_mode = _setmode( _fileno( file ), old_mode ); + } } }; @@ -2268,7 +2244,7 @@ private: FILE * file; int old_mode; public: - FD_binary_raii(FILE * file, bool set_binary) + FD_binary_raii( FILE * file, bool set_binary ) : file(file) , old_mode(-1) { @@ -2306,12 +2282,14 @@ static int main( int argc, char * argv [] ) { #endif #if defined(WIN32) - ConsoleCP_utf8_raii console_cp; + FD_utf8_raii stdin_utf8_guard( stdin, true ); + FD_utf8_raii stdout_utf8_guard( stdout, true ); + FD_utf8_raii stderr_utf8_guard( stderr, true ); #endif textout_dummy dummy_log; #if defined(WIN32) - textout_console std_out( GetStdHandle( STD_OUTPUT_HANDLE ) ); - textout_console std_err( GetStdHandle( STD_ERROR_HANDLE ) ); + textout_ostream_console std_out( std::wcout, STD_OUTPUT_HANDLE ); + textout_ostream_console std_err( std::wclog, STD_ERROR_HANDLE ); #else textout_ostream std_out( std::cout ); textout_ostream std_err( std::clog ); @@ -2398,16 +2376,16 @@ static int main( int argc, char * argv [] ) { // setup terminal #if !defined(WIN32) if ( stdin_can_ui ) { - if ( flags.mode == ModeUI ) { + if ( flags.mode == Mode::UI ) { set_input_mode(); } } #endif - textout & log = flags.quiet ? *static_cast( &dummy_log ) : *static_cast( stdout_can_ui ? &std_out : &std_err ); + textout & log = flags.quiet ? static_cast( dummy_log ) : static_cast( stdout_can_ui ? std_out : std_err ); show_info( log, flags.verbose ); - + if ( !flags.warnings.empty() ) { log << flags.warnings << std::endl; } @@ -2418,24 +2396,30 @@ static int main( int argc, char * argv [] ) { log.writeout(); - std::random_device rd; - std::seed_seq seq{ rd(), static_cast( std::time( NULL ) ) }; - std::default_random_engine prng( seq ); + std::default_random_engine prng; + try { + std::random_device rd; + std::seed_seq seq{ rd(), static_cast( std::time( NULL ) ) }; + prng = std::default_random_engine{ seq }; + } catch ( const std::exception & ) { + std::seed_seq seq{ static_cast( std::time( NULL ) ) }; + prng = std::default_random_engine{ seq }; + } std::srand( std::uniform_int_distribution()( prng ) ); switch ( flags.mode ) { - case ModeProbe: { + case Mode::Probe: { for ( const auto & filename : flags.filenames ) { probe_file( flags, filename, log ); flags.playlist_index++; } } break; - case ModeInfo: { + case Mode::Info: { void_audio_stream dummy; render_files( flags, log, dummy, prng ); } break; - case ModeUI: - case ModeBatch: { + case Mode::UI: + case Mode::Batch: { if ( flags.use_stdout ) { flags.apply_default_buffer_sizes(); stdout_stream_raii stdout_audio_stream; @@ -2454,11 +2438,6 @@ static int main( int argc, char * argv [] ) { sdl2_stream_raii sdl2_stream( flags, log ); render_files( flags, log, sdl2_stream, prng ); #endif -#if defined( MPT_WITH_SDL ) - } else if ( flags.driver == "sdl" || flags.driver.empty() ) { - sdl_stream_raii sdl_stream( flags, log ); - render_files( flags, log, sdl_stream, prng ); -#endif #if defined( MPT_WITH_PORTAUDIO ) } else if ( flags.driver == "portaudio" || flags.driver.empty() ) { portaudio_stream_raii portaudio_stream( flags, log ); @@ -2482,7 +2461,7 @@ static int main( int argc, char * argv [] ) { } } } break; - case ModeRender: { + case Mode::Render: { for ( const auto & filename : flags.filenames ) { flags.apply_default_buffer_sizes(); file_audio_stream_raii file_audio_stream( flags, filename + std::string(".") + flags.output_extension, log ); @@ -2490,7 +2469,7 @@ static int main( int argc, char * argv [] ) { flags.playlist_index++; } } break; - case ModeNone: + case Mode::None: break; } @@ -2515,12 +2494,6 @@ static int main( int argc, char * argv [] ) { std_err.writeout(); return 1; #endif -#ifdef MPT_WITH_SDL - } catch ( sdl_exception & e ) { - std_err << "SDL error: " << e.what() << std::endl; - std_err.writeout(); - return 1; -#endif #ifdef MPT_WITH_SDL2 } catch ( sdl2_exception & e ) { std_err << "SDL2 error: " << e.what() << std::endl; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp index 89104c9e4..89a28bf67 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp @@ -31,84 +31,743 @@ struct args_error_exception { struct show_help_keyboard_exception { }; #if defined(WIN32) +bool IsConsole( DWORD stdHandle ); +#endif +bool IsTerminal( int fd ); -std::string wstring_to_utf8( const std::wstring & unicode_string ) { - int required_size = WideCharToMultiByte( CP_UTF8, 0, unicode_string.c_str(), -1, NULL, 0, NULL, NULL ); - if ( required_size <= 0 ) { - return std::string(); - } - std::vector utf8_buf( required_size ); - WideCharToMultiByte( CP_UTF8, 0, unicode_string.c_str(), -1, &utf8_buf[0], required_size, NULL, NULL ); - return &utf8_buf[0]; -} -std::wstring utf8_to_wstring( const std::string & utf8_string ) { - int required_size = MultiByteToWideChar( CP_UTF8, 0, utf8_string.c_str(), -1, NULL, 0 ); - if ( required_size <= 0 ) { - return std::wstring(); - } - std::vector unicode_buf( required_size ); - MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), -1, &unicode_buf[0], required_size ); - return &unicode_buf[0]; -} - -std::string wstring_to_locale( const std::wstring & unicode_string ) { - int required_size = WideCharToMultiByte( CP_ACP, 0, unicode_string.c_str(), -1, NULL, 0, NULL, NULL ); - if ( required_size <= 0 ) { - return std::string(); - } - std::vector locale_buf( required_size ); - WideCharToMultiByte( CP_ACP, 0, unicode_string.c_str(), -1, &locale_buf[0], required_size, NULL, NULL ); - return &locale_buf[0]; -} - -std::wstring locale_to_wstring( const std::string & locale_string ) { - int required_size = MultiByteToWideChar( CP_ACP, 0, locale_string.c_str(), -1, NULL, 0 ); - if ( required_size <= 0 ) { - return std::wstring(); - } - std::vector unicode_buf( required_size ); - MultiByteToWideChar( CP_ACP, 0, locale_string.data(), -1, &unicode_buf[0], required_size ); - return &unicode_buf[0]; -} - -#endif // WIN32 - -#if defined(MPT_NEEDS_THREADS) #if defined(WIN32) -class mutex { -private: - CRITICAL_SECTION impl; -public: - mutex() { InitializeCriticalSection(&impl); } - ~mutex() { DeleteCriticalSection(&impl); } - void lock() { EnterCriticalSection(&impl); } - void unlock() { LeaveCriticalSection(&impl); } -}; -#else -class mutex { -private: - pthread_mutex_t impl; -public: - mutex() { - pthread_mutexattr_t attr; - pthread_mutexattr_init( &attr ); - pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_NORMAL ); - pthread_mutex_init( &impl, &attr ); - pthread_mutexattr_destroy( &attr ); +static inline std::string wstring_to_utf8( const std::wstring & unicode_string ) { + std::string utf8_string; + int source_length = ( unicode_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( unicode_string.size() ) : std::numeric_limits::max(); + int required_size = WideCharToMultiByte( CP_UTF8, 0, unicode_string.data(), source_length, NULL, 0, NULL, NULL ); + if ( required_size > 0 ) { + utf8_string.resize( required_size ); + WideCharToMultiByte( CP_UTF8, 0, unicode_string.data(), source_length, utf8_string.data(), required_size, NULL, NULL ); } - ~mutex() { pthread_mutex_destroy( &impl ); } - void lock() { pthread_mutex_lock( &impl ); } - void unlock() { pthread_mutex_unlock( &impl ); } + return utf8_string; +} + +static inline std::wstring utf8_to_wstring( const std::string & utf8_string ) { + std::wstring unicode_string; + int source_length = ( utf8_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( utf8_string.size() ) : std::numeric_limits::max(); + int required_size = MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), source_length, NULL, 0 ); + if ( required_size > 0 ) { + unicode_string.resize( required_size ); + MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), source_length, unicode_string.data(), required_size ); + } + return unicode_string; +} + +static inline std::string wstring_to_locale( const std::wstring & unicode_string ) { + std::string locale_string; + int source_length = ( unicode_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( unicode_string.size() ) : std::numeric_limits::max(); + int required_size = WideCharToMultiByte( CP_ACP, 0, unicode_string.data(), source_length, NULL, 0, NULL, NULL ); + if ( required_size > 0 ) { + locale_string.resize( required_size ); + WideCharToMultiByte( CP_ACP, 0, unicode_string.data(), source_length, locale_string.data(), required_size, NULL, NULL ); + } + return locale_string; +} + +static inline std::wstring locale_to_wstring( const std::string & locale_string ) { + std::wstring unicode_string; + int source_length = ( locale_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( locale_string.size() ) : std::numeric_limits::max(); + int required_size = MultiByteToWideChar( CP_ACP, 0, locale_string.data(), source_length, NULL, 0 ); + if ( required_size > 0 ) { + unicode_string.resize( required_size ); + MultiByteToWideChar( CP_ACP, 0, locale_string.data(), source_length, unicode_string.data(), required_size ); + } + return unicode_string; +} + + + +#endif + + + +/* +default 1:1 mapping +static constexpr char32_t CharsetTableISO8859_1[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f, + 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; +*/ + +static constexpr char32_t CharsetTableISO8859_15[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f, + 0x00a0,0x00a1,0x00a2,0x00a3,0x20ac,0x00a5,0x0160,0x00a7,0x0161,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x017d,0x00b5,0x00b6,0x00b7,0x017e,0x00b9,0x00ba,0x00bb,0x0152,0x0153,0x0178,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff }; -#endif +static constexpr char32_t CharsetTableWindows1252[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x20ac,0x0081,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,0x02c6,0x2030,0x0160,0x2039,0x0152,0x008d,0x017d,0x008f, + 0x0090,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,0x02dc,0x2122,0x0161,0x203a,0x0153,0x009d,0x017e,0x0178, + 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; + +static constexpr char32_t CharsetTableCP437[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, + 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, + 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static constexpr char32_t CharsetTableCP850[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00F8,0x00a3,0x00D8,0x00D7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x00AE,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x00C1,0x00C2,0x00C0,0x00A9,0x2563,0x2551,0x2557,0x255d,0x00A2,0x00A5,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00E3,0x00C3,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00A4, + 0x00F0,0x00D0,0x00CA,0x00CB,0x00C8,0x0131,0x00CD,0x00CE,0x00CF,0x2518,0x250c,0x2588,0x2584,0x00A6,0x00CC,0x2580, + 0x00D3,0x00df,0x00D4,0x00D2,0x00F5,0x00D5,0x00b5,0x00FE,0x00DE,0x00DA,0x00DB,0x00D9,0x00FD,0x00DD,0x00AF,0x00B4, + 0x00AD,0x00b1,0x2017,0x00BE,0x00B6,0x00A7,0x00f7,0x00B8,0x00b0,0x00A8,0x00b7,0x00B9,0x00B3,0x00b2,0x25a0,0x00a0 +}; + + +#if defined(__DJGPP__) +using widechar = char32_t; +using widestring = std::u32string; +static constexpr widechar wide_default_replacement = 0xFFFD; +#else // !__DJGPP__ +using widechar = wchar_t; +using widestring = std::wstring; +static constexpr widechar wide_default_replacement = L'\uFFFD'; +#endif // !__DJGPP__ + + +static inline widestring From8bit(const std::string &str, const char32_t (&table)[256], widechar replacement = wide_default_replacement) +{ + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + std::size_t c = static_cast(static_cast(str[i])); + if(c < std::size(table)) + { + res.push_back(static_cast(table[c])); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static inline std::string To8bit(const widestring &str, const char32_t (&table)[256], char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + bool found = false; + // Try non-control characters first. + // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets), + // characters in the common range are preferred this way. + for(std::size_t x = 0x20; x < std::size(table); ++x) + { + if(c == table[x]) + { + res.push_back(static_cast(static_cast(x))); + found = true; + break; + } + } + if(!found) + { + // try control characters + for(std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) + { + if(c == table[x]) + { + res.push_back(static_cast(static_cast(x))); + found = true; + break; + } + } + } + if(!found) + { + res.push_back(replacement); + } + } + return res; +} + +static inline widestring FromAscii(const std::string &str, widechar replacement = wide_default_replacement) +{ + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + std::uint8_t c = str[i]; + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static inline std::string ToAscii(const widestring &str, char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static inline widestring FromISO_8859_1(const std::string &str, widechar replacement = wide_default_replacement) +{ + static_cast( replacement ); + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + std::uint8_t c = str[i]; + res.push_back(static_cast(static_cast(c))); + } + return res; +} + +static inline std::string ToISO_8859_1(const widestring &str, char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + if(c <= 0xff) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + + +#if !defined(__DJGPP__) && !defined(__EMSCRIPTEN__) + +// Note: +// +// std::codecvt::out in LLVM libc++ does not advance in and out pointers when +// running into a non-convertible character. This can happen when no locale is +// set on FreeBSD or MacOSX. This behaviour violates the C++ standard. +// +// We apply the following (albeit costly, even on other platforms) work-around: +// If the conversion errors out and does not advance the pointers at all, we +// retry the conversion with a space character prepended to the string. If it +// still does error out, we retry the whole conversion character by character. +// This is costly even on other platforms in one single case: The first +// character is an invalid Unicode code point or otherwise not convertible. Any +// following non-convertible characters are not a problem. + +static inline std::wstring LocaleDecode(const std::string &str, const std::locale & locale, wchar_t replacement = L'\uFFFD', int retry = 0, bool * progress = nullptr) +{ + if(str.empty()) + { + return std::wstring(); + } + std::vector out; + using codecvt_type = std::codecvt; + std::mbstate_t state = std::mbstate_t(); + const codecvt_type & facet = std::use_facet(locale); + codecvt_type::result result = codecvt_type::partial; + const char * in_begin = str.data(); + const char * in_end = in_begin + str.size(); + out.resize((in_end - in_begin) * (facet.max_length() + 1)); + wchar_t * out_begin = &(out[0]); + wchar_t * out_end = &(out[0]) + out.size(); + const char * in_next = nullptr; + wchar_t * out_next = nullptr; + do + { + if(retry == 2) + { + for(;;) + { + in_next = nullptr; + out_next = nullptr; + result = facet.in(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); + if(result == codecvt_type::partial && in_next == in_begin + 1) + { + in_begin = in_next; + out_begin = out_next; + continue; + } else + { + break; + } + } + } else + { + in_next = nullptr; + out_next = nullptr; + result = facet.in(state, in_begin, in_end, in_next, out_begin, out_end, out_next); + } + if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) + { + out.resize(out.size() * 2); + in_begin = in_next; + out_begin = &(out[0]) + (out_next - out_begin); + out_end = &(out[0]) + out.size(); + continue; + } + if(retry == 0) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + bool made_progress = true; + LocaleDecode(std::string(" ") + str, locale, replacement, 1, &made_progress); + if(!made_progress) + { + return LocaleDecode(str, locale, replacement, 2); + } + } + } else if(retry == 1) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + *progress = false; + } else + { + *progress = true; + } + return std::wstring(); + } + if(result == codecvt_type::error) + { + ++in_next; + *out_next = replacement; + ++out_next; + } + in_begin = in_next; + out_begin = out_next; + } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); + return std::wstring(&(out[0]), out_next); +} + +static inline std::string LocaleEncode(const std::wstring &str, const std::locale & locale, char replacement = '?', int retry = 0, bool * progress = nullptr) +{ + if(str.empty()) + { + return std::string(); + } + std::vector out; + using codecvt_type = std::codecvt; + std::mbstate_t state = std::mbstate_t(); + const codecvt_type & facet = std::use_facet(locale); + codecvt_type::result result = codecvt_type::partial; + const wchar_t * in_begin = str.data(); + const wchar_t * in_end = in_begin + str.size(); + out.resize((in_end - in_begin) * (facet.max_length() + 1)); + char * out_begin = &(out[0]); + char * out_end = &(out[0]) + out.size(); + const wchar_t * in_next = nullptr; + char * out_next = nullptr; + do + { + if(retry == 2) + { + for(;;) + { + in_next = nullptr; + out_next = nullptr; + result = facet.out(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); + if(result == codecvt_type::partial && in_next == in_begin + 1) + { + in_begin = in_next; + out_begin = out_next; + continue; + } else + { + break; + } + } + } else + { + in_next = nullptr; + out_next = nullptr; + result = facet.out(state, in_begin, in_end, in_next, out_begin, out_end, out_next); + } + if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) + { + out.resize(out.size() * 2); + in_begin = in_next; + out_begin = &(out[0]) + (out_next - out_begin); + out_end = &(out[0]) + out.size(); + continue; + } + if(retry == 0) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + bool made_progress = true; + LocaleEncode(std::wstring(L" ") + str, locale, replacement, 1, &made_progress); + if(!made_progress) + { + return LocaleEncode(str, locale, replacement, 2); + } + } + } else if(retry == 1) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + *progress = false; + } else + { + *progress = true; + } + return std::string(); + } + if(result == codecvt_type::error) + { + ++in_next; + *out_next = replacement; + ++out_next; + } + in_begin = in_next; + out_begin = out_next; + } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); + return std::string(&(out[0]), out_next); +} + +static inline std::wstring FromLocaleCpp(const std::string &str, wchar_t replacement) +{ + try + { + std::locale locale(""); // user locale + return LocaleDecode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return LocaleDecode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return LocaleDecode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + assert(0); + return FromAscii(str, replacement); // fallback +} + +static inline std::string ToLocaleCpp(const std::wstring &str, char replacement) +{ + try + { + std::locale locale(""); // user locale + return LocaleEncode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return LocaleEncode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return LocaleEncode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + assert(0); + return ToAscii(str, replacement); // fallback +} + + +static inline std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\uFFFD') +{ + return FromLocaleCpp(str, replacement); +} + +static inline std::string ToLocale(const std::wstring &str, char replacement = '?') +{ + return ToLocaleCpp(str, replacement); +} + + + +#endif // !__DJGPP__ && !__EMSCRIPTEN + + + +static inline widestring FromUTF8(const std::string &str, widechar replacement = wide_default_replacement) +{ + const std::string &in = str; + + widestring out; + + // state: + std::size_t charsleft = 0; + char32_t ucs4 = 0; + + for ( std::uint8_t c : in ) { + + if ( charsleft == 0 ) { + + if ( ( c & 0x80 ) == 0x00 ) { + out.push_back( (wchar_t)c ); + } else if ( ( c & 0xE0 ) == 0xC0 ) { + ucs4 = c & 0x1F; + charsleft = 1; + } else if ( ( c & 0xF0 ) == 0xE0 ) { + ucs4 = c & 0x0F; + charsleft = 2; + } else if ( ( c & 0xF8 ) == 0xF0 ) { + ucs4 = c & 0x07; + charsleft = 3; + } else { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + + } else { + + if ( ( c & 0xC0 ) != 0x80 ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + ucs4 <<= 6; + ucs4 |= c & 0x3F; + charsleft--; + + if ( charsleft == 0 ) { + if constexpr ( sizeof( widechar ) == 2 ) { + if ( ucs4 > 0x1fffff ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + if ( ucs4 <= 0xffff ) { + out.push_back( static_cast(ucs4) ); + } else { + std::uint32_t surrogate = static_cast(ucs4) - 0x10000; + std::uint16_t hi_sur = static_cast( ( 0x36 << 10 ) | ( (surrogate>>10) & ((1<<10)-1) ) ); + std::uint16_t lo_sur = static_cast( ( 0x37 << 10 ) | ( (surrogate>> 0) & ((1<<10)-1) ) ); + out.push_back( hi_sur ); + out.push_back( lo_sur ); + } + } else { + out.push_back( static_cast( ucs4 ) ); + } + ucs4 = 0; + } + + } + + } + + if ( charsleft != 0 ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + + return out; + +} + +static inline std::string ToUTF8(const widestring &str, char replacement = '?') +{ + const widestring &in = str; + + std::string out; + + for ( std::size_t i=0; i( wc ); + if ( i + 1 < in.length() ) { + // check for surrogate pair + std::uint16_t hi_sur = in[i+0]; + std::uint16_t lo_sur = in[i+1]; + if ( hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37 ) { + // surrogate pair + ++i; + hi_sur &= (1<<10)-1; + lo_sur &= (1<<10)-1; + ucs4 = ( static_cast(hi_sur) << 10 ) | ( static_cast(lo_sur) << 0 ); + } else { + // no surrogate pair + ucs4 = static_cast( c ); + } + } else { + // no surrogate possible + ucs4 = static_cast( c ); + } + } else { + ucs4 = static_cast( wc ); + } + + if ( ucs4 > 0x1fffff ) { + out.push_back( replacement ); + continue; + } + + std::uint8_t utf8[6]; + std::size_t numchars = 0; + for ( numchars = 0; numchars < 6; numchars++ ) { + utf8[numchars] = ucs4 & 0x3F; + ucs4 >>= 6; + if ( ucs4 == 0 ) { + break; + } + } + numchars++; + + if ( numchars == 1 ) { + out.push_back( utf8[0] ); + continue; + } + + if ( numchars == 2 && utf8[numchars-1] == 0x01 ) { + // generate shortest form + out.push_back( utf8[0] | 0x40 ); + continue; + } + + std::size_t charsleft = numchars; + while ( charsleft > 0 ) { + if ( charsleft == numchars ) { + out.push_back( utf8[ charsleft - 1 ] | ( ((1<( lines ); } }; @@ -145,55 +809,140 @@ public: return; } public: - void write( const std::string & /* text */ ) override { - return; + void writeout() override { + static_cast( pop() ); } }; class textout_ostream : public textout { private: std::ostream & s; +#if defined(__DJGPP__) + std::uint16_t active_codepage; + std::uint16_t system_codepage; +#endif public: textout_ostream( std::ostream & s_ ) : s(s_) +#if defined(__DJGPP__) + , active_codepage(437) + , system_codepage(437) +#endif { + #if defined(__DJGPP__) + __dpmi_regs regs; + std::memset( ®s, 0, sizeof( __dpmi_regs ) ); + regs.x.ax = 0x6601; + if ( __dpmi_int( 0x21, ®s ) == 0 ) { + int cf = ( regs.x.flags >> 0 ) & 1; + if ( cf == 0 ) { + active_codepage = regs.x.bx; + system_codepage = regs.x.dx; + } + } + #endif return; } virtual ~textout_ostream() { - writeout(); + writeout_impl(); + } +private: + void writeout_impl() { + std::string text = pop(); + if ( text.length() > 0 ) { + #if defined(__DJGPP__) + if ( active_codepage == 0 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } else if ( active_codepage == 437 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } else if ( active_codepage == 850 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP850 ); + } else if ( system_codepage == 437 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } else if ( system_codepage == 850 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP850 ); + } else { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } + #elif defined(__EMSCRIPTEN__) + s << text; + #elif defined(WIN32) + s << ToLocale( FromUTF8( text ) ); + #else + s << ToLocale( FromUTF8( text ) ); + #endif + s.flush(); + } } public: - void write( const std::string & text ) override { - s << text; - } void writeout() override { - textout::writeout(); + writeout_impl(); + } + void cursor_up( std::size_t lines ) override { s.flush(); + for ( std::size_t line = 0; line < lines; ++line ) { + *this << "\x1b[1A"; + } } }; #if defined(WIN32) -class textout_console : public textout { +class textout_ostream_console : public textout { private: + std::wostream & s; HANDLE handle; + bool console; public: - textout_console( HANDLE handle_ ) - : handle(handle_) + textout_ostream_console( std::wostream & s_, DWORD stdHandle_ ) + : s(s_) + , handle(GetStdHandle( stdHandle_ )) + , console(IsConsole( stdHandle_ )) { return; } - virtual ~textout_console() { - writeout(); + virtual ~textout_ostream_console() { + writeout_impl(); + } +private: + void writeout_impl() { + std::string text = pop(); + if ( text.length() > 0 ) { + if ( console ) { + #if defined(UNICODE) + std::wstring wtext = utf8_to_wstring( text ); + WriteConsole( handle, wtext.data(), static_cast( wtext.size() ), NULL, NULL ); + #else + std::string ltext = wstring_to_locale( utf8_to_wstring( text ) ); + WriteConsole( handle, ltext.data(), static_cast( ltext.size() ), NULL, NULL ); + #endif + } else { + #if defined(UNICODE) + s << utf8_to_wstring( text ); + #else + s << wstring_to_locale( utf8_to_wstring( text ) ); + #endif + s.flush(); + } + } } public: - 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 ); - #else - WriteConsole( handle, text.data(), static_cast( text.size() ), NULL, NULL ); - #endif + void writeout() override { + writeout_impl(); + } + void cursor_up( std::size_t lines ) override { + if ( console ) { + s.flush(); + CONSOLE_SCREEN_BUFFER_INFO csbi; + ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); + COORD coord_cursor = COORD(); + if ( GetConsoleScreenBufferInfo( handle, &csbi ) != FALSE ) { + coord_cursor = csbi.dwCursorPosition; + coord_cursor.X = 1; + coord_cursor.Y -= static_cast( lines ); + SetConsoleCursorPosition( handle, coord_cursor ); + } + } } }; @@ -232,25 +981,23 @@ static inline std::string get_extension( std::string filename ) { return ""; } -bool IsTerminal( int fd ); - -enum Mode { - ModeNone, - ModeProbe, - ModeInfo, - ModeUI, - ModeBatch, - ModeRender +enum class Mode { + None, + Probe, + Info, + UI, + Batch, + Render }; static inline std::string mode_to_string( Mode mode ) { switch ( mode ) { - case ModeNone: return "none"; break; - case ModeProbe: return "probe"; break; - case ModeInfo: return "info"; break; - case ModeUI: return "ui"; break; - case ModeBatch: return "batch"; break; - case ModeRender: return "render"; break; + case Mode::None: return "none"; break; + case Mode::Probe: return "probe"; break; + case Mode::Info: return "info"; break; + case Mode::UI: return "ui"; break; + case Mode::Batch: return "batch"; break; + case Mode::Render: return "render"; break; } return ""; } @@ -322,7 +1069,7 @@ struct commandlineflags { } } commandlineflags() { - mode = ModeUI; + mode = Mode::UI; ui_redraw_interval = default_high; driver = ""; device = ""; @@ -358,11 +1105,17 @@ struct commandlineflags { terminal_height = 23; #endif #if defined(WIN32) - CONSOLE_SCREEN_BUFFER_INFO csbi; - ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); - GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &csbi ); - terminal_width = csbi.dwSize.X - 1; - terminal_height = 23; //csbi.dwSize.Y - 1; + terminal_width = 72; + terminal_height = 23; + HANDLE hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE ); + if ( ( hStdOutput != NULL ) && ( hStdOutput != INVALID_HANDLE_VALUE ) ) { + CONSOLE_SCREEN_BUFFER_INFO csbi; + ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); + if ( GetConsoleScreenBufferInfo( hStdOutput, &csbi ) != FALSE ) { + terminal_width = std::min( static_cast( 1 + csbi.srWindow.Right - csbi.srWindow.Left ), static_cast( csbi.dwSize.X ) ); + terminal_height = std::min( static_cast( 1 + csbi.srWindow.Bottom - csbi.srWindow.Top ), static_cast( csbi.dwSize.Y ) ); + } + } #else // WIN32 if ( isatty( STDERR_FILENO ) ) { if ( std::getenv( "COLUMNS" ) ) { @@ -435,45 +1188,45 @@ struct commandlineflags { } } show_ui = canUI; - if ( mode == ModeNone ) { + if ( mode == Mode::None ) { if ( canUI ) { - mode = ModeUI; + mode = Mode::UI; } else { - mode = ModeBatch; + mode = Mode::Batch; } } - if ( mode == ModeUI && !canUI ) { + if ( mode == Mode::UI && !canUI ) { throw args_error_exception(); } if ( show_progress && !canProgress ) { throw args_error_exception(); } switch ( mode ) { - case ModeNone: + case Mode::None: throw args_error_exception(); break; - case ModeProbe: + case Mode::Probe: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; - case ModeInfo: + case Mode::Info: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; - case ModeUI: + case Mode::UI: break; - case ModeBatch: + case Mode::Batch: show_meters = false; show_channel_meters = false; show_pattern = false; break; - case ModeRender: + case Mode::Render: show_meters = false; show_channel_meters = false; show_pattern = false; @@ -499,13 +1252,13 @@ struct commandlineflags { if ( output_extension == "auto" ) { output_extension = ""; } - if ( mode != ModeRender && !output_extension.empty() ) { + if ( mode != Mode::Render && !output_extension.empty() ) { throw args_error_exception(); } - if ( mode == ModeRender && !output_filename.empty() ) { + if ( mode == Mode::Render && !output_filename.empty() ) { throw args_error_exception(); } - if ( mode != ModeRender && !output_filename.empty() ) { + if ( mode != Mode::Render && !output_filename.empty() ) { output_extension = get_extension( output_filename ); } if ( output_extension.empty() ) { @@ -555,77 +1308,6 @@ public: } }; -class write_buffers_blocking_wrapper : public write_buffers_interface { -protected: - std::size_t channels; - std::size_t sampleQueueMaxFrames; - std::deque sampleQueue; -protected: - virtual ~write_buffers_blocking_wrapper() { - return; - } -protected: - write_buffers_blocking_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 ); - } - template < typename Tsample > - void fill_buffer( Tsample * buf, std::size_t framesToRender ) { - for ( std::size_t frame = 0; frame < framesToRender; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - *buf = pop_queue(); - buf++; - } - } - } -private: - void wait_for_queue_space() { - while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { - unlock(); - sleep( 1 ); - lock(); - } - } -public: - 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 ) { - wait_for_queue_space(); - sampleQueue.push_back( buffers[channel][frame] ); - } - } - unlock(); - } - 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 ) { - wait_for_queue_space(); - sampleQueue.push_back( buffers[channel][frame] * (1.0f/32768.0f) ); - } - } - unlock(); - } - virtual void lock() = 0; - virtual void unlock() = 0; - bool sleep( int ms ) override = 0; -}; - class write_buffers_polling_wrapper : public write_buffers_interface { protected: std::size_t channels; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp index 9558215ad..7cba28fbc 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp @@ -10,6 +10,36 @@ #ifndef OPENMPT123_CONFIG_HPP #define OPENMPT123_CONFIG_HPP +#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__) +#define MPT_PP_STRINGIFY(x) #x +#define MPT_PP_JOIN_HELPER(a, b) a ## b +#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b) +#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix , __LINE__) + +#if defined(__clang__) +#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#elif defined(_MSC_VER) +#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) +#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) +#elif defined(__GNUC__) +#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#else +#define MPT_WARNING(text) \ + static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) () noexcept { \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + } \ +/**/ +#define MPT_WARNING_STATEMENT(text) \ + int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = [](){ \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + }() \ +/**/ +#endif + #if defined(HAVE_CONFIG_H) // wrapper for autoconf macros #include "config.h" @@ -40,12 +70,6 @@ #define MPT_WITH_MMIO #endif // WIN32 -#if defined(_MSC_VER) - -#pragma warning( disable : 4996 ) // 'foo': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _foo. See online help for details. - -#endif // _MSC_VER - #if defined(MPT_BUILD_MSVC) #define MPT_WITH_FLAC @@ -57,16 +81,6 @@ #endif // MPT_BUILD_MSVC -#if defined(MPT_WITH_SDL) && defined(MPT_WITH_SDL2) -#error "MPT_WITH_SDL2 and MPT_WITH_SDL are mutually exclusive." -#endif - -#if defined(MPT_WITH_SDL) -#ifndef MPT_NEEDS_THREADS -#define MPT_NEEDS_THREADS -#endif -#endif - #define OPENMPT123_VERSION_STRING OPENMPT_API_VERSION_STRING #endif // OPENMPT123_CONFIG_HPP diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp index bc5b75628..969c6530d 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp @@ -21,9 +21,16 @@ typedef _off_t off_t; #endif #endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif #include #include #include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif namespace openmpt123 { diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp index cb44587bd..a7f0f31ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp @@ -47,10 +47,10 @@ public: ZeroMemory( &waveformatex, sizeof( WAVEFORMATEX ) ); waveformatex.cbSize = 0; waveformatex.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; - waveformatex.nChannels = flags.channels; + waveformatex.nChannels = static_cast( flags.channels ); waveformatex.nSamplesPerSec = flags.samplerate; waveformatex.wBitsPerSample = flags.use_float ? 32 : 16; - waveformatex.nBlockAlign = flags.channels * ( waveformatex.wBitsPerSample / 8 ); + waveformatex.nBlockAlign = static_cast( flags.channels * ( waveformatex.wBitsPerSample / 8 ) ); waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign; #if defined(WIN32) && defined(UNICODE) diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp index 75065142e..4444c3c0c 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp @@ -120,18 +120,18 @@ public: streamparameters.suggestedLatency = flags.buffer * 0.001; } unsigned long framesperbuffer = 0; - if ( flags.mode != ModeUI ) { + if ( flags.mode != Mode::UI ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; - flags.period = std::min( flags.period, flags.buffer / 3 ); + flags.period = std::min( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_high ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; - flags.period = std::min( flags.period, flags.buffer / 3 ); + flags.period = std::min( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_low ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 10; - flags.period = std::min( flags.period, flags.buffer / 3 ); + flags.period = std::min( flags.period, flags.buffer / 3 ); } else { framesperbuffer = flags.period * flags.samplerate / 1000; } @@ -187,7 +187,7 @@ private: template void write_frames( const Tsample * buffer, std::size_t frames ) { while ( frames > 0 ) { - unsigned long chunk_frames = static_cast( std::min( frames, std::numeric_limits::max() ) ); + unsigned long chunk_frames = static_cast( std::min( static_cast( frames ), static_cast( std::numeric_limits::max() ) ) ); check_portaudio_error( Pa_WriteStream( stream, buffer, chunk_frames ) ); buffer += chunk_frames * channels; frames -= chunk_frames; @@ -196,7 +196,7 @@ private: template void write_frames( std::vector buffers, std::size_t frames ) { while ( frames > 0 ) { - unsigned long chunk_frames = static_cast( std::min( frames, std::numeric_limits::max() ) ); + unsigned long chunk_frames = static_cast( std::min( static_cast( frames ), static_cast( std::numeric_limits::max() ) ) ); check_portaudio_error( Pa_WriteStream( stream, buffers.data(), chunk_frames ) ); for ( std::size_t channel = 0; channel < channels; ++channel ) { buffers[channel] += chunk_frames; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp deleted file mode 100644 index 55d64e04a..000000000 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * openmpt123_sdl.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_SDL_HPP -#define OPENMPT123_SDL_HPP - -#include "openmpt123_config.hpp" -#include "openmpt123.hpp" - -#if defined(MPT_WITH_SDL) - -#include -#ifdef main -#undef main -#endif -#ifdef SDL_main -#undef SDL_main -#endif - -namespace openmpt123 { - -struct sdl_exception : public exception { - sdl_exception( int /*code*/ ) : exception( "SDL error" ) { } -}; - -class sdl_stream_raii : public write_buffers_blocking_wrapper { -private: - std::ostream & log; - std::size_t channels; -protected: - void check_sdl_error( int e ) { - if ( e < 0 ) { - throw sdl_exception( e ); - return; - } - } - std::uint32_t round_up_power2(std::uint32_t x) - { - std::uint32_t result = 1; - while ( result < x ) { - result *= 2; - } - return result; - } -public: - sdl_stream_raii( commandlineflags & flags, std::ostream & log_ ) - : write_buffers_blocking_wrapper(flags) - , log(log_) - , channels(flags.channels) - { - if ( flags.buffer == default_high ) { - flags.buffer = 160; - } else if ( flags.buffer == default_low ) { - flags.buffer = 80; - } - if ( flags.period == default_high ) { - flags.period = 20; - } else if ( flags.period == default_low ) { - flags.period = 10; - } - flags.apply_default_buffer_sizes(); - check_sdl_error( SDL_Init( SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO ) ); - SDL_AudioSpec audiospec; - std::memset( &audiospec, 0, sizeof( SDL_AudioSpec ) ); - audiospec.freq = flags.samplerate; - audiospec.format = AUDIO_S16SYS; - audiospec.channels = flags.channels; - audiospec.silence = 0; - audiospec.samples = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); - audiospec.size = audiospec.samples * audiospec.channels * sizeof( std::int16_t ); - audiospec.callback = &sdl_callback_wrapper; - audiospec.userdata = this; - if ( flags.verbose ) { - log << "SDL:" << std::endl; - log << " latency: " << ( audiospec.samples * 2.0 / flags.samplerate ) << " (2 * " << audiospec.samples << ")" << std::endl; - log << std::endl; - } - set_queue_size_frames( round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ) ); - check_sdl_error( SDL_OpenAudio( &audiospec, NULL ) ); - SDL_PauseAudio( 0 ); - } - ~sdl_stream_raii() { - SDL_PauseAudio( 1 ); - SDL_CloseAudio(); - SDL_Quit(); - } -private: - static void sdl_callback_wrapper( void * userdata, Uint8 * stream, int len ) { - return reinterpret_cast( userdata )->sdl_callback( stream, len ); - } - void sdl_callback( Uint8 * stream, int len ) { - std::size_t framesToRender = len / sizeof( std::int16_t ) / channels; - for ( std::size_t frame = 0; frame < framesToRender; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - std::int16_t sample = pop_queue(); - std::memcpy( stream, &sample, sizeof( std::int16_t ) ); - stream += sizeof( std::int16_t ); - } - } - } -public: - bool pause() override { - SDL_PauseAudio( 1 ); - return true; - } - bool unpause() override { - SDL_PauseAudio( 0 ); - return true; - } - void lock() override { - SDL_LockAudio(); - } - void unlock() override { - SDL_UnlockAudio(); - } - bool sleep( int ms ) override { - SDL_Delay( ms ); - return true; - } -}; - -} // namespace openmpt123 - -#endif // MPT_WITH_SDL - -#endif // OPENMPT123_SDL_HPP - diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp index 307d67ffe..5acab163d 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp @@ -15,24 +15,41 @@ #if defined(MPT_WITH_SDL2) +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // __clang__ #include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // __clang__ #ifdef main #undef main #endif #ifdef SDL_main #undef SDL_main #endif +#if (SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 4)) +MPT_WARNING("Support for SDL2 < 2.0.4 has been deprecated and will be removed in a future openmpt123 version.") +#endif namespace openmpt123 { struct sdl2_exception : public exception { - sdl2_exception( int /*code*/ ) : exception( "SDL2 error" ) { } +private: + static std::string text_from_code( int code ) { + std::ostringstream s; + s << code; + return s.str(); + } +public: + sdl2_exception( int code, const char * error ) : exception( text_from_code( code ) + " (" + ( error ? std::string(error) : std::string("NULL") ) + ")" ) { } }; static void check_sdl2_error( int e ) { if ( e < 0 ) { - throw sdl2_exception( e ); - return; + throw sdl2_exception( e, SDL_GetError() ); } } @@ -46,13 +63,16 @@ public: } }; -class sdl2_stream_raii : public write_buffers_blocking_wrapper { +class sdl2_stream_raii : public write_buffers_interface { private: std::ostream & log; sdl2_raii sdl2; int dev; std::size_t channels; bool use_float; + std::size_t sampleQueueMaxFrames; + std::vector sampleBufFloat; + std::vector sampleBufInt; protected: std::uint32_t round_up_power2(std::uint32_t x) { @@ -64,12 +84,12 @@ protected: } public: sdl2_stream_raii( commandlineflags & flags, std::ostream & log_ ) - : write_buffers_blocking_wrapper(flags) - , log(log_) + : log(log_) , sdl2( SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO ) , dev(-1) , channels(flags.channels) , use_float(flags.use_float) + , sampleQueueMaxFrames(0) { if ( flags.buffer == default_high ) { flags.buffer = 160; @@ -90,14 +110,14 @@ public: audiospec.silence = 0; audiospec.samples = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); audiospec.size = audiospec.samples * audiospec.channels * ( flags.use_float ? sizeof( float ) : sizeof( std::int16_t ) ); - audiospec.callback = &sdl2_callback_wrapper; - audiospec.userdata = this; + audiospec.callback = NULL; + audiospec.userdata = NULL; if ( flags.verbose ) { log << "SDL2:" << std::endl; log << " latency: " << ( audiospec.samples * 2.0 / flags.samplerate ) << " (2 * " << audiospec.samples << ")" << std::endl; log << std::endl; } - set_queue_size_frames( round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ) ); + sampleQueueMaxFrames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); SDL_AudioSpec audiospec_obtained; std::memset( &audiospec_obtained, 0, sizeof( SDL_AudioSpec ) ); std::memcpy( &audiospec_obtained, &audiospec, sizeof( SDL_AudioSpec ) ); @@ -114,24 +134,45 @@ public: SDL_CloseAudioDevice( dev ); } private: - static void sdl2_callback_wrapper( void * userdata, Uint8 * stream, int len ) { - return reinterpret_cast( userdata )->sdl2_callback( stream, len ); + std::size_t get_num_writeable_frames() { + std::size_t num_queued_frames = SDL_GetQueuedAudioSize( dev ) / ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) / channels; + if ( num_queued_frames > sampleQueueMaxFrames ) { + return 0; + } + return sampleQueueMaxFrames - num_queued_frames; } - void sdl2_callback( Uint8 * stream, int len ) { - return ( use_float ? sdl2_callback_impl( stream, len ) : sdl2_callback_impl( stream, len ) ); - } - template < typename Tsample > - void sdl2_callback_impl( Uint8 * stream, int len ) { - std::size_t framesToRender = len / sizeof( Tsample ) / channels; - for ( std::size_t frame = 0; frame < framesToRender; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - Tsample sample = pop_queue(); - std::memcpy( stream, &sample, sizeof( Tsample ) ); - stream += sizeof( Tsample ); + template + void write_frames( const Tsample * buffer, std::size_t frames ) { + while ( frames > 0 ) { + std::size_t chunk_frames = std::min( frames, get_num_writeable_frames() ); + if ( chunk_frames > 0 ) { + check_sdl2_error( SDL_QueueAudio( dev, buffer, chunk_frames * channels * ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) ) ); + frames -= chunk_frames; + buffer += chunk_frames * channels; + } else { + SDL_Delay( 1 ); } } } public: + void write( const std::vector buffers, std::size_t frames ) override { + sampleBufFloat.clear(); + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleBufFloat.push_back( buffers[channel][frame] ); + } + } + write_frames( sampleBufFloat.data(), frames ); + } + void write( const std::vector buffers, std::size_t frames ) override { + sampleBufInt.clear(); + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleBufInt.push_back( buffers[channel][frame] ); + } + } + write_frames( sampleBufInt.data(), frames ); + } bool pause() override { SDL_PauseAudioDevice( dev, 1 ); return true; @@ -140,12 +181,6 @@ public: SDL_PauseAudioDevice( dev, 0 ); return true; } - void lock() override { - SDL_LockAudioDevice( dev ); - } - void unlock() override { - SDL_UnlockAudioDevice( dev ); - } bool sleep( int ms ) override { SDL_Delay( ms ); return true; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp index e19a0640d..5598f709a 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp @@ -53,7 +53,7 @@ public: WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof( wfx ) ); wfx.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; - wfx.nChannels = flags.channels; + wfx.nChannels = static_cast( flags.channels ); wfx.nSamplesPerSec = flags.samplerate; wfx.wBitsPerSample = flags.use_float ? 32 : 16; wfx.nBlockAlign = ( wfx.wBitsPerSample / 8 ) * wfx.nChannels; diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.cpp b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.cpp new file mode 100644 index 000000000..ac1130cf0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.cpp @@ -0,0 +1,34 @@ +/* + * Dither.cpp + * ---------- + * Purpose: Dithering when converting to lower resolution sample formats. + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" + +#include "Dither.h" + +#include "../common/misc_util.h" + + +OPENMPT_NAMESPACE_BEGIN + + +mpt::ustring DitherNames::GetModeName(DitherMode mode) +{ + switch(mode) + { + 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; + } +} + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h new file mode 100644 index 000000000..319f63671 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h @@ -0,0 +1,330 @@ +/* + * Dither.h + * -------- + * Purpose: Dithering when converting to lower resolution sample formats. + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "SampleTypes.h" +#include "SampleFormatConverters.h" +#include "../common/mptRandom.h" + + +OPENMPT_NAMESPACE_BEGIN + + +enum DitherMode +{ + DitherNone = 0, + DitherDefault = 1, // chosen by OpenMPT code, might change + DitherModPlug = 2, // rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker) + DitherSimple = 3, // rectangular, 1 bit depth, simple 1st order noise shaping + NumDitherModes +}; + + +struct Dither_None +{ +public: + template + MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &) + { + return sample; + } + template + MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &) + { + return sample; + } +}; + + +struct Dither_ModPlug +{ +public: + template + MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &rng) + { + if constexpr(targetbits == 0) + { + MPT_UNREFERENCED_PARAMETER(rng); + return sample; + } else if constexpr(targetbits + MixSampleIntTraits::mix_headroom_bits() + 1 >= 32) + { + MPT_UNREFERENCED_PARAMETER(rng); + return sample; + } else + { + sample += mpt::rshift_signed(static_cast(mpt::random(rng)), (targetbits + MixSampleIntTraits::mix_headroom_bits() + 1)); + return sample; + } + } + template + MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng) + { + SC::ConvertToFixedPoint conv1; + SC::ConvertFixedPoint conv2; + return conv2(process(conv1(sample), prng)); + } +}; + + +template +struct Dither_SimpleImpl +{ +private: + int32 error = 0; +public: + template + MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &prng) + { + if constexpr(targetbits == 0) + { + MPT_UNREFERENCED_PARAMETER(prng); + return sample; + } else + { + static_assert(sizeof(MixSampleInt) == 4); + constexpr int rshift = (32-targetbits) - MixSampleIntTraits::mix_headroom_bits(); + if constexpr(rshift <= 1) + { + MPT_UNREFERENCED_PARAMETER(prng); + // nothing to dither + return sample; + } else + { + constexpr int rshiftpositive = (rshift > 1) ? rshift : 1; // work-around warnings about negative shift with C++14 compilers + constexpr int round_mask = ~((1<(prng, noise_bits) + mpt::random(prng, noise_bits)) >> 1; + } else + { + unoise = mpt::random(prng, noise_bits); + } + int noise = static_cast(unoise) - noise_bias; // un-bias + int val = sample; + if constexpr(shaped) + { + val += (e >> 1); + } + int rounded = (val + noise + round_offset) & round_mask;; + e = val - rounded; + sample = rounded; + error = e; + return sample; + } + } + } + template + MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng) + { + SC::ConvertToFixedPoint conv1; + SC::ConvertFixedPoint conv2; + return conv2(process(conv1(sample), prng)); + } +}; + +using Dither_Simple = Dither_SimpleImpl<>; + + +template +class MultiChannelDither +{ +private: + std::array DitherChannels; +public: + void Reset() + { + DitherChannels.fill(Tdither()); + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample, Trng &prng) + { + return DitherChannels[channel].template process(sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample, Trng &prng) + { + return DitherChannels[channel].template process(sample, prng); + } +}; + + +template +class DitherTemplate; + +template +class DitherTemplate + : public MultiChannelDither +{ + struct {} prng; +public: + template + DitherTemplate(Trd &) + { + return; + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } +}; + +template +class DitherTemplate + : public MultiChannelDither +{ +private: + mpt::rng::modplug_dither prng; +public: + template + DitherTemplate(Trd &) + : prng(0, 0) + { + return; + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } +}; + +template +class DitherTemplate + : public MultiChannelDither +{ +private: + mpt::fast_prng prng; +public: + template + DitherTemplate(Trd & rd) + : prng(rd) + { + return; + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } +}; + + +class DitherNames +{ +public: + static mpt::ustring GetModeName(DitherMode mode); +}; + + +template +class DitherChannels + : public DitherNames +{ + +private: + + DitherTemplate ditherNone; + DitherTemplate ditherModPlug; + DitherTemplate ditherSimple; + + DitherMode mode = DitherDefault; + +public: + + template + DitherChannels(Trd & rd) + : ditherNone(rd) + , ditherModPlug(rd) + , ditherSimple(rd) + { + return; + } + void Reset() + { + ditherModPlug.Reset(); + ditherSimple.Reset(); + } + + DitherTemplate & NoDither() + { + MPT_ASSERT(mode == DitherNone); + return ditherNone; + } + DitherTemplate & DefaultDither() + { + MPT_ASSERT(mode == DitherDefault); + return ditherModPlug; + } + DitherTemplate & ModPlugDither() + { + MPT_ASSERT(mode == DitherModPlug); + return ditherModPlug; + } + DitherTemplate & SimpleDither() + { + MPT_ASSERT(mode == DitherSimple); + return ditherSimple; + } + + template + auto WithDither(Tfn fn) + { + switch(GetMode()) + { + case DitherNone: return fn(NoDither()); break; + case DitherModPlug: return fn(ModPlugDither()); break; + case DitherSimple: return fn(SimpleDither()); break; + case DitherDefault: return fn(DefaultDither()); break; + default: return fn(DefaultDither()); break; + } + } + + void SetMode(DitherMode mode_) + { + mode = mode_; + } + DitherMode GetMode() const + { + return mode; + } + +}; + + +using Dither = DitherChannels<4>; + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleBuffer.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleBuffer.h new file mode 100644 index 000000000..718d1636f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleBuffer.h @@ -0,0 +1,135 @@ +/* + * SampleBuffer.h + * -------------- + * Purpose: C++2b audio_buffer-like thingy. + * 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 "../common/mptBaseMacros.h" +#include "../common/mptBaseTypes.h" + + +OPENMPT_NAMESPACE_BEGIN + + +template +struct audio_buffer_planar +{ +public: + using sample_type = SampleType; +private: + SampleType * const * m_buffers; + std::size_t m_channels; + std::size_t m_frames; + std::size_t m_offset; +public: + constexpr audio_buffer_planar(SampleType * const * buffers, std::size_t channels, std::size_t frames) + : m_buffers(buffers) + , m_channels(channels) + , m_frames(frames) + , m_offset(0) + { + return; + } + SampleType & operator()(std::size_t channel, std::size_t frame) + { + return m_buffers[channel][m_offset + frame]; + } + const SampleType & operator()(std::size_t channel, std::size_t frame) const + { + return m_buffers[channel][m_offset + frame]; + } + std::size_t size_channels() const noexcept + { + return m_channels; + } + std::size_t size_frames() const noexcept + { + return m_frames; + } + audio_buffer_planar & advance(std::size_t numFrames) + { + m_offset += numFrames; + m_frames -= numFrames; + return *this; + } +}; + +template +struct audio_buffer_interleaved +{ +public: + using sample_type = SampleType; +private: + SampleType * m_buffer; + std::size_t m_channels; + std::size_t m_frames; +public: + constexpr audio_buffer_interleaved(SampleType* buffer, std::size_t channels, std::size_t frames) + : m_buffer(buffer) + , m_channels(channels) + , m_frames(frames) + { + return; + } + SampleType * data() + { + return m_buffer; + } + const SampleType * data() const + { + return m_buffer; + } + SampleType & operator()(std::size_t channel, std::size_t frame) + { + return m_buffer[m_channels * frame + channel]; + } + const SampleType & operator()(std::size_t channel, std::size_t frame) const + { + return m_buffer[m_channels * frame + channel]; + } + std::size_t size_channels() const noexcept + { + return m_channels; + } + std::size_t size_frames() const noexcept + { + return m_frames; + } + audio_buffer_interleaved & advance(std::size_t numFrames) + { + m_buffer += size_channels() * numFrames; + m_frames -= numFrames; + return *this; + } +}; + +template +std::size_t planar_audio_buffer_valid_channels(SampleType * const * buffers, std::size_t maxChannels) +{ + std::size_t channel; + for(channel = 0; channel < maxChannels; ++channel) + { + if(!buffers[channel]) + { + break; + } + } + return channel; +} + +template +BufferType advance_audio_buffer(BufferType buf, std::size_t numFrames) +{ + MPT_ASSERT(numFrames <= buf.size_frames()); + return buf.advance(numFrames); +} + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h index c2ca79fd3..32d0de222 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h @@ -18,80 +18,132 @@ OPENMPT_NAMESPACE_BEGIN struct int24; -enum SampleFormatEnum +enum SampleFormatEnum : uint8 { - SampleFormatUnsigned8 = 8, // do not change value (for compatibility with old configuration settings) + SampleFormatUnsigned8 = 9, // do not change value (for compatibility with old configuration settings) + SampleFormatInt8 = 8, // do not change value (for compatibility with old configuration settings) SampleFormatInt16 = 16, // do not change value (for compatibility with old configuration settings) SampleFormatInt24 = 24, // do not change value (for compatibility with old configuration settings) SampleFormatInt32 = 32, // do not change value (for compatibility with old configuration settings) SampleFormatFloat32 = 32 + 128, // do not change value (for compatibility with old configuration settings) + SampleFormatFloat64 = 64 + 128, // do not change value (for compatibility with old configuration settings) SampleFormatInvalid = 0 }; +template +Container AllSampleFormats() +{ + return { SampleFormatFloat64, SampleFormatFloat32, SampleFormatInt32, SampleFormatInt24, SampleFormatInt16, SampleFormatInt8, SampleFormatUnsigned8 }; +} + +template +Container DefaultSampleFormats() +{ + return { SampleFormatFloat32, SampleFormatInt32, SampleFormatInt24, SampleFormatInt16, SampleFormatInt8 }; +} + template struct SampleFormatTraits; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatUnsigned8; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt16; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt24; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt32; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat32; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatUnsigned8; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt8; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt16; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt24; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt32; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat32; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat64; } }; template struct SampleFormatToType; -template<> struct SampleFormatToType { typedef uint8 type; }; -template<> struct SampleFormatToType { typedef int16 type; }; -template<> struct SampleFormatToType { typedef int24 type; }; -template<> struct SampleFormatToType { typedef int32 type; }; -template<> struct SampleFormatToType { typedef float type; }; +template<> struct SampleFormatToType { typedef uint8 type; }; +template<> struct SampleFormatToType { typedef int8 type; }; +template<> struct SampleFormatToType { typedef int16 type; }; +template<> struct SampleFormatToType { typedef int24 type; }; +template<> struct SampleFormatToType { typedef int32 type; }; +template<> struct SampleFormatToType { typedef float type; }; +template<> struct SampleFormatToType { typedef double type; }; -struct SampleFormat +class SampleFormat { + +private: + SampleFormatEnum value; - MPT_CONSTEXPR11_FUN SampleFormat(SampleFormatEnum v = SampleFormatInvalid) : value(v) { } - MPT_CONSTEXPR11_FUN bool operator == (SampleFormat other) const { return value == other.value; } - MPT_CONSTEXPR11_FUN bool operator != (SampleFormat other) const { return value != other.value; } - MPT_CONSTEXPR11_FUN bool operator == (SampleFormatEnum other) const { return value == other; } - MPT_CONSTEXPR11_FUN bool operator != (SampleFormatEnum other) const { return value != other; } - MPT_CONSTEXPR11_FUN operator SampleFormatEnum () const + + template + static MPT_CONSTEXPR14_FUN SampleFormatEnum Sanitize(T x) noexcept { - return value; + using uT = typename std::make_unsigned::type; + uT ux = x; + if(ux == static_cast(-8)) + { + ux = 8+1; + } + // float|64|32|16|8|?|?|unsigned + ux &= 0b11111001; + return static_cast(ux); } - MPT_CONSTEXPR11_FUN bool IsValid() const + +public: + + MPT_CONSTEXPR11_FUN SampleFormat() noexcept + : value(SampleFormatInvalid) + { + } + + MPT_CONSTEXPR14_FUN SampleFormat(SampleFormatEnum v) noexcept + : value(Sanitize(v)) + { + } + + MPT_CONSTEXPR11_FUN bool IsValid() const noexcept { return value != SampleFormatInvalid; } - MPT_CONSTEXPR11_FUN bool IsUnsigned() const + + MPT_CONSTEXPR11_FUN bool IsUnsigned() const noexcept { return IsValid() && (value == SampleFormatUnsigned8); } - MPT_CONSTEXPR11_FUN bool IsFloat() const + MPT_CONSTEXPR11_FUN bool IsFloat() const noexcept { - return IsValid() && (value == SampleFormatFloat32); + return IsValid() && ((value == SampleFormatFloat32) || (value == SampleFormatFloat64)); } - MPT_CONSTEXPR11_FUN bool IsInt() const + MPT_CONSTEXPR11_FUN bool IsInt() const noexcept { - return IsValid() && (value != SampleFormatFloat32); + return IsValid() && ((value != SampleFormatFloat32) && (value != SampleFormatFloat64)); } - MPT_CONSTEXPR11_FUN uint8 GetBitsPerSample() const + MPT_CONSTEXPR11_FUN uint8 GetBitsPerSample() const noexcept { return !IsValid() ? 0 : (value == SampleFormatUnsigned8) ? 8 : + (value == SampleFormatInt8) ? 8 : (value == SampleFormatInt16) ? 16 : (value == SampleFormatInt24) ? 24 : (value == SampleFormatInt32) ? 32 : (value == SampleFormatFloat32) ? 32 : + (value == SampleFormatFloat64) ? 64 : 0; } + MPT_CONSTEXPR11_FUN operator SampleFormatEnum () const noexcept + { + return value; + } + // backward compatibility, conversion to/from integers - MPT_CONSTEXPR11_FUN operator int () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(int v) : value(SampleFormatEnum(v)) { } - MPT_CONSTEXPR11_FUN operator long () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(long v) : value(SampleFormatEnum(v)) { } - MPT_CONSTEXPR11_FUN operator unsigned int () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(unsigned int v) : value(SampleFormatEnum(v)) { } - MPT_CONSTEXPR11_FUN operator unsigned long () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(unsigned long v) : value(SampleFormatEnum(v)) { } + static MPT_CONSTEXPR14_FUN SampleFormat FromInt(int x) noexcept + { + return SampleFormat(Sanitize(x)); + } + static MPT_CONSTEXPR14_FUN int ToInt(SampleFormat x) noexcept + { + return x.value; + } + MPT_CONSTEXPR11_FUN int AsInt() const noexcept + { + return value; + } + }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h index 88c7a24d6..7b53de1e8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h @@ -36,16 +36,16 @@ namespace SC { // SC = _S_ample_C_onversion #if MPT_COMPILER_MSVC -#if defined(_M_IX86) && !(defined(_M_X64)) && (_M_IX86_FP < 2) -#define MPT_SC_AVOID_FLOOR 1 +#define MPT_SC_AVOID_ROUND 1 #else -#define MPT_SC_AVOID_FLOOR 0 -#endif -#else -#define MPT_SC_AVOID_FLOOR 0 +#define MPT_SC_AVOID_ROUND 0 #endif - +#if MPT_SC_AVOID_ROUND +#define MPT_SC_FASTROUND(x) std::floor((x) + 0.5f) +#else +#define MPT_SC_FASTROUND(x) mpt::round(x) +#endif #if MPT_COMPILER_SHIFT_SIGNED @@ -54,57 +54,26 @@ namespace SC { // SC = _S_ample_C_onversion #else -//#define MPT_SC_USE_MULDIV - -#ifdef MPT_SC_USE_MULDIV - -// just use mul and div - -template -MPT_FORCEINLINE auto rshift_signed_muldiv(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; - return x / (static_cast(1) << y); -} - -template -MPT_FORCEINLINE auto lshift_signed_muldiv(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; - return x * (static_cast(1) << y); -} - -#define MPT_SC_RSHIFT_SIGNED(val, shift) SC::rshift_signed_muldiv((val), (shift)) -#define MPT_SC_LSHIFT_SIGNED(val, shift) SC::lshift_signed_muldiv((val), (shift)) - -#else - #define MPT_SC_RSHIFT_SIGNED(val, shift) mpt::rshift_signed((val), (shift)) #define MPT_SC_LSHIFT_SIGNED(val, shift) mpt::lshift_signed((val), (shift)) #endif -#endif - // Every sample decoding functor has to typedef its input_t and output_t -// and has to provide a static const input_inc member +// and has to provide a static constexpr input_inc member // which describes by how many input_t elements inBuf has to be incremented between invocations. // input_inc is normally 1 except when decoding e.g. bigger sample values -// from multiple mpt::byte values. +// from multiple std::byte values. // decodes signed 7bit values stored as signed int8 struct DecodeInt7 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return Clamp(mpt::byte_cast(*inBuf), static_cast(-64), static_cast(63)) * 2; @@ -113,9 +82,9 @@ struct DecodeInt7 struct DecodeInt8 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return mpt::byte_cast(*inBuf); @@ -124,9 +93,9 @@ struct DecodeInt8 struct DecodeUint8 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return static_cast(int(mpt::byte_cast(*inBuf)) - 128); @@ -135,9 +104,9 @@ struct DecodeUint8 struct DecodeInt8Delta { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; uint8 delta; DecodeInt8Delta() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -150,9 +119,9 @@ struct DecodeInt8Delta template struct DecodeInt16 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int16 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 2; + static constexpr std::size_t input_inc = 2; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return (mpt::byte_cast(inBuf[loByteIndex]) | (mpt::byte_cast(inBuf[hiByteIndex]) << 8)) - offset; @@ -162,9 +131,9 @@ struct DecodeInt16 template struct DecodeInt16Delta { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int16 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 2; + static constexpr std::size_t input_inc = 2; uint16 delta; DecodeInt16Delta() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -176,9 +145,9 @@ struct DecodeInt16Delta struct DecodeInt16Delta8 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int16 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 2; + static constexpr std::size_t input_inc = 2; uint16 delta; DecodeInt16Delta8() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -194,9 +163,9 @@ struct DecodeInt16Delta8 template struct DecodeInt24 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 3; + static constexpr std::size_t input_inc = 3; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return ((mpt::byte_cast(inBuf[loByteIndex]) << 8) | (mpt::byte_cast(inBuf[midByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiByteIndex]) << 24)) - offset; @@ -206,9 +175,9 @@ struct DecodeInt24 template struct DecodeInt32 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; + static constexpr std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return (mpt::byte_cast(inBuf[loLoByteIndex]) | (mpt::byte_cast(inBuf[loHiByteIndex]) << 8) | (mpt::byte_cast(inBuf[hiLoByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiHiByteIndex]) << 24)) - offset; @@ -218,9 +187,9 @@ struct DecodeInt32 template struct DecodeInt64 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int64 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 8; + static constexpr std::size_t input_inc = 8; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return (uint64(0) @@ -239,9 +208,9 @@ struct DecodeInt64 template struct DecodeFloat32 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef float32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; + static constexpr std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return IEEE754binary32LE(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex]); @@ -251,9 +220,9 @@ struct DecodeFloat32 template struct DecodeScaledFloat32 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef float32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; + static constexpr std::size_t input_inc = 4; float factor; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { @@ -269,9 +238,9 @@ struct DecodeScaledFloat32 template struct DecodeFloat64 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef float64 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 8; + static constexpr std::size_t input_inc = 8; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return IEEE754binary64LE(inBuf[b0], inBuf[b1], inBuf[b2], inBuf[b3], inBuf[b4], inBuf[b5], inBuf[b6], inBuf[b7]); @@ -283,7 +252,7 @@ struct DecodeIdentity { typedef Tsample input_t; typedef Tsample output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return *inBuf; @@ -340,6 +309,98 @@ struct Convert } }; +template <> +struct Convert +{ + typedef int8 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(val+0x80); + } +}; + +template <> +struct Convert +{ + typedef int16 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(val, 8))+0x80); + } +}; + +template <> +struct Convert +{ + typedef int24 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val), 16))+0x80); + } +}; + +template <> +struct Convert +{ + typedef int32 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(val, 24))+0x80); + } +}; + +template <> +struct Convert +{ + typedef int64 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(val, 56))+0x80); + } +}; + +template <> +struct Convert +{ + typedef float32 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0f, 1.0f); + val *= 128.0f; + return static_cast(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val)))+0x80); + } +}; + +template <> +struct Convert +{ + typedef double input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0, 1.0); + val *= 128.0; + return static_cast(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val)))+0x80); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(val)-0x80); + } +}; + template <> struct Convert { @@ -393,12 +454,31 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= 128.0f; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0f + 1.0f), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5f))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef double input_t; + typedef int8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0, 1.0); + val *= 128.0; + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int16 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 8)); } }; @@ -455,30 +535,7 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= 32768.0f; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0f + 1.0f), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5f))); -#endif - } -}; - -template <> -struct Convert -{ - typedef double input_t; - typedef int8 output_t; - MPT_FORCEINLINE output_t operator() (input_t val) - { - Limit(val, -1.0, 1.0); - val *= 128.0; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0 + 1.0), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); } }; @@ -491,12 +548,18 @@ struct Convert { Limit(val, -1.0, 1.0); val *= 32768.0; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0 + 1.0), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int24 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 16)); } }; @@ -544,6 +607,43 @@ struct Convert } }; +template <> +struct Convert +{ + typedef float32 input_t; + typedef int24 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0f, 1.0f); + val *= 2147483648.0f; + return static_cast(MPT_SC_RSHIFT_SIGNED(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))), 8)); + } +}; + +template <> +struct Convert +{ + typedef double input_t; + typedef int24 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0, 1.0); + val *= 2147483648.0; + return static_cast(MPT_SC_RSHIFT_SIGNED(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))), 8)); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int32 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 24)); + } +}; + template <> struct Convert { @@ -597,12 +697,7 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= 2147483648.0f; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0f + 1.0f), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5f))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); } }; @@ -615,12 +710,18 @@ struct Convert { Limit(val, -1.0, 1.0); val *= 2147483648.0; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0 + 1.0), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int64 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 56); } }; @@ -677,7 +778,7 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= static_cast(uint64(1)<<63); - return mpt::saturate_cast(std::floor(val + 0.5f)); + return mpt::saturate_cast(MPT_SC_FASTROUND(val)); } }; @@ -690,7 +791,18 @@ struct Convert { Limit(val, -1.0, 1.0); val *= static_cast(uint64(1)<<63); - return mpt::saturate_cast(std::floor(val + 0.5)); + return mpt::saturate_cast(MPT_SC_FASTROUND(val)); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef float32 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return (static_cast(val)-0x80) * (1.0f / static_cast(static_cast(1)<<7)); } }; @@ -716,6 +828,17 @@ struct Convert } }; +template <> +struct Convert +{ + typedef int24 input_t; + typedef float32 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return val * (1.0f / static_cast(static_cast(1)<<23)); + } +}; + template <> struct Convert { @@ -738,6 +861,17 @@ struct Convert } }; +template <> +struct Convert +{ + typedef uint8 input_t; + typedef double output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return (static_cast(val)-0x80) * (1.0 / static_cast(static_cast(1)<<7)); + } +}; + template <> struct Convert { @@ -760,6 +894,17 @@ struct Convert } }; +template <> +struct Convert +{ + typedef int24 input_t; + typedef double output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return val * (1.0 / static_cast(static_cast(1)<<23)); + } +}; + template <> struct Convert { @@ -805,19 +950,19 @@ struct Convert }; -template +template struct ConvertFixedPoint; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef uint8 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int8_min) val = int8_min; if(val > int8_max) val = int8_max; @@ -825,16 +970,16 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int8 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int8_min) val = int8_min; if(val > int8_max) val = int8_max; @@ -842,16 +987,16 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int16 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int16_min) val = int16_min; if(val > int16_max) val = int16_max; @@ -859,16 +1004,16 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int24 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int24_min) val = int24_min; if(val > int24_max) val = int24_max; @@ -876,20 +1021,20 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int32 output_t; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); return static_cast(Clamp(val, static_cast(-((1<(1< -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef float32 output_t; @@ -901,17 +1046,26 @@ struct ConvertFixedPoint } MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - MPT_CONSTANT_IF(clipOutput) - { - float32 out = val * factor; - if(out < -1.0f) out = -1.0f; - if(out > 1.0f) out = 1.0f; - return out; - } else - { - return val * factor; - } + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return val * factor; + } +}; + +template +struct ConvertFixedPoint +{ + typedef int32 input_t; + typedef float64 output_t; + const double factor; + MPT_FORCEINLINE ConvertFixedPoint() + : factor( 1.0 / static_cast(1 << fractionalBits) ) + { + return; + } + MPT_FORCEINLINE output_t operator() (input_t val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return val * factor; } }; @@ -924,25 +1078,39 @@ struct ConvertToFixedPoint { typedef uint8 input_t; typedef int32 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); return MPT_SC_LSHIFT_SIGNED(static_cast(static_cast(val)-0x80), shiftBits); } }; +template +struct ConvertToFixedPoint +{ + typedef int8 input_t; + typedef int32 output_t; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + MPT_FORCEINLINE output_t operator() (input_t val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); + return MPT_SC_LSHIFT_SIGNED(static_cast(val), shiftBits); + } +}; + template struct ConvertToFixedPoint { typedef int16 input_t; typedef int32 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); return MPT_SC_LSHIFT_SIGNED(static_cast(val), shiftBits); } }; @@ -952,11 +1120,11 @@ struct ConvertToFixedPoint { typedef int24 input_t; typedef int32 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); return MPT_SC_LSHIFT_SIGNED(static_cast(val), shiftBits); } }; @@ -968,7 +1136,7 @@ struct ConvertToFixedPoint typedef int32 output_t; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); return MPT_SC_RSHIFT_SIGNED(static_cast(val), (sizeof(input_t)*8-1-fractionalBits)); } }; @@ -986,8 +1154,26 @@ struct ConvertToFixedPoint } MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - return mpt::saturate_cast(std::floor(val * factor + 0.5f)); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return mpt::saturate_cast(MPT_SC_FASTROUND(val * factor)); + } +}; + +template +struct ConvertToFixedPoint +{ + typedef float64 input_t; + typedef int32 output_t; + const double factor; + MPT_FORCEINLINE ConvertToFixedPoint() + : factor( static_cast(1 << fractionalBits) ) + { + return; + } + MPT_FORCEINLINE output_t operator() (input_t val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return mpt::saturate_cast(MPT_SC_FASTROUND(val * factor)); } }; @@ -1000,7 +1186,7 @@ struct ConversionChain { typedef typename Func1::input_t input_t; typedef typename Func2::output_t output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = Func1::input_inc; + static constexpr std::size_t input_inc = Func1::input_inc; Func1 func1; Func2 func2; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -1016,6 +1202,69 @@ struct ConversionChain }; +template +struct ClipFixed +{ + typedef Tfixed input_t; + typedef Tfixed output_t; + MPT_FORCEINLINE Tfixed operator() (Tfixed val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + if constexpr(clipOutput) + { + constexpr Tfixed clip_max = (Tfixed(1) << fractionalBits) - Tfixed(1); + constexpr Tfixed clip_min = Tfixed(0) - (Tfixed(1) << fractionalBits); + if(val < clip_min) val = clip_min; + if(val > clip_max) val = clip_max; + return val; + } else + { + return val; + } + } +}; + + +template +struct ClipFloat; + +template +struct ClipFloat +{ + typedef float input_t; + typedef float output_t; + MPT_FORCEINLINE float operator() (float val) + { + if constexpr(clipOutput) + { + if(val < -1.0f) val = -1.0f; + if(val > 1.0f) val = 1.0f; + return val; + } else + { + return val; + } + } +}; + +template +struct ClipFloat +{ + typedef double input_t; + typedef double output_t; + MPT_FORCEINLINE double operator() (double val) + { + if constexpr(clipOutput) + { + if(val < -1.0) val = -1.0; + if(val > 1.0) val = 1.0; + return val; + } else + { + return val; + } + } +}; template @@ -1149,7 +1398,7 @@ struct NormalizationChain typedef typename Func1::output_t normalize_t; typedef typename Normalize::peak_t peak_t; typedef typename Func2::output_t output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = Func1::input_inc; + static constexpr std::size_t input_inc = Func1::input_inc; Func1 func1; Normalize normalize; Func2 func2; @@ -1181,6 +1430,7 @@ struct NormalizationChain #undef MPT_SC_RSHIFT_SIGNED #undef MPT_SC_LSHIFT_SIGNED +#undef MPT_SC_FASTROUND diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h index 85906b237..c7547df2b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h @@ -15,6 +15,7 @@ #include "../common/Endianness.h" #include "SampleFormatConverters.h" +#include "SampleFormat.h" OPENMPT_NAMESPACE_BEGIN @@ -77,28 +78,109 @@ 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) +template +void ConvertBufferMixFixedToBuffer(TOutBuf outBuf, TInBuf inBuf, Tdither & dither, std::size_t channels, std::size_t count) { - SC::ConvertFixedPoint conv; - count *= channels; - for(std::size_t i = 0; i < count; ++i) - { - p[i] = conv(mixbuffer[i]); - } -} - -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; + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + constexpr int ditherBits = SampleFormat(SampleFormatTraits::sampleFormat()).IsInt() + ? SampleFormat(SampleFormatTraits::sampleFormat()).GetBitsPerSample() + : 0; + SC::ClipFixed clip; + SC::ConvertFixedPoint conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { - buffers[channel][i] = conv(*mixbuffer); - mixbuffer++; + outBuf(channel, i) = conv(clip(dither.template process(channel, inBuf(channel, i)))); + } + } +} + + +template +void ConvertBufferToBufferMixFixed(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + SC::ConvertToFixedPoint conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(inBuf(channel, i)); + } + } +} + + +template +void ConvertBufferMixFloatToBuffer(TOutBuf outBuf, TInBuf inBuf, Tdither & dither, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + constexpr int ditherBits = SampleFormat(SampleFormatTraits::sampleFormat()).IsInt() + ? SampleFormat(SampleFormatTraits::sampleFormat()).GetBitsPerSample() + : 0; + SC::ClipFloat clip; + SC::Convert conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(clip(dither.template process(channel, inBuf(channel, i)))); + } + } +} + + +template +void ConvertBufferToBufferMixFloat(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + SC::Convert conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(inBuf(channel, i)); + } + } +} + + +template +void ConvertBufferToBuffer(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + SC::Convert conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(inBuf(channel, i)); } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleTypes.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleTypes.h new file mode 100644 index 000000000..a88e98cfb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleTypes.h @@ -0,0 +1,59 @@ +/* + * SampleTypes.h + * ------------- + * Purpose: Basic audio sample types. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + +#include "../common/mptBaseMacros.h" +#include "../common/mptBaseTypes.h" + +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +using AudioSampleInt = int16; +using AudioSampleFloat = nativefloat; + +using AudioSample = mpt::select_type::is_hard, AudioSampleFloat, AudioSampleInt>::type; + + +template +struct FixedPointSampleTraits +{ + static_assert(std::is_integral::value); + static_assert(std::is_signed::value); + static_assert((sizeof(Tsample) * 8u) - 1 > MIX_HEADROOM_BITS); + static_assert((sizeof(Tsample) * 8u) - 1 > FILTER_HEADROOM_BITS); + using sample_type = Tsample; + enum class sample_type_strong : sample_type {}; + static constexpr int mix_headroom_bits() noexcept { return static_cast(MIX_HEADROOM_BITS); } + static constexpr int mix_precision_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - MIX_HEADROOM_BITS); } // including sign bit + static constexpr int mix_fractional_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - 1 - MIX_HEADROOM_BITS); } // excluding sign bit + static constexpr sample_type mix_clip_max() noexcept { return ((sample_type(1) << mix_fractional_bits()) - sample_type(1)); } + static constexpr sample_type mix_clip_min() noexcept { return -((sample_type(1) << mix_fractional_bits()) - sample_type(1)); } + static constexpr int filter_headroom_bits() noexcept { return static_cast(FILTER_HEADROOM_BITS); } + static constexpr int filter_precision_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - FILTER_HEADROOM_BITS); } // including sign bit + static constexpr int filter_fractional_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - 1 - FILTER_HEADROOM_BITS); } // excluding sign bit + template + static constexpr Tfloat mix_scale() noexcept { return static_cast(sample_type(1) << mix_fractional_bits()); } +}; + +using MixSampleIntTraits = FixedPointSampleTraits; + +using MixSampleInt = MixSampleIntTraits::sample_type; +using MixSampleFloat = AudioSampleFloat; + +using MixSample = mpt::select_type::is_hard, MixSampleFloat, MixSampleInt>::type; + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp index 228236d9e..0da829da2 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp @@ -10,7 +10,8 @@ #include "stdafx.h" -#include "../sounddsp/DSP.h" +#include "DSP.h" +#include "../soundbase/SampleTypes.h" #include OPENMPT_NAMESPACE_BEGIN @@ -171,8 +172,8 @@ void CMegaBass::Initialize(bool bReset, DWORD MixingFreq) int32 a1 = 0, b0 = 1024, b1 = 0; int nXBassCutOff = 50 + (m_Settings.m_nXBassRange+2) * 20; int nXBassGain = m_Settings.m_nXBassDepth; - nXBassGain = mpt::clamp(nXBassGain, 2, 8); - nXBassCutOff = mpt::clamp(nXBassCutOff, 60, 600); + nXBassGain = std::clamp(nXBassGain, 2, 8); + nXBassCutOff = std::clamp(nXBassCutOff, 60, 600); ShelfEQ(1024, a1, b0, b1, nXBassCutOff, MixingFreq, 1.0f + (1.0f/16.0f) * (0x300 >> nXBassGain), 1.0f, @@ -418,6 +419,62 @@ void CSurround::SetSurroundParameters(uint32 nDepth, uint32 nDelay) } +BitCrushSettings::BitCrushSettings() + : m_Bits(8) +{ + return; +} + + +BitCrush::BitCrush() +{ +} + + +void BitCrush::Initialize(bool bReset, DWORD MixingFreq) +{ + MPT_UNREFERENCED_PARAMETER(bReset); + MPT_UNREFERENCED_PARAMETER(MixingFreq); +} + + +void BitCrush::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels) +{ + if(m_Settings.m_Bits <= 0) + { + return; + } + if(m_Settings.m_Bits > MixSampleIntTraits::mix_precision_bits()) + { + return; + } + unsigned int mask = ~((1u << (MixSampleIntTraits::mix_precision_bits() - m_Settings.m_Bits)) - 1u); + if(nChannels == 4) + { + for(int frame = 0; frame < count; ++frame) + { + MixSoundBuffer[frame*2 + 0] &= mask; + MixSoundBuffer[frame*2 + 1] &= mask; + MixRearBuffer[frame*2 + 0] &= mask; + MixRearBuffer[frame*2 + 1] &= mask; + } + } else if(nChannels == 2) + { + for(int frame = 0; frame < count; ++frame) + { + MixSoundBuffer[frame*2 + 0] &= mask; + MixSoundBuffer[frame*2 + 1] &= mask; + } + } else if(nChannels == 1) + { + for(int frame = 0; frame < count; ++frame) + { + MixSoundBuffer[frame] &= mask; + } + } +} + + #else diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h index 982ba6a1f..825db80f6 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h @@ -43,6 +43,13 @@ public: }; +struct BitCrushSettings +{ + int m_Bits; + BitCrushSettings(); +}; + + class CSurround { public: @@ -116,6 +123,19 @@ public: }; +class BitCrush +{ +public: + BitCrushSettings m_Settings; +public: + BitCrush(); +public: + void SetSettings(const BitCrushSettings &settings) { m_Settings = settings; } + void Initialize(bool bReset, DWORD MixingFreq); + void Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels); +}; + + #endif // NO_DSP diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp index 1b39297fe..5b3f32d1e 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp @@ -27,7 +27,7 @@ OPENMPT_NAMESPACE_BEGIN -static const UINT gEqLinearToDB[33] = +static constexpr UINT gEqLinearToDB[33] = { 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, @@ -35,7 +35,7 @@ static const UINT gEqLinearToDB[33] = 160, 172, 184, 196, 208, 220, 232, 244, 256 }; -static const EQBANDSTRUCT gEQDefaults[MAX_EQ_BANDS*2] = +static constexpr EQBANDSTRUCT gEQDefaults[MAX_EQ_BANDS*2] = { // Default: Flat EQ {0,0,0,0,0, 0,0,0,0, 1, 120, false}, @@ -69,7 +69,7 @@ static const EQBANDSTRUCT gEQDefaults[MAX_EQ_BANDS*2] = #define PBS_Y1 DWORD PTR [eax + EQBANDSTRUCT.y1] #define PBS_Y2 DWORD PTR [eax + EQBANDSTRUCT.y2] -static void EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) +static void X86_EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) { _asm { mov eax, pbs // eax = pbs @@ -115,96 +115,11 @@ EQ_Loop: } -#ifdef ENABLE_X86_AMD - -static void AMD_StereoEQ(EQBANDSTRUCT *pbl, EQBANDSTRUCT *pbr, float32 *pbuffer, UINT nCount) -{ - float tmp[16]; - - _asm { - mov eax, pbl - mov edx, pbr - mov ebx, pbuffer - mov ecx, nCount - lea edi, [tmp+8] - and edi, 0xfffffff8 - movd mm7, [eax+EQBANDSTRUCT.a0] - movd mm0, [edx+EQBANDSTRUCT.a0] - movd mm6, [eax+EQBANDSTRUCT.a1] - movd mm1, [edx+EQBANDSTRUCT.a1] - punpckldq mm7, mm0 - punpckldq mm6, mm1 - movq [edi], mm7 // [edi] = a0 - movq [edi+8], mm6 // [edi+8] = a1 - movd mm5, [eax+EQBANDSTRUCT.a2] - movd mm0, [edx+EQBANDSTRUCT.a2] - movd mm4, [eax+EQBANDSTRUCT.b1] - movd mm1, [edx+EQBANDSTRUCT.b1] - movd mm3, [eax+EQBANDSTRUCT.b2] - movd mm2, [edx+EQBANDSTRUCT.b2] - punpckldq mm5, mm0 - punpckldq mm4, mm1 - punpckldq mm3, mm2 - movq [edi+16], mm5 // [edi+16] = a2 - movq [edi+24], mm4 // [edi+24] = b1 - movq [edi+32], mm3 // [edi+32] = b2 - movd mm4, [eax+EQBANDSTRUCT.x1] - movd mm0, [edx+EQBANDSTRUCT.x1] - movd mm5, [eax+EQBANDSTRUCT.x2] - movd mm1, [edx+EQBANDSTRUCT.x2] - punpckldq mm4, mm0 // mm4 = x1 - punpckldq mm5, mm1 // mm5 = x2 - movd mm6, [eax+EQBANDSTRUCT.y1] - movd mm2, [edx+EQBANDSTRUCT.y1] - movd mm7, [eax+EQBANDSTRUCT.y2] - movd mm3, [edx+EQBANDSTRUCT.y2] - punpckldq mm6, mm2 // mm6 = y1 - punpckldq mm7, mm3 // mm7 = y2 -mainloop: - movq mm0, [ebx] - movq mm3, [edi+8] - add ebx, 8 - movq mm1, [edi+16] - pfmul mm3, mm4 // x1 * a1 - movq mm2, [edi+32] - pfmul mm1, mm5 // x2 * a2 - movq mm5, mm4 // x2 = x1 - pfmul mm2, mm7 // y2 * b2 - movq mm7, mm6 // y2 = y1 - pfmul mm6, [edi+24] // y1 * b1 - movq mm4, mm0 // x1 = x - pfmul mm0, [edi] // x * a0 - pfadd mm6, mm1 // x2*a2 + y1*b1 - pfadd mm6, mm2 // x2*a2 + y1*b1 + y2*b2 - pfadd mm6, mm3 // x1*a1 + x2*a2 + y1*b1 + y2*b2 - pfadd mm6, mm0 // x*a0 + x1*a1 + x2*a2 + y1*b1 + y2*b2 - dec ecx - movq [ebx-8], mm6 - jnz mainloop - movd [eax+EQBANDSTRUCT.x1], mm4 - punpckhdq mm4, mm4 - movd [eax+EQBANDSTRUCT.x2], mm5 - punpckhdq mm5, mm5 - movd [eax+EQBANDSTRUCT.y1], mm6 - punpckhdq mm6, mm6 - movd [eax+EQBANDSTRUCT.y2], mm7 - punpckhdq mm7, mm7 - movd [edx+EQBANDSTRUCT.x1], mm4 - movd [edx+EQBANDSTRUCT.x2], mm5 - movd [edx+EQBANDSTRUCT.y1], mm6 - movd [edx+EQBANDSTRUCT.y2], mm7 - emms - } -} - -#endif // ENABLE_X86_AMD - - #if defined(ENABLE_X86) && defined(ENABLE_SSE) static void SSE_StereoEQ(EQBANDSTRUCT *pbl, EQBANDSTRUCT *pbr, float32 *pbuffer, UINT nCount) { - static const float gk1 = 1.0f; + static constexpr float gk1 = 1.0f; _asm { mov eax, pbl mov edx, pbr @@ -299,7 +214,7 @@ done:; #pragma warning(pop) #endif // MPT_COMPILER_MSVC -#else +#endif static void EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) { @@ -315,15 +230,21 @@ static void EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) } } -#endif - void CEQ::ProcessMono(int *pbuffer, float *MixFloatBuffer, UINT nCount) { MonoMixToFloat(pbuffer, MixFloatBuffer, nCount, 1.0f/MIXING_SCALEF); for (UINT b=0; b -#endif #ifdef ENABLE_SSE2 #include #endif @@ -31,35 +28,21 @@ OPENMPT_NAMESPACE_BEGIN #ifndef NO_REVERB -#ifdef ENABLE_MMX -// Load two 32-bit values -static MPT_FORCEINLINE __m64 Load64MMX(const int32 *x) { return _mm_set_pi32(x[1], x[0]); } -// Load four 16-bit values -static MPT_FORCEINLINE __m64 Load64MMX(const LR16 (&x)[2]) { return Load64MMX(&x->lr); } -// Store 64-bit value from register (MSVC does not have_mm_cvtsi64_si64x) - macro to avoid emms warnings -#define Store64MMX(dst, src) \ - MPT_DO \ - { \ - STATIC_ASSERT(sizeof((dst)[0]) == 4); \ - (dst)[0] = _mm_cvtsi64_si32(src); \ - (dst)[1] = _mm_cvtsi64_si32(_mm_unpackhi_pi32(src, src)); \ - } MPT_WHILE_0 -#endif #ifdef ENABLE_SSE2 // Load two 32-bit values static MPT_FORCEINLINE __m128i Load64SSE(const int32 *x) { return _mm_loadl_epi64(reinterpret_cast(x)); } // Load four 16-bit values -static MPT_FORCEINLINE __m128i Load64SSE(const LR16 (&x)[2]) { return _mm_loadl_epi64(reinterpret_cast(&x)); } +static MPT_FORCEINLINE __m128i Load64SSE(const LR16 (&x)[2]) { return _mm_loadl_epi64(&reinterpret_cast(x)); } // Store two 32-bit or four 16-bit values from register static MPT_FORCEINLINE void Store64SSE(int32 *dst, __m128i src) { return _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), src); } -static MPT_FORCEINLINE void Store64SSE(LR16 *dst, __m128i src) { return _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), src); } +static MPT_FORCEINLINE void Store64SSE(LR16 (&dst)[2], __m128i src) { return _mm_storel_epi64(&reinterpret_cast<__m128i &>(dst), src); } #endif CReverb::CReverb() { // Shared reverb state - InitMixBuffer(MixReverbBuffer, static_cast(mpt::size(MixReverbBuffer))); + InitMixBuffer(MixReverbBuffer, static_cast(std::size(MixReverbBuffer))); // Reverb mix buffers MemsetZero(g_RefDelay); @@ -92,64 +75,56 @@ static int32 mBToLinear(int32 scale, int32 value_mB) return mpt::saturate_round(mBToLinear(value_mB) * scale); } - -struct SNDMIX_REVERB_PROPERTIES +static constexpr std::pair ReverbPresets[NUM_REVERBTYPES] = { - int32 lRoom; // [-10000, 0] default: -10000 mB - int32 lRoomHF; // [-10000, 0] default: 0 mB - float flDecayTime; // [0.1, 20.0] default: 1.0 s - float flDecayHFRatio; // [0.1, 2.0] default: 0.5 - int32 lReflections; // [-10000, 1000] default: -10000 mB - float flReflectionsDelay; // [0.0, 0.3] default: 0.02 s - int32 lReverb; // [-10000, 2000] default: -10000 mB - float flReverbDelay; // [0.0, 0.1] default: 0.04 s - float flDiffusion; // [0.0, 100.0] default: 100.0 % - float flDensity; // [0.0, 100.0] default: 100.0 % + // Examples simulating General MIDI 2'musical' reverb presets + // Name (Decay time) Description + // Plate (1.3s) A plate reverb simulation. + {{ -1000, -200, 1.30f,0.90f, 0,0.002f, 0,0.010f,100.0f, 75.0f }, "GM Plate"}, + // Small Room (1.1s) A small size room with a length of 5m or so. + {{ -1000, -600, 1.10f,0.83f, -400,0.005f, 500,0.010f,100.0f,100.0f }, "GM Small Room"}, + // Medium Room (1.3s) A medium size room with a length of 10m or so. + {{ -1000, -600, 1.30f,0.83f, -1000,0.010f, -200,0.020f,100.0f,100.0f }, "GM Medium Room"}, + // Large Room (1.5s) A large size room suitable for live performances. + {{ -1000, -600, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f }, "GM Large Room"}, + // Medium Hall (1.8s) A medium size concert hall. + {{ -1000, -600, 1.80f,0.70f, -1300,0.015f, -800,0.030f,100.0f,100.0f }, "GM Medium Hall"}, + // Large Hall (1.8s) A large size concert hall suitable for a full orchestra. + {{ -1000, -600, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f }, "GM Large Hall"}, + + {{ -1000, -100, 1.49f,0.83f, -2602,0.007f, 200,0.011f,100.0f,100.0f }, "Generic"}, + {{ -1000,-6000, 0.17f,0.10f, -1204,0.001f, 207,0.002f,100.0f,100.0f }, "Padded Cell"}, + {{ -1000, -454, 0.40f,0.83f, -1646,0.002f, 53,0.003f,100.0f,100.0f }, "Room"}, + {{ -1000,-1200, 1.49f,0.54f, -370,0.007f, 1030,0.011f,100.0f, 60.0f }, "Bathroom"}, + {{ -1000,-6000, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f }, "Living Room"}, + {{ -1000, -300, 2.31f,0.64f, -711,0.012f, 83,0.017f,100.0f,100.0f }, "Stone Room"}, + {{ -1000, -476, 4.32f,0.59f, -789,0.020f, -289,0.030f,100.0f,100.0f }, "Auditorium"}, + {{ -1000, -500, 3.92f,0.70f, -1230,0.020f, -2,0.029f,100.0f,100.0f }, "Concert Hall"}, + {{ -1000, 0, 2.91f,1.30f, -602,0.015f, -302,0.022f,100.0f,100.0f }, "Cave"}, + {{ -1000, -698, 7.24f,0.33f, -1166,0.020f, 16,0.030f,100.0f,100.0f }, "Arena"}, + {{ -1000,-1000,10.05f,0.23f, -602,0.020f, 198,0.030f,100.0f,100.0f }, "Hangar"}, + {{ -1000,-4000, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f }, "Carpeted Hallway"}, + {{ -1000, -300, 1.49f,0.59f, -1219,0.007f, 441,0.011f,100.0f,100.0f }, "Hallway"}, + {{ -1000, -237, 2.70f,0.79f, -1214,0.013f, 395,0.020f,100.0f,100.0f }, "Stone Corridor"}, + {{ -1000, -270, 1.49f,0.86f, -1204,0.007f, -4,0.011f,100.0f,100.0f }, "Alley"}, + {{ -1000,-3300, 1.49f,0.54f, -2560,0.162f, -613,0.088f, 79.0f,100.0f }, "Forest"}, + {{ -1000, -800, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f }, "City"}, + {{ -1000,-2500, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f }, "Mountains"}, + {{ -1000,-1000, 1.49f,0.83f,-10000,0.061f, 500,0.025f,100.0f,100.0f }, "Quarry"}, + {{ -1000,-2000, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f }, "Plain"}, + {{ -1000, 0, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f }, "Parking Lot"}, + {{ -1000,-1000, 2.81f,0.14f, 429,0.014f, 648,0.021f, 80.0f, 60.0f }, "Sewer Pipe"}, + {{ -1000,-4000, 1.49f,0.10f, -449,0.007f, 1700,0.011f,100.0f,100.0f }, "Underwater"}, }; -struct SNDMIX_RVBPRESET +mpt::ustring GetReverbPresetName(uint32 preset) { - SNDMIX_REVERB_PROPERTIES Preset; - const MPT_UCHAR_TYPE *name; -}; + return (preset < NUM_REVERBTYPES) ? mpt::ToUnicode(mpt::Charset::ASCII, ReverbPresets[preset].second) : mpt::ustring{}; +} - -static const SNDMIX_RVBPRESET gRvbPresets[NUM_REVERBTYPES] = +const SNDMIX_REVERB_PROPERTIES *GetReverbPreset(uint32 preset) { - {{ 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")}, -}; - -mpt::ustring GetReverbPresetName(uint32 nPreset) -{ - return (nPreset < NUM_REVERBTYPES) ? mpt::ustring(gRvbPresets[nPreset].name) : mpt::ustring(); + return (preset < NUM_REVERBTYPES) ? &ReverbPresets[preset].first : nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -301,7 +276,7 @@ void CReverb::Shutdown() void CReverb::Initialize(bool bReset, uint32 MixingFreq) { if (m_Settings.m_nReverbType >= NUM_REVERBTYPES) m_Settings.m_nReverbType = 0; - const SNDMIX_REVERB_PROPERTIES *rvbPreset = &gRvbPresets[m_Settings.m_nReverbType].Preset; + const SNDMIX_REVERB_PROPERTIES *rvbPreset = &ReverbPresets[m_Settings.m_nReverbType].first; if ((rvbPreset != m_currentPreset) || (bReset)) { @@ -418,7 +393,7 @@ mixsample_t *CReverb::GetReverbSendBuffer(uint32 nSamples) // Reverb -void CReverb::Process(mixsample_t *MixSoundBuffer, uint32 nSamples) +void CReverb::Process(MixSampleInt *MixSoundBuffer, uint32 nSamples) { if((!gnReverbSend) && (!gnReverbSamples)) { // no data is sent to reverb and reverb decayed completely @@ -618,28 +593,27 @@ void CReverb::ReverbProcessPostFiltering2x(const int32 * MPT_RESTRICT pRvb, int3 // Stereo Add + DC removal void CReverb::ReverbProcessPostFiltering1x(const int32 * MPT_RESTRICT pRvb, int32 * MPT_RESTRICT pDry, uint32 nSamples) { -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) +#ifdef ENABLE_SSE2 + if(GetProcSupport() & PROCSUPPORT_SSE2) { - __m64 nDCRRvb_Y1 = Load64MMX(gnDCRRvb_Y1); - __m64 nDCRRvb_X1 = Load64MMX(gnDCRRvb_X1); - __m64 in = _mm_set1_pi32(0); + __m128i nDCRRvb_Y1 = Load64SSE(gnDCRRvb_Y1); + __m128i nDCRRvb_X1 = Load64SSE(gnDCRRvb_X1); + __m128i in = _mm_set1_epi32(0); while(nSamples--) { - in = Load64MMX(pRvb); + in = Load64SSE(pRvb); pRvb += 2; // x(n-1) - x(n) - __m64 diff = _mm_sub_pi32(nDCRRvb_X1, in); - nDCRRvb_X1 = _mm_add_pi32(nDCRRvb_Y1, _mm_sub_pi32(_mm_srai_pi32(diff, DCR_AMOUNT + 1), diff)); - __m64 out = _mm_add_pi32(Load64MMX(pDry), nDCRRvb_X1); - nDCRRvb_Y1 = _mm_sub_pi32(nDCRRvb_X1, _mm_srai_pi32(nDCRRvb_X1, DCR_AMOUNT)); + __m128i diff = _mm_sub_epi32(nDCRRvb_X1, in); + nDCRRvb_X1 = _mm_add_epi32(nDCRRvb_Y1, _mm_sub_epi32(_mm_srai_epi32(diff, DCR_AMOUNT + 1), diff)); + __m128i out = _mm_add_epi32(Load64SSE(pDry), nDCRRvb_X1); + nDCRRvb_Y1 = _mm_sub_epi32(nDCRRvb_X1, _mm_srai_epi32(nDCRRvb_X1, DCR_AMOUNT)); nDCRRvb_X1 = in; - Store64MMX(pDry, out); + Store64SSE(pDry, out); pDry += 2; } - Store64MMX(gnDCRRvb_X1, in); - Store64MMX(gnDCRRvb_Y1, nDCRRvb_Y1); - _mm_empty(); + Store64SSE(gnDCRRvb_X1, in); + Store64SSE(gnDCRRvb_Y1, nDCRRvb_Y1); return; } #endif @@ -681,24 +655,23 @@ void CReverb::ReverbProcessPostFiltering1x(const int32 * MPT_RESTRICT pRvb, int3 void CReverb::ReverbDCRemoval(int32 * MPT_RESTRICT pBuffer, uint32 nSamples) { -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) +#ifdef ENABLE_SSE2 + if(GetProcSupport() & PROCSUPPORT_SSE2) { - __m64 nDCRRvb_Y1 = Load64MMX(gnDCRRvb_Y1); - __m64 nDCRRvb_X1 = Load64MMX(gnDCRRvb_X1); + __m128i nDCRRvb_Y1 = Load64SSE(gnDCRRvb_Y1); + __m128i nDCRRvb_X1 = Load64SSE(gnDCRRvb_X1); while(nSamples--) { - __m64 in = Load64MMX(pBuffer); - __m64 diff = _mm_sub_pi32(nDCRRvb_X1, in); - __m64 out = _mm_add_pi32(nDCRRvb_Y1, _mm_sub_pi32(_mm_srai_pi32(diff, DCR_AMOUNT + 1), diff)); - Store64MMX(pBuffer, out); + __m128i in = Load64SSE(pBuffer); + __m128i diff = _mm_sub_epi32(nDCRRvb_X1, in); + __m128i out = _mm_add_epi32(nDCRRvb_Y1, _mm_sub_epi32(_mm_srai_epi32(diff, DCR_AMOUNT + 1), diff)); + Store64SSE(pBuffer, out); pBuffer += 2; - nDCRRvb_Y1 = _mm_sub_pi32(out, _mm_srai_pi32(out, DCR_AMOUNT)); + nDCRRvb_Y1 = _mm_sub_epi32(out, _mm_srai_epi32(out, DCR_AMOUNT)); nDCRRvb_X1 = in; } - Store64MMX(gnDCRRvb_X1, nDCRRvb_X1); - Store64MMX(gnDCRRvb_Y1, nDCRRvb_Y1); - _mm_empty(); + Store64SSE(gnDCRRvb_X1, nDCRRvb_X1); + Store64SSE(gnDCRRvb_Y1, nDCRRvb_Y1); return; } #endif @@ -773,34 +746,6 @@ void CReverb::ProcessPreDelay(SWRvbRefDelay * MPT_RESTRICT pPreDelay, const int3 pPreDelay->History.lr = _mm_cvtsi128_si32(history); return; } -#endif -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) - { - __m64 coeffs = _mm_cvtsi32_si64(pPreDelay->nCoeffs.lr); - __m64 history = _mm_cvtsi32_si64(pPreDelay->History.lr); - __m64 preDifCoeffs = _mm_cvtsi32_si64(pPreDelay->nPreDifCoeffs.lr); - while(nSamples--) - { - __m64 in32 = Load64MMX(pIn); // 16-bit unsaturated reverb input [ r | l ] - __m64 inSat = _mm_packs_pi32(in32, in32); // [ r | l | r | l ] (16-bit saturated) - pIn += 2; - // Low-pass - __m64 lp = _mm_mulhi_pi16(_mm_subs_pi16(history, inSat), coeffs); - __m64 preDif = _mm_cvtsi32_si64(pPreDelay->PreDifBuffer[preDifPos].lr); - history = _mm_adds_pi16(_mm_adds_pi16(lp, lp), inSat); - // Pre-Diffusion - preDifPos = (preDifPos + 1) & SNDMIX_PREDIFFUSION_DELAY_MASK; - delayPos = (delayPos + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - __m64 preDif2 = _mm_subs_pi16(history, _mm_mulhi_pi16(preDif, preDifCoeffs)); - pPreDelay->PreDifBuffer[preDifPos].lr = _mm_cvtsi64_si32(preDif2); - pPreDelay->RefDelayBuffer[delayPos].lr = _mm_cvtsi64_si32(_mm_adds_pi16(_mm_mulhi_pi16(preDifCoeffs, preDif2), preDif)); - } - pPreDelay->nPreDifPos = preDifPos; - pPreDelay->History.lr = _mm_cvtsi64_si32(history); - _mm_empty(); - return; - } #endif const int32 coeffsL = pPreDelay->nCoeffs.c.l, coeffsR = pPreDelay->nCoeffs.c.r; const int32 preDifCoeffsL = pPreDelay->nPreDifCoeffs.c.l, preDifCoeffsR = pPreDelay->nPreDifCoeffs.c.r; @@ -895,75 +840,6 @@ void CReverb::ProcessReflections(SWRvbRefDelay * MPT_RESTRICT pPreDelay, LR16 * } return; } -#endif -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) - { - // First stage - uint32 numSamples = nSamples; - const LR16 *refDelayBuffer = pPreDelay->RefDelayBuffer; - int pos1 = pPreDelay->nDelayPos - pPreDelay->Reflections[0].Delay - 1; - int pos2 = pPreDelay->nDelayPos - pPreDelay->Reflections[1].Delay - 1; - int pos3 = pPreDelay->nDelayPos - pPreDelay->Reflections[2].Delay - 1; - int pos4 = pPreDelay->nDelayPos - pPreDelay->Reflections[3].Delay - 1; - __m64 gain1 = Load64MMX(pPreDelay->Reflections[0].Gains); - __m64 gain2 = Load64MMX(pPreDelay->Reflections[1].Gains); - __m64 gain3 = Load64MMX(pPreDelay->Reflections[2].Gains); - __m64 gain4 = Load64MMX(pPreDelay->Reflections[3].Gains); - while(numSamples--) - { - pos1 = (pos1 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos2 = (pos2 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos3 = (pos3 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos4 = (pos4 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - __m64 ref1 = _mm_cvtsi32_si64(refDelayBuffer[pos1].lr); // [0 | 0 | r | l ] - __m64 ref2 = _mm_cvtsi32_si64(refDelayBuffer[pos2].lr); - __m64 ref3 = _mm_cvtsi32_si64(refDelayBuffer[pos3].lr); - __m64 ref4 = _mm_cvtsi32_si64(refDelayBuffer[pos4].lr); - __m64 refOut = _mm_srai_pi32(_mm_add_pi32( - _mm_add_pi32(_mm_madd_pi16(_mm_unpacklo_pi32(ref1, ref1), gain1), _mm_madd_pi16(_mm_unpacklo_pi32(ref2, ref2), gain2)), - _mm_add_pi32(_mm_madd_pi16(_mm_unpacklo_pi32(ref3, ref3), gain3), _mm_madd_pi16(_mm_unpacklo_pi32(ref4, ref4), gain4))), - 15); - pRefOut->lr = _mm_cvtsi64_si32(_mm_packs_pi32(refOut, refOut)); - pRefOut++; - } - - // Second stage - numSamples = nSamples; - pRefOut -= nSamples; - - __m64 refGain = _mm_unpacklo_pi16(_mm_cvtsi32_si64(pPreDelay->ReflectionsGain.lr), _mm_cvtsi32_si64(0)); // [0 | g_r | 0 | g_l] - refGain = _mm_srai_pi32(refGain, 3); // For 28-bit final output: 16+15-3 = 28 - int pos5 = pPreDelay->nDelayPos - pPreDelay->Reflections[4].Delay - 1; - int pos6 = pPreDelay->nDelayPos - pPreDelay->Reflections[5].Delay - 1; - int pos7 = pPreDelay->nDelayPos - pPreDelay->Reflections[6].Delay - 1; - __m64 gain5 = Load64MMX(pPreDelay->Reflections[4].Gains); - __m64 gain6 = Load64MMX(pPreDelay->Reflections[5].Gains); - __m64 gain7 = Load64MMX(pPreDelay->Reflections[6].Gains); - while(numSamples--) - { - pos5 = (pos5 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos6 = (pos6 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos7 = (pos7 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - __m64 ref5 = _mm_cvtsi32_si64(refDelayBuffer[pos5].lr); // [0 | 0 | r | l ] - __m64 ref6 = _mm_cvtsi32_si64(refDelayBuffer[pos6].lr); - __m64 ref7 = _mm_cvtsi32_si64(refDelayBuffer[pos7].lr); - __m64 refPrev = _mm_cvtsi32_si64(pRefOut->lr); // output of previous reflections - __m64 refOut = _mm_srai_pi32(_mm_add_pi32( - _mm_add_pi32(_mm_madd_pi16(_mm_unpacklo_pi32(ref5, ref5), gain5), _mm_madd_pi16(_mm_unpacklo_pi32(ref7, ref7), gain7)), - _mm_madd_pi16(_mm_unpacklo_pi32(ref6, ref6), gain6)), - 15); - refOut = _mm_adds_pi16(_mm_packs_pi32(refOut, refOut), refPrev); - pRefOut->lr = _mm_cvtsi64_si32(refOut); // late reverb stereo input - pRefOut++; - __m64 out = _mm_madd_pi16(_mm_unpacklo_pi16(refOut, refOut), refGain); // Apply reflections gain - // At this, point, this is the only output of the reverb - Store64MMX(pOut, out); - pOut += 2; - } - _mm_empty(); - return; - } #endif int pos[7]; for(int i = 0; i < 7; i++) @@ -1077,73 +953,6 @@ void CReverb::ProcessLateReverb(SWLateReverb * MPT_RESTRICT pReverb, LR16 * MPT_ pReverb->nDelayPos = delayPos; return; } -#endif -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) - { - int delayPos = pReverb->nDelayPos & RVBDLY_MASK; - __m64 rvbOutGains = Load64MMX(pReverb->RvbOutGains); - __m64 difCoeffs = Load64MMX(pReverb->nDifCoeffs); - __m64 decayLP = Load64MMX(pReverb->nDecayLP); - __m64 lpHistory = Load64MMX(pReverb->LPHistory); - while(nSamples--) - { - __m64 refIn = _mm_cvtsi32_si64(pRefOut->lr); // 16-bit stereo input - pRefOut++; - - __m64 delay2 = _mm_unpacklo_pi32( - _mm_cvtsi32_si64(pReverb->Delay2[DELAY_OFFSET(RVBDLY2L_LEN)].lr), - _mm_cvtsi32_si64(pReverb->Delay2[DELAY_OFFSET(RVBDLY2R_LEN)].lr)); - - // Unsigned to avoid sign extension - uint16 diff1L = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1L_LEN)].c.l; - uint16 diff1R = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1R_LEN)].c.r; - int32 diffusion1 = diff1L | (diff1R << 16); // diffusion1 history - - uint16 diff2L = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2L_LEN)].c.l; - uint16 diff2R = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2R_LEN)].c.r; - int32 diffusion2 = diff2L | (diff2R << 16); // diffusion2 history - - __m64 lpDecay = _mm_mulhi_pi16(_mm_subs_pi16(lpHistory, delay2), decayLP); - lpHistory = _mm_adds_pi16(_mm_adds_pi16(lpDecay, lpDecay), delay2); // Low-passed decay - - // Apply decay gain - __m64 histDecay = _mm_srai_pi32(_mm_madd_pi16(Load64MMX(pReverb->nDecayDC), lpHistory), 15); - __m64 histDecayIn = _mm_adds_pi16(_mm_packs_pi32(histDecay, histDecay), _mm_srai_pi16(_mm_unpacklo_pi32(refIn, refIn), 2)); - __m64 histDecayInDiff = _mm_subs_pi16(histDecayIn, _mm_mulhi_pi16(_mm_cvtsi32_si64(diffusion1), difCoeffs)); - pReverb->Diffusion1[delayPos].lr = _mm_cvtsi64_si32(histDecayInDiff); - - __m64 delay1Out = _mm_adds_pi16(_mm_mulhi_pi16(difCoeffs, histDecayInDiff), _mm_cvtsi32_si64(diffusion1)); - // Insert the diffusion output in the reverb delay line - pReverb->Delay1[delayPos].lr = _mm_cvtsi64_si32(delay1Out); - __m64 histDecayInDelay = _mm_adds_pi16(histDecayIn, _mm_unpacklo_pi32(delay1Out, delay1Out)); - - // Input to second diffuser - __m64 delay1 = _mm_unpacklo_pi32( - _mm_cvtsi32_si64(pReverb->Delay1[DELAY_OFFSET(RVBDLY1L_LEN)].lr), - _mm_cvtsi32_si64(pReverb->Delay1[DELAY_OFFSET(RVBDLY1R_LEN)].lr)); - - __m64 delay1Gains = _mm_srai_pi32(_mm_madd_pi16(delay1, Load64MMX(pReverb->Dif2InGains)), 15); - __m64 delay1GainsSat = _mm_packs_pi32(delay1Gains, delay1Gains); - __m64 histDelay1 = _mm_subs_pi16(_mm_adds_pi16(histDecayInDelay, delay1), delay1GainsSat); // accumulate with reverb output - __m64 diff2out = _mm_subs_pi16(delay1GainsSat, _mm_mulhi_pi16(_mm_cvtsi32_si64(diffusion2), difCoeffs)); - __m64 diff2outCoeffs = _mm_mulhi_pi16(difCoeffs, diff2out); - pReverb->Diffusion2[delayPos].lr = _mm_cvtsi64_si32(diff2out); - - __m64 mixOut = Load64MMX(pMixOut); - __m64 delay2out = _mm_adds_pi16(diff2outCoeffs, _mm_cvtsi32_si64(diffusion2)); - pReverb->Delay2[delayPos].lr = _mm_cvtsi64_si32(delay2out); - delayPos = (delayPos + 1) & RVBDLY_MASK; - // Accumulate with reverb output - __m64 out = _mm_add_pi32(_mm_madd_pi16(_mm_adds_pi16(histDelay1, delay2out), rvbOutGains), mixOut); - Store64MMX(pMixOut, out); - pMixOut += 2; - } - Store64MMX(&pReverb->LPHistory[0].lr, lpHistory); - pReverb->nDelayPos = delayPos; - _mm_empty(); - return; - } #endif int delayPos = pReverb->nDelayPos & RVBDLY_MASK; while(nSamples--) diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h index 8a777ea55..2118c9a8d 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h @@ -21,10 +21,6 @@ OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////////////////// // Reverberation -#define NUM_REVERBTYPES 29 - -mpt::ustring GetReverbPresetName(uint32 nPreset); - ///////////////////////////////////////////////////////////////////////////// // // SW Reverb structures @@ -142,9 +138,9 @@ public: // Shared reverb state private: - mixsample_t MixReverbBuffer[MIXBUFFERSIZE * 2]; + MixSampleInt MixReverbBuffer[MIXBUFFERSIZE * 2]; public: - mixsample_t gnRvbROfsVol = 0, gnRvbLOfsVol = 0; + MixSampleInt gnRvbROfsVol = 0, gnRvbLOfsVol = 0; private: const SNDMIX_REVERB_PROPERTIES *m_currentPreset = nullptr; @@ -176,10 +172,10 @@ public: void Initialize(bool bReset, uint32 MixingFreq); // can be called multiple times or never (if no data is sent to reverb) - mixsample_t *GetReverbSendBuffer(uint32 nSamples); + MixSampleInt *GetReverbSendBuffer(uint32 nSamples); // call once after all data has been sent. - void Process(mixsample_t *MixSoundBuffer, uint32 nSamples); + void Process(MixSampleInt *MixSoundBuffer, uint32 nSamples); private: void Shutdown(); @@ -204,79 +200,26 @@ private: // I3DL2 reverb presets // -#define SNDMIX_REVERB_PRESET_DEFAULT \ --10000, 0, 1.00f,0.50f,-10000,0.020f,-10000,0.040f,100.0f,100.0f +struct SNDMIX_REVERB_PROPERTIES +{ + int32 lRoom; // [-10000, 0] default: -10000 mB + int32 lRoomHF; // [-10000, 0] default: 0 mB + float flDecayTime; // [0.1, 20.0] default: 1.0 s + float flDecayHFRatio; // [0.1, 2.0] default: 0.5 + int32 lReflections; // [-10000, 1000] default: -10000 mB + float flReflectionsDelay; // [0.0, 0.3] default: 0.02 s + int32 lReverb; // [-10000, 2000] default: -10000 mB + float flReverbDelay; // [0.0, 0.1] default: 0.04 s + float flDiffusion; // [0.0, 100.0] default: 100.0 % + float flDensity; // [0.0, 100.0] default: 100.0 % +}; -#define SNDMIX_REVERB_PRESET_GENERIC \ - -1000, -100, 1.49f,0.83f, -2602,0.007f, 200,0.011f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_PADDEDCELL \ - -1000,-6000, 0.17f,0.10f, -1204,0.001f, 207,0.002f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_ROOM \ - -1000, -454, 0.40f,0.83f, -1646,0.002f, 53,0.003f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_BATHROOM \ - -1000,-1200, 1.49f,0.54f, -370,0.007f, 1030,0.011f,100.0f, 60.0f -#define SNDMIX_REVERB_PRESET_LIVINGROOM \ - -1000,-6000, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_STONEROOM \ - -1000, -300, 2.31f,0.64f, -711,0.012f, 83,0.017f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_AUDITORIUM \ - -1000, -476, 4.32f,0.59f, -789,0.020f, -289,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_CONCERTHALL \ - -1000, -500, 3.92f,0.70f, -1230,0.020f, -2,0.029f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_CAVE \ - -1000, 0, 2.91f,1.30f, -602,0.015f, -302,0.022f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_ARENA \ - -1000, -698, 7.24f,0.33f, -1166,0.020f, 16,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_HANGAR \ - -1000,-1000,10.05f,0.23f, -602,0.020f, 198,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_CARPETEDHALLWAY \ - -1000,-4000, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_HALLWAY \ - -1000, -300, 1.49f,0.59f, -1219,0.007f, 441,0.011f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_STONECORRIDOR \ - -1000, -237, 2.70f,0.79f, -1214,0.013f, 395,0.020f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_ALLEY \ - -1000, -270, 1.49f,0.86f, -1204,0.007f, -4,0.011f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_FOREST \ - -1000,-3300, 1.49f,0.54f, -2560,0.162f, -613,0.088f, 79.0f,100.0f -#define SNDMIX_REVERB_PRESET_CITY \ - -1000, -800, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f -#define SNDMIX_REVERB_PRESET_MOUNTAINS \ - -1000,-2500, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f -#define SNDMIX_REVERB_PRESET_QUARRY \ - -1000,-1000, 1.49f,0.83f,-10000,0.061f, 500,0.025f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_PLAIN \ - -1000,-2000, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f -#define SNDMIX_REVERB_PRESET_PARKINGLOT \ - -1000, 0, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_SEWERPIPE \ - -1000,-1000, 2.81f,0.14f, 429,0.014f, 648,0.021f, 80.0f, 60.0f -#define SNDMIX_REVERB_PRESET_UNDERWATER \ - -1000,-4000, 1.49f,0.10f, -449,0.007f, 1700,0.011f,100.0f,100.0f - -// Examples simulating General MIDI 2'musical' reverb presets -// -// Name (Decay time) Description -// -// Small Room (1.1s) A small size room with a length of 5m or so. -// Medium Room (1.3s) A medium size room with a length of 10m or so. -// Large Room (1.5s) A large size room suitable for live performances. -// Medium Hall (1.8s) A medium size concert hall. -// Large Hall (1.8s) A large size concert hall suitable for a full orchestra. -// Plate (1.3s) A plate reverb simulation. - -#define SNDMIX_REVERB_PRESET_SMALLROOM \ - -1000, -600, 1.10f,0.83f, -400,0.005f, 500,0.010f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_MEDIUMROOM \ - -1000, -600, 1.30f,0.83f, -1000,0.010f, -200,0.020f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_LARGEROOM \ - -1000, -600, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_MEDIUMHALL \ - -1000, -600, 1.80f,0.70f, -1300,0.015f, -800,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_LARGEHALL \ - -1000, -600, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_PLATE \ - -1000, -200, 1.30f,0.90f, 0,0.002f, 0,0.010f,100.0f, 75.0f +enum : uint32 +{ + NUM_REVERBTYPES = 29 +}; +mpt::ustring GetReverbPresetName(uint32 preset); +const SNDMIX_REVERB_PROPERTIES *GetReverbPreset(uint32 preset); OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp index c1c10df2d..29ff9eac7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp @@ -26,7 +26,7 @@ CriticalSection::CriticalSection() Enter(); } -CriticalSection::CriticalSection(CriticalSection &&other) +CriticalSection::CriticalSection(CriticalSection &&other) noexcept : m_refGlobalMutex(other.m_refGlobalMutex) , inSection(other.inSection) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h index 8735e46a1..7a1b901ac 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h @@ -42,7 +42,7 @@ public: }; public: CriticalSection(); - CriticalSection(CriticalSection &&other); + CriticalSection(CriticalSection &&other) noexcept; explicit CriticalSection(InitialState state); void Enter(); void Leave(); @@ -61,7 +61,8 @@ public: }; public: CriticalSection() {} - explicit CriticalSection(InitialState state) { MPT_UNREFERENCED_PARAMETER(state); } + CriticalSection(CriticalSection &&) noexcept {} + explicit CriticalSection(InitialState) {} void Enter() {} void Leave() {} ~CriticalSection() {} diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h index 3bb6f5d82..21bc93d58 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h @@ -12,10 +12,11 @@ #include "BuildSettings.h" #include "Sndfile.h" -#include "Dither.h" #include "../soundbase/SampleFormat.h" #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" +#include "../soundbase/SampleBuffer.h" +#include "../soundbase/Dither.h" #include "MixerLoops.h" #include "Mixer.h" @@ -23,7 +24,7 @@ OPENMPT_NAMESPACE_BEGIN -template +template class AudioReadTargetBuffer : public IAudioReadTarget { @@ -31,251 +32,116 @@ private: std::size_t countRendered; Dither &dither; protected: - Tsample *outputBuffer; - Tsample * const *outputBuffers; + Tbuffer outputBuffer; public: - AudioReadTargetBuffer(Dither &dither_, Tsample *buffer, Tsample * const *buffers) + AudioReadTargetBuffer(Tbuffer buf, Dither &dither_) : countRendered(0) , dither(dither_) - , outputBuffer(buffer) - , outputBuffers(buffers) + , outputBuffer(buf) { - MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat()).IsValid()); + MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat()).IsValid()); } std::size_t GetRenderedCount() const { return countRendered; } public: - void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + void DataCallback(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { - // Convert to output sample format and optionally perform dithering and clipping if needed - - const SampleFormat sampleFormat = SampleFormatTraits::sampleFormat(); - - if(sampleFormat.IsInt()) - { - dither.Process(MixSoundBuffer, countChunk, channels, sampleFormat.GetBitsPerSample()); - } - - if(outputBuffer) - { - ConvertInterleavedFixedPointToInterleaved(outputBuffer + (channels * countRendered), MixSoundBuffer, channels, countChunk); - } - if(outputBuffers) - { - Tsample *buffers[4] = { nullptr, nullptr, nullptr, nullptr }; - for(std::size_t channel = 0; channel < channels; ++channel) + dither.WithDither( + [&](auto &ditherInstance) { - buffers[channel] = outputBuffers[channel] + countRendered; + ConvertBufferMixFixedToBuffer(advance_audio_buffer(outputBuffer, countRendered), audio_buffer_interleaved(MixSoundBuffer, channels, countChunk), ditherInstance, channels, countChunk); } - ConvertInterleavedFixedPointToNonInterleaved(buffers, MixSoundBuffer, channels, countChunk); - } - + ); + countRendered += countChunk; + } + void DataCallback(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + { + dither.WithDither( + [&](auto &ditherInstance) + { + ConvertBufferMixFloatToBuffer(advance_audio_buffer(outputBuffer, countRendered), audio_buffer_interleaved(MixSoundBuffer, channels, countChunk), ditherInstance, channels, countChunk); + } + ); countRendered += countChunk; } }; -#if defined(MODPLUG_TRACKER) - - -class AudioReadTargetBufferInterleavedDynamic - : public IAudioReadTarget -{ -private: - const SampleFormat sampleFormat; - bool clipFloat; - Dither &dither; - void *buffer; -public: - AudioReadTargetBufferInterleavedDynamic(SampleFormat sampleFormat_, bool clipFloat_, Dither &dither_, void *buffer_) - : sampleFormat(sampleFormat_) - , clipFloat(clipFloat_) - , dither(dither_) - , buffer(buffer_) - { - MPT_ASSERT_ALWAYS(sampleFormat.IsValid()); - } - void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override - { - switch(sampleFormat.value) - { - case SampleFormatUnsigned8: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatInt16: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatInt24: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatInt32: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatFloat32: - if(clipFloat) - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } else - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - } - // increment output buffer for potentially next callback - buffer = reinterpret_cast(buffer) + (sampleFormat.GetBitsPerSample()/8) * channels * countChunk; - } -}; - - -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 +#if defined(LIBOPENMPT_BUILD) template -void ApplyGainBeforeConversionIfAppropriate(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) +void ApplyGainBeforeConversionIfAppropriateFixed(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) { // Apply final output gain for non floating point output ApplyGain(MixSoundBuffer, channels, countChunk, mpt::saturate_round(gainFactor * (1<<16))); } template<> -void ApplyGainBeforeConversionIfAppropriate(int32 * /*MixSoundBuffer*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) +void ApplyGainBeforeConversionIfAppropriateFixed(MixSampleInt * /*MixSoundBuffer*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) { // nothing } template -void ApplyGainAfterConversionIfAppropriate(Tsample * /*buffer*/, Tsample * const * /*buffers*/, std::size_t /*countRendered*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_interleaved /*buffer*/, std::size_t /*countRendered*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) +{ + // nothing +} +template +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_planar /*buffer*/, std::size_t /*countRendered*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) { // nothing } template<> -void ApplyGainAfterConversionIfAppropriate(float *buffer, float * const *buffers, std::size_t countRendered, std::size_t channels, std::size_t countChunk, float gainFactor) +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_interleaved buffer, std::size_t countRendered, std::size_t channels, std::size_t countChunk, float gainFactor) { // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping - ApplyGain(buffer, buffers, countRendered, channels, countChunk, gainFactor); + ApplyGain(buffer, countRendered, channels, countChunk, gainFactor); +} +template<> +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_planar buffer, std::size_t countRendered, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping + ApplyGain(buffer, countRendered, channels, countChunk, gainFactor); } -template +inline void ApplyGainBeforeConversionIfAppropriateFloat(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + // Apply final output gain for non floating point output + ApplyGain(MixSoundBuffer, channels, countChunk, gainFactor); +} + +template class AudioReadTargetGainBuffer - : public AudioReadTargetBuffer + : public AudioReadTargetBuffer { private: - typedef AudioReadTargetBuffer Tbase; + typedef AudioReadTargetBuffer Tbase; private: const float gainFactor; public: - AudioReadTargetGainBuffer(Dither &dither, Tsample *buffer, Tsample * const *buffers, float gainFactor_) - : Tbase(dither, buffer, buffers) + AudioReadTargetGainBuffer(Tbuffer buf, Dither &dither, float gainFactor_) + : Tbase(buf, dither) , gainFactor(gainFactor_) { return; } public: - void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + void DataCallback(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { const std::size_t countRendered_ = Tbase::GetRenderedCount(); - - ApplyGainBeforeConversionIfAppropriate(MixSoundBuffer, channels, countChunk, gainFactor); - + ApplyGainBeforeConversionIfAppropriateFixed(MixSoundBuffer, channels, countChunk, gainFactor); + Tbase::DataCallback(MixSoundBuffer, channels, countChunk); + ApplyGainAfterConversionIfAppropriateFixed(Tbase::outputBuffer, countRendered_, channels, countChunk, gainFactor); + } + void DataCallback(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + { + ApplyGainBeforeConversionIfAppropriateFloat(MixSoundBuffer, channels, countChunk, gainFactor); Tbase::DataCallback(MixSoundBuffer, channels, countChunk); - - ApplyGainAfterConversionIfAppropriate(Tbase::outputBuffer, Tbase::outputBuffers, countRendered_, channels, countChunk, gainFactor); - } }; -#endif // MODPLUG_TRACKER +#endif // LIBOPENMPT_BUILD OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h b/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h index e4f52d3b3..32e6a2ed2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h @@ -29,7 +29,7 @@ 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]; + std::byte buffer[mpt::IO::BUFFERSIZE_TINY]{}; public: @@ -39,7 +39,7 @@ public: eof() : std::range_error("Truncated bit buffer") { } }; - BitReader(mpt::span bytedata) : FileReader(bytedata) { } + BitReader(mpt::span bytedata) : FileReader(bytedata) { } BitReader(const FileReader &other = FileReader()) : FileReader(other) { } off_t GetLength() const diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp index 92450b4c3..88cf18664 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp @@ -20,7 +20,9 @@ OPENMPT_NAMESPACE_BEGIN -//#define MMCMP_LOG +#ifdef MPT_ALL_LOGGING +#define MMCMP_LOG +#endif struct MMCMPFILEHEADER @@ -72,23 +74,23 @@ MPT_BINARY_STRUCT(MMCMPSUBBLOCK, 8) #define MMCMP_ABS16 0x0200 #define MMCMP_ENDIAN 0x0400 -static const uint8 MMCMP8BitCommands[8] = +static constexpr uint8 MMCMP8BitCommands[8] = { 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 }; -static const uint8 MMCMP8BitFetch[8] = +static constexpr uint8 MMCMP8BitFetch[8] = { 3, 3, 3, 3, 2, 1, 0, 0 }; -static const uint16 MMCMP16BitCommands[16] = +static constexpr uint16 MMCMP16BitCommands[16] = { 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0 }; -static const uint8 MMCMP16BitFetch[16] = +static constexpr uint8 MMCMP16BitFetch[16] = { 4, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -198,7 +200,7 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C if(mmh.blktable + 4 * mmh.nblocks > file.GetLength()) return false; containerItems.emplace_back(); - containerItems.back().data_cache = mpt::make_unique >(); + containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); unpackedData.resize(mmh.filesize); @@ -224,9 +226,9 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C uint32 memPos = blkPos + sizeof(MMCMPBLOCK) + blk.sub_blk * sizeof(MMCMPSUBBLOCK); #ifdef MMCMP_LOG - Log("block %d: flags=%04X sub_blocks=%d", nBlock, (uint32)pblk->flags, (uint32)pblk->sub_blk); - Log(" pksize=%d unpksize=%d", pblk->pk_size, pblk->unpk_size); - Log(" tt_entries=%d num_bits=%d\n", pblk->tt_entries, pblk->num_bits); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_("block %1: flags=%2 sub_blocks=%3"))(nBlock, mpt::ufmt::HEX0<4>(static_cast(blk.flags)), static_cast(blk.sub_blk))); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" pksize=%1 unpksize=%2"))(static_cast(blk.pk_size), static_cast(blk.unpk_size))); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" tt_entries=%1 num_bits=%2"))(static_cast(blk.tt_entries), static_cast(blk.num_bits))); #endif // Data is not packed if (!(blk.flags & MMCMP_COMP)) @@ -236,7 +238,7 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C if(!psubblk) return false; if(!MMCMP_IsDstBlockValid(unpackedData, *psubblk)) return false; #ifdef MMCMP_LOG - Log(" Unpacked sub-block %d: offset %d, size=%d\n", i, psubblk->unpk_pos, psubblk->unpk_size); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" Unpacked sub-block %1: offset %2, size=%3"))(i, static_cast(psubblk->unpk_pos), static_cast(psubblk->unpk_size))); #endif if(!file.Seek(memPos)) return false; if(file.ReadRaw(&(unpackedData[psubblk->unpk_pos]), psubblk->unpk_size) != psubblk->unpk_size) return false; @@ -256,10 +258,7 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C uint32 oldval = 0; #ifdef MMCMP_LOG - Log(" 16-bit block: pos=%d size=%d ", psubblk->unpk_pos, psubblk->unpk_size); - if (pblk->flags & MMCMP_DELTA) Log("DELTA "); - if (pblk->flags & MMCMP_ABS16) Log("ABS16 "); - Log("\n"); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" 16-bit block: pos=%1 size=%2 %3 %4"))(psubblk->unpk_pos, psubblk->unpk_size, (blk.flags & MMCMP_DELTA) ? U_("DELTA ") : U_(""), (blk.flags & MMCMP_ABS16) ? U_("ABS16 ") : U_(""))); #endif if(!file.Seek(memPos + blk.tt_entries)) return false; if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp index 286d68411..90c59ac1e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp @@ -23,10 +23,10 @@ OPENMPT_NAMESPACE_BEGIN struct PPBITBUFFER { - uint32 bitcount; - uint32 bitbuffer; - const uint8 *pStart; - const uint8 *pSrc; + uint32 bitcount = 0; + uint32 bitbuffer = 0; + const uint8 *pStart = nullptr; + const uint8 *pSrc = nullptr; uint32 GetBits(uint32 n); }; @@ -36,15 +36,16 @@ uint32 PPBITBUFFER::GetBits(uint32 n) { uint32 result = 0; - for (uint32 i=0; i>= 1; bitcount--; } @@ -52,58 +53,54 @@ uint32 PPBITBUFFER::GetBits(uint32 n) } -static bool PP20_DoUnpack(const uint8 *pSrc, uint32 nSrcLen, uint8 *pDst, uint32 nDstLen) +static bool PP20_DoUnpack(const uint8 *pSrc, uint32 srcLen, uint8 *pDst, uint32 dstLen) { + const std::array modeTable{pSrc[0], pSrc[1], pSrc[2], pSrc[3]}; PPBITBUFFER BitBuffer; - uint32 nBytesLeft; - BitBuffer.pStart = pSrc; - BitBuffer.pSrc = pSrc + nSrcLen - 4; - BitBuffer.bitbuffer = 0; - BitBuffer.bitcount = 0; - BitBuffer.GetBits(pSrc[nSrcLen-1]); - nBytesLeft = nDstLen; - while (nBytesLeft > 0) + BitBuffer.pSrc = pSrc + srcLen - 4; + BitBuffer.GetBits(pSrc[srcLen - 1]); + uint32 bytesLeft = dstLen; + while(bytesLeft > 0) { - if (!BitBuffer.GetBits(1)) + if(!BitBuffer.GetBits(1)) { - uint32 n = 1; - while (n < nBytesLeft) + uint32 count = 1, countAdd; + do { - uint32 code = BitBuffer.GetBits(2); - n += code; - if (code != 3) break; - } - LimitMax(n, nBytesLeft); - for (uint32 i=0; i= nSrcLen) return false; - uint32 nbits = pSrc[n-1]; - uint32 nofs; - if (n==4) + uint32 modeIndex = BitBuffer.GetBits(2); + MPT_CHECKER_ASSUME(modeIndex < 4); + uint32 count = modeIndex + 2, offset; + if(modeIndex == 3) { - nofs = BitBuffer.GetBits( (BitBuffer.GetBits(1)) ? nbits : 7 ); - while (n < nBytesLeft) + offset = BitBuffer.GetBits((BitBuffer.GetBits(1)) ? modeTable[modeIndex] : 7); + uint32 countAdd = 7; + do { - uint32 code = BitBuffer.GetBits(3); - n += code; - if (code != 7) break; - } + countAdd = BitBuffer.GetBits(3); + count += countAdd; + } while(countAdd == 7); } else { - nofs = BitBuffer.GetBits(nbits); + offset = BitBuffer.GetBits(modeTable[modeIndex]); } - LimitMax(n, nBytesLeft); - for (uint32 i=0; i<=n; i++) + LimitMax(count, bytesLeft); + for(uint32 i = 0; i < count; i++) { - pDst[nBytesLeft-1] = (nBytesLeft+nofs < nDstLen) ? pDst[nBytesLeft+nofs] : 0; - if (!--nBytesLeft) break; + pDst[bytesLeft - 1] = (bytesLeft + offset < dstLen) ? pDst[bytesLeft + offset] : 0; + --bytesLeft; } } } @@ -113,8 +110,8 @@ static bool PP20_DoUnpack(const uint8 *pSrc, uint32 nSrcLen, uint8 *pDst, uint32 struct PP20header { - char magic[4]; // "PP20" - uint8be efficiency[4]; + char magic[4]; // "PP20" + uint8 efficiency[4]; }; MPT_BINARY_STRUCT(PP20header, 8) @@ -178,7 +175,7 @@ bool UnpackPP20(std::vector &containerItems, FileReader &file, Co } containerItems.emplace_back(); - containerItems.back().data_cache = mpt::make_unique >(); + containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); FileReader::off_t length = file.GetLength(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp index 9ec76941e..53671c787 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp @@ -174,7 +174,7 @@ bool UnpackUMX(std::vector &containerItems, FileReader &file, Con if(objName >= 0 && static_cast(objName) < names.size()) { - item.name = mpt::ToUnicode(mpt::CharsetISO8859_1, names[objName]); + item.name = mpt::ToUnicode(mpt::Charset::ISO8859_1, names[objName]); } item.file = chunk.ReadChunk(size); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp index 3ade69d22..5eb301fc2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp @@ -21,7 +21,9 @@ OPENMPT_NAMESPACE_BEGIN -//#define MMCMP_LOG +#ifdef MPT_ALL_LOGGING +#define MMCMP_LOG +#endif struct XPKFILEHEADER @@ -102,11 +104,11 @@ static int32 bfexts(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs) static uint8 XPK_ReadTable(int32 index) { - static const uint8 xpk_table[] = { + static constexpr uint8 xpk_table[] = { 2,3,4,5,6,7,8,0,3,2,4,5,6,7,8,0,4,3,5,2,6,7,8,0,5,4,6,2,3,7,8,0,6,5,7,2,3,4,8,0,7,6,8,2,3,4,5,0,8,7,6,2,3,4,5,0 }; if(index < 0) throw XPK_error(); - if(static_cast(index) >= mpt::size(xpk_table)) throw XPK_error(); + if(static_cast(index) >= std::size(xpk_table)) throw XPK_error(); return xpk_table[index]; } @@ -157,9 +159,9 @@ static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector &un if (type != 1) { - #ifdef MMCMP_LOG - Log("Invalid XPK type! (%d bytes left)\n", len); - #endif + #ifdef MMCMP_LOG + MPT_LOG(LogDebug, "XPK", mpt::format(U_("Invalid XPK type! (%1 bytes left)"))(len)); + #endif break; } len -= cup1; @@ -344,7 +346,7 @@ static bool ValidateHeader(const XPKFILEHEADER &header) { return false; } - MPT_STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); + static_assert(sizeof(XPKFILEHEADER) >= 8); if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) { return false; @@ -410,12 +412,12 @@ bool UnpackXPK(std::vector &containerItems, FileReader &file, Con } containerItems.emplace_back(); - containerItems.back().data_cache = mpt::make_unique >(); + containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); -#ifdef MMCMP_LOG - Log("XPK detected (SrcLen=%d DstLen=%d) filesize=%d\n", header.SrcLen, header.DstLen, file.GetLength()); -#endif + #ifdef MMCMP_LOG + MPT_LOG(LogDebug, "XPK", mpt::uformat(U_("XPK detected (SrcLen=%1 DstLen=%2) filesize=%3"))(static_cast(header.SrcLen), static_cast(header.DstLen), file.GetLength())); + #endif bool result = false; try { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp deleted file mode 100644 index 697065d76..000000000 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Dither.cpp - * ---------- - * Purpose: Dithering when converting to lower resolution sample formats. - * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - -#include "stdafx.h" - -#include "Dither.h" -#include "Mixer.h" - -#include "../common/misc_util.h" - - -OPENMPT_NAMESPACE_BEGIN - - -////////////////////////////////////////////////////////////////////////// -// Noise Shaping (Dithering) - - -mpt::ustring Dither::GetModeName(DitherMode mode) -{ - switch(mode) - { - 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; - } -} - - -#if MPT_COMPILER_MSVC -#pragma warning(disable:4731) // ebp modified -#endif - - -#ifdef ENABLE_X86 - -void X86_Dither(int32 *pBuffer, uint32 nSamples, uint32 nBits, DitherModPlugState *state) -{ - if(nBits + MIXING_ATTENUATION + 1 >= 32) //if(nBits>16) - { - return; - } - - static int gDitherA_global, gDitherB_global; - - int gDitherA = state ? state->rng_a : gDitherA_global; - int gDitherB = state ? state->rng_b : gDitherB_global; - - _asm { - mov esi, pBuffer // esi = pBuffer+i - mov eax, nSamples // ebp = i - mov ecx, nBits // ecx = number of bits of noise - mov edi, gDitherA // Noise generation - mov ebx, gDitherB - add ecx, MIXING_ATTENUATION - add ecx, 1 - push ebp - mov ebp, eax -noiseloop: - rol edi, 1 - mov eax, dword ptr [esi] - xor edi, 0x10204080 - add esi, 4 - lea edi, [ebx*4+edi+0x78649E7D] - mov edx, edi - rol edx, 16 - lea edx, [edx*4+edx] - add ebx, edx - mov edx, ebx - sar edx, cl - add eax, edx - dec ebp - mov dword ptr [esi-4], eax - jnz noiseloop - pop ebp - mov gDitherA, edi - mov gDitherB, ebx - } - - if(state) state->rng_a = gDitherA; else gDitherA_global = gDitherA; - if(state) state->rng_b = gDitherB; else gDitherB_global = gDitherB; - -} - -#endif // ENABLE_X86 - - -static MPT_FORCEINLINE int32 dither_rand(uint32 &a, uint32 &b) -{ - a = (a << 1) | (a >> 31); - a ^= 0x10204080u; - a += 0x78649E7Du + (b * 4); - b += ((a << 16 ) | (a >> 16)) * 5; - return static_cast(b); -} - -static void C_Dither(int32 *pBuffer, std::size_t count, uint32 nBits, DitherModPlugState *state) -{ - if(nBits + MIXING_ATTENUATION + 1 >= 32) //if(nBits>16) - { - return; - } - - static uint32 global_a = 0; - static uint32 global_b = 0; - - uint32 a = state ? state->rng_a : global_a; - uint32 b = state ? state->rng_b : global_b; - - while(count--) - { - *pBuffer += mpt::rshift_signed(dither_rand(a, b), (nBits + MIXING_ATTENUATION + 1)); - pBuffer++; - } - - if(state) state->rng_a = a; else global_a = a; - if(state) state->rng_b = b; else global_b = b; - -} - -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); - #else // !ENABLE_X86 - C_Dither(pBuffer, count * channels, nBits, &state); - #endif // ENABLE_X86 -} - - -template -struct Dither_SimpleTemplate -{ -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; - MPT_CONSTANT_IF(rshift <= 0) - { - // nothing to dither - return; - } - const int round_mask = ~((1<(prng, noise_bits) + mpt::random(prng, noise_bits)) >> 1; - } else - { - unoise = mpt::random(prng, noise_bits); - } - int noise = static_cast(unoise) - noise_bias; // un-bias - int val = *mixbuffer; - MPT_CONSTANT_IF(shaped) - { - val += (s.error[channel] >> 1); - } - int rounded = (val + noise + round_offset) & round_mask;; - s.error[channel] = val - rounded; - *mixbuffer = rounded; - mixbuffer++; - } - } - state = s; -} -}; - -static void Dither_Simple(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits, DitherSimpleState &state, mpt::fast_prng &prng) -{ - switch(bits) - { - case 8: - switch(channels) - { - case 1: - Dither_SimpleTemplate<8,1>()(mixbuffer, count, state, prng); - break; - case 2: - Dither_SimpleTemplate<8,2>()(mixbuffer, count, state, prng); - break; - case 4: - Dither_SimpleTemplate<8,4>()(mixbuffer, count, state, prng); - break; - } - break; - case 16: - switch(channels) - { - case 1: - Dither_SimpleTemplate<16,1>()(mixbuffer, count, state, prng); - break; - case 2: - Dither_SimpleTemplate<16,2>()(mixbuffer, count, state, prng); - break; - case 4: - Dither_SimpleTemplate<16,4>()(mixbuffer, count, state, prng); - break; - } - break; - case 24: - switch(channels) - { - case 1: - Dither_SimpleTemplate<24,1>()(mixbuffer, count, state, prng); - break; - case 2: - Dither_SimpleTemplate<24,2>()(mixbuffer, count, state, prng); - break; - case 4: - Dither_SimpleTemplate<24,4>()(mixbuffer, count, state, prng); - break; - } - break; - } -} - - -void Dither::Reset() -{ - state.Reset(); - } - - -void Dither::SetMode(DitherMode mode_) -{ - mode = mode_; -} - - -DitherMode Dither::GetMode() const -{ - return mode; -} - - -void Dither::Process(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits) -{ - switch(mode) - { - case DitherNone: - // nothing - break; - case DitherModPlug: - Dither_ModPlug(mixbuffer, count, channels, bits, state.modplug); - break; - case DitherSimple: - Dither_Simple(mixbuffer, count, channels, bits, state.simple, state.prng); - break; - case DitherDefault: - default: - Dither_ModPlug(mixbuffer, count, channels, bits, state.modplug); - break; - } -} - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h deleted file mode 100644 index db83780d8..000000000 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Dither.h - * -------- - * Purpose: Dithering when converting to lower resolution sample formats. - * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - -#include "BuildSettings.h" - - -#include "../common/mptRandom.h" - - -OPENMPT_NAMESPACE_BEGIN - - -struct DitherModPlugState -{ - uint32 rng_a; - uint32 rng_b; - DitherModPlugState() - { - rng_a = 0; - rng_b = 0; - } -}; - -struct DitherSimpleState -{ - int32 error[4]; - DitherSimpleState() { - error[0] = 0; - error[1] = 0; - error[2] = 0; - error[3] = 0; - } -}; - -struct DitherState -{ - DitherModPlugState modplug; - DitherSimpleState simple; - mpt::fast_prng prng; - void Reset() - { - modplug = DitherModPlugState(); - simple = DitherSimpleState(); - } - template - DitherState(Trd & rd) - : prng(mpt::make_prng(rd)) - { - return; - } -}; - -enum DitherMode -{ - DitherNone = 0, - DitherDefault = 1, // chosen by OpenMPT code, might change - DitherModPlug = 2, // rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker) - DitherSimple = 3, // rectangular, 1 bit depth, simple 1st order noise shaping - NumDitherModes -}; - -class Dither -{ -private: - DitherState state; - DitherMode mode; -public: - template - Dither(Trd & rd) - : state(rd) - { - mode = DitherDefault; - } - void SetMode(DitherMode mode_); - DitherMode GetMode() const; - void Reset(); - void Process(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits); - static mpt::ustring GetModeName(DitherMode mode); -}; - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp index 39d10a012..4f849747a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp @@ -28,149 +28,179 @@ OPENMPT_NAMESPACE_BEGIN #ifdef MODPLUG_TRACKER -//#define DLSBANK_LOG -//#define DLSINSTR_LOG +#ifdef MPT_ALL_LOGGING +#define DLSBANK_LOG +#define DLSINSTR_LOG +#endif #define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001 +// Region Flags +enum RegionFlags +{ + DLSREGION_KEYGROUPMASK = 0x0F, + DLSREGION_OVERRIDEWSMP = 0x10, + DLSREGION_PINGPONGLOOP = 0x20, + DLSREGION_SAMPLELOOP = 0x40, + DLSREGION_SELFNONEXCLUSIVE = 0x80, + DLSREGION_SUSTAINLOOP = 0x100, + DLSREGION_ISGLOBAL = 0x200, +}; + /////////////////////////////////////////////////////////////////////////// // Articulation connection graph definitions -// Generic Sources -#define CONN_SRC_NONE 0x0000 -#define CONN_SRC_LFO 0x0001 -#define CONN_SRC_KEYONVELOCITY 0x0002 -#define CONN_SRC_KEYNUMBER 0x0003 -#define CONN_SRC_EG1 0x0004 -#define CONN_SRC_EG2 0x0005 -#define CONN_SRC_PITCHWHEEL 0x0006 +enum ConnectionSource : uint16 +{ + // Generic Sources + CONN_SRC_NONE = 0x0000, + CONN_SRC_LFO = 0x0001, + CONN_SRC_KEYONVELOCITY = 0x0002, + CONN_SRC_KEYNUMBER = 0x0003, + CONN_SRC_EG1 = 0x0004, + CONN_SRC_EG2 = 0x0005, + CONN_SRC_PITCHWHEEL = 0x0006, -#define CONN_SRC_POLYPRESSURE 0x0007 -#define CONN_SRC_CHANNELPRESSURE 0x0008 -#define CONN_SRC_VIBRATO 0x0009 + CONN_SRC_POLYPRESSURE = 0x0007, + CONN_SRC_CHANNELPRESSURE = 0x0008, + CONN_SRC_VIBRATO = 0x0009, -// Midi Controllers 0-127 -#define CONN_SRC_CC1 0x0081 -#define CONN_SRC_CC7 0x0087 -#define CONN_SRC_CC10 0x008a -#define CONN_SRC_CC11 0x008b + // Midi Controllers 0-127 + CONN_SRC_CC1 = 0x0081, + CONN_SRC_CC7 = 0x0087, + CONN_SRC_CC10 = 0x008a, + CONN_SRC_CC11 = 0x008b, -#define CONN_SRC_CC91 0x00db -#define CONN_SRC_CC93 0x00dd + CONN_SRC_CC91 = 0x00db, + CONN_SRC_CC93 = 0x00dd, -#define CONN_SRC_RPN0 0x0100 -#define CONN_SRC_RPN1 0x0101 -#define CONN_SRC_RPN2 0x0102 + CONN_SRC_RPN0 = 0x0100, + CONN_SRC_RPN1 = 0x0101, + CONN_SRC_RPN2 = 0x0102, +}; -// Generic Destinations -#define CONN_DST_NONE 0x0000 -#define CONN_DST_ATTENUATION 0x0001 -#define CONN_DST_RESERVED 0x0002 -#define CONN_DST_PITCH 0x0003 -#define CONN_DST_PAN 0x0004 +enum ConnectionDestination : uint16 +{ + // Generic Destinations + CONN_DST_NONE = 0x0000, + CONN_DST_ATTENUATION = 0x0001, + CONN_DST_RESERVED = 0x0002, + CONN_DST_PITCH = 0x0003, + CONN_DST_PAN = 0x0004, -// LFO Destinations -#define CONN_DST_LFO_FREQUENCY 0x0104 -#define CONN_DST_LFO_STARTDELAY 0x0105 + // LFO Destinations + CONN_DST_LFO_FREQUENCY = 0x0104, + CONN_DST_LFO_STARTDELAY = 0x0105, -#define CONN_DST_KEYNUMBER 0x0005 + CONN_DST_KEYNUMBER = 0x0005, -// EG1 Destinations -#define CONN_DST_EG1_ATTACKTIME 0x0206 -#define CONN_DST_EG1_DECAYTIME 0x0207 -#define CONN_DST_EG1_RESERVED 0x0208 -#define CONN_DST_EG1_RELEASETIME 0x0209 -#define CONN_DST_EG1_SUSTAINLEVEL 0x020a + // EG1 Destinations + CONN_DST_EG1_ATTACKTIME = 0x0206, + CONN_DST_EG1_DECAYTIME = 0x0207, + CONN_DST_EG1_RESERVED = 0x0208, + CONN_DST_EG1_RELEASETIME = 0x0209, + CONN_DST_EG1_SUSTAINLEVEL = 0x020a, -#define CONN_DST_EG1_DELAYTIME 0x020b -#define CONN_DST_EG1_HOLDTIME 0x020c -#define CONN_DST_EG1_SHUTDOWNTIME 0x020d + CONN_DST_EG1_DELAYTIME = 0x020b, + CONN_DST_EG1_HOLDTIME = 0x020c, + CONN_DST_EG1_SHUTDOWNTIME = 0x020d, -// EG2 Destinations -#define CONN_DST_EG2_ATTACKTIME 0x030a -#define CONN_DST_EG2_DECAYTIME 0x030b -#define CONN_DST_EG2_RESERVED 0x030c -#define CONN_DST_EG2_RELEASETIME 0x030d -#define CONN_DST_EG2_SUSTAINLEVEL 0x030e + // EG2 Destinations + CONN_DST_EG2_ATTACKTIME = 0x030a, + CONN_DST_EG2_DECAYTIME = 0x030b, + CONN_DST_EG2_RESERVED = 0x030c, + CONN_DST_EG2_RELEASETIME = 0x030d, + CONN_DST_EG2_SUSTAINLEVEL = 0x030e, -#define CONN_DST_EG2_DELAYTIME 0x030f -#define CONN_DST_EG2_HOLDTIME 0x0310 + CONN_DST_EG2_DELAYTIME = 0x030f, + CONN_DST_EG2_HOLDTIME = 0x0310, -#define CONN_TRN_NONE 0x0000 -#define CONN_TRN_CONCAVE 0x0001 + CONN_TRN_NONE = 0x0000, + CONN_TRN_CONCAVE = 0x0001, +}; ////////////////////////////////////////////////////////// // Supported DLS1 Articulations -#define MAKE_ART(src, ctl, dst) ( ((dst)<<16) | ((ctl)<<8) | (src) ) +// [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID +constexpr uint32 DLSArt(uint8 src, uint8 ctl, uint16 dst) +{ + return (dst << 16u) | (ctl << 8u) | src; +} -// Vibrato / Tremolo -#define ART_LFO_FREQUENCY MAKE_ART (CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_FREQUENCY) -#define ART_LFO_STARTDELAY MAKE_ART (CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_STARTDELAY) -#define ART_LFO_ATTENUATION MAKE_ART (CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_ATTENUATION) -#define ART_LFO_PITCH MAKE_ART (CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_PITCH) -#define ART_LFO_MODWTOATTN MAKE_ART (CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_ATTENUATION) -#define ART_LFO_MODWTOPITCH MAKE_ART (CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_PITCH) +enum DLSArt : uint32 +{ + // Vibrato / Tremolo + ART_LFO_FREQUENCY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_FREQUENCY), + ART_LFO_STARTDELAY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_STARTDELAY), + ART_LFO_ATTENUATION = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_ATTENUATION), + ART_LFO_PITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_PITCH), + ART_LFO_MODWTOATTN = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_ATTENUATION), + ART_LFO_MODWTOPITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_PITCH), -// Volume Envelope -#define ART_VOL_EG_ATTACKTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME) -#define ART_VOL_EG_DECAYTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME) -#define ART_VOL_EG_SUSTAINLEVEL MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SUSTAINLEVEL) -#define ART_VOL_EG_RELEASETIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_RELEASETIME) -#define ART_VOL_EG_DELAYTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DELAYTIME) -#define ART_VOL_EG_HOLDTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_HOLDTIME) -#define ART_VOL_EG_SHUTDOWNTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SHUTDOWNTIME) -#define ART_VOL_EG_VELTOATTACK MAKE_ART(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME) -#define ART_VOL_EG_KEYTODECAY MAKE_ART(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME) + // Volume Envelope + ART_VOL_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME), + ART_VOL_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME), + ART_VOL_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SUSTAINLEVEL), + ART_VOL_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_RELEASETIME), + ART_VOL_EG_DELAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DELAYTIME), + ART_VOL_EG_HOLDTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_HOLDTIME), + ART_VOL_EG_SHUTDOWNTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SHUTDOWNTIME), + ART_VOL_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME), + ART_VOL_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME), -// Pitch Envelope -#define ART_PITCH_EG_ATTACKTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME) -#define ART_PITCH_EG_DECAYTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME) -#define ART_PITCH_EG_SUSTAINLEVEL MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_SUSTAINLEVEL) -#define ART_PITCH_EG_RELEASETIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_RELEASETIME) -#define ART_PITCH_EG_VELTOATTACK MAKE_ART(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME) -#define ART_PITCH_EG_KEYTODECAY MAKE_ART(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME) + // Pitch Envelope + ART_PITCH_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME), + ART_PITCH_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME), + ART_PITCH_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_SUSTAINLEVEL), + ART_PITCH_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_RELEASETIME), + ART_PITCH_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME), + ART_PITCH_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME), -// Default Pan -#define ART_DEFAULTPAN MAKE_ART (CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_PAN) + // Default Pan + ART_DEFAULTPAN = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_PAN), +}; ////////////////////////////////////////////////////////// // DLS IFF Chunk IDs -// Standard IFF chunks IDs -#define IFFID_FORM 0x4d524f46 -#define IFFID_RIFF 0x46464952 -#define IFFID_LIST 0x5453494C -#define IFFID_INFO 0x4F464E49 +enum IFFChunkID : uint32 +{ + // Standard IFF chunks IDs + IFFID_FORM = 0x4d524f46, + IFFID_RIFF = 0x46464952, + IFFID_LIST = 0x5453494C, + IFFID_INFO = 0x4F464E49, -// IFF Info fields -#define IFFID_ICOP 0x504F4349 -#define IFFID_INAM 0x4D414E49 -#define IFFID_ICMT 0x544D4349 -#define IFFID_IENG 0x474E4549 -#define IFFID_ISFT 0x54465349 -#define IFFID_ISBJ 0x4A425349 + // IFF Info fields + IFFID_ICOP = 0x504F4349, + IFFID_INAM = 0x4D414E49, + IFFID_ICMT = 0x544D4349, + IFFID_IENG = 0x474E4549, + IFFID_ISFT = 0x54465349, + IFFID_ISBJ = 0x4A425349, -// Wave IFF chunks IDs -#define IFFID_wave 0x65766177 -#define IFFID_wsmp 0x706D7377 + // Wave IFF chunks IDs + IFFID_wave = 0x65766177, + IFFID_wsmp = 0x706D7377, -#define IFFID_XDLS 0x534c4458 -#define IFFID_DLS 0x20534C44 -#define IFFID_MLS 0x20534C4D -#define IFFID_RMID 0x44494D52 -#define IFFID_colh 0x686C6F63 -#define IFFID_ins 0x20736E69 -#define IFFID_insh 0x68736E69 -#define IFFID_ptbl 0x6C627470 -#define IFFID_wvpl 0x6C707677 -#define IFFID_rgn 0x206E6772 -#define IFFID_rgn2 0x326E6772 -#define IFFID_rgnh 0x686E6772 -#define IFFID_wlnk 0x6B6E6C77 -#define IFFID_art1 0x31747261 -#define IFFID_art2 0x32747261 + IFFID_XDLS = 0x534c4458, + IFFID_DLS = 0x20534C44, + IFFID_MLS = 0x20534C4D, + IFFID_RMID = 0x44494D52, + IFFID_colh = 0x686C6F63, + IFFID_ins = 0x20736E69, + IFFID_insh = 0x68736E69, + IFFID_ptbl = 0x6C627470, + IFFID_wvpl = 0x6C707677, + IFFID_rgn = 0x206E6772, + IFFID_rgn2 = 0x326E6772, + IFFID_rgnh = 0x686E6772, + IFFID_wlnk = 0x6B6E6C77, + IFFID_art1 = 0x31747261, + IFFID_art2 = 0x32747261, +}; ////////////////////////////////////////////////////////// // DLS Structures definitions @@ -186,24 +216,20 @@ MPT_BINARY_STRUCT(IFFCHUNK, 8) struct RIFFCHUNKID { uint32le id_RIFF; - union - { - uint32le riff_len; - uint32be riff_len_be; - }; + uint32le riff_len; uint32le id_DLS; }; MPT_BINARY_STRUCT(RIFFCHUNKID, 12) -struct LISTCHUNK +struct LISTChunk { uint32le id; uint32le len; uint32le listid; }; -MPT_BINARY_STRUCT(LISTCHUNK, 12) +MPT_BINARY_STRUCT(LISTChunk, 12) struct DLSRGNRANGE { @@ -230,50 +256,42 @@ struct PTBLCHUNK MPT_BINARY_STRUCT(PTBLCHUNK, 8) -struct INSHCHUNK +struct INSHChunk { - uint32le id; - uint32le len; uint32le cRegions; uint32le ulBank; uint32le ulInstrument; }; -MPT_BINARY_STRUCT(INSHCHUNK, 20) +MPT_BINARY_STRUCT(INSHChunk, 12) -struct RGNHCHUNK +struct RGNHChunk { - uint32le id; - uint32le len; DLSRGNRANGE RangeKey; DLSRGNRANGE RangeVelocity; uint16le fusOptions; uint16le usKeyGroup; }; -MPT_BINARY_STRUCT(RGNHCHUNK, 20) +MPT_BINARY_STRUCT(RGNHChunk, 12) -struct WLNKCHUNK +struct WLNKChunk { - uint32le id; - uint32le len; uint16le fusOptions; uint16le usPhaseGroup; uint32le ulChannel; uint32le ulTableIndex; }; -MPT_BINARY_STRUCT(WLNKCHUNK, 20) +MPT_BINARY_STRUCT(WLNKChunk, 12) -struct ART1CHUNK +struct ART1Chunk { - uint32le id; - uint32le len; uint32le cbSize; uint32le cConnectionBlocks; }; -MPT_BINARY_STRUCT(ART1CHUNK, 16) +MPT_BINARY_STRUCT(ART1Chunk, 8) struct CONNECTIONBLOCK { @@ -288,8 +306,6 @@ MPT_BINARY_STRUCT(CONNECTIONBLOCK, 12) struct WSMPCHUNK { - uint32le id; - uint32le len; uint32le cbSize; uint16le usUnityNote; int16le sFineTune; @@ -298,7 +314,7 @@ struct WSMPCHUNK uint32le cSampleLoops; }; -MPT_BINARY_STRUCT(WSMPCHUNK, 28) +MPT_BINARY_STRUCT(WSMPCHUNK, 20) struct WSMPSAMPLELOOP { @@ -315,22 +331,28 @@ MPT_BINARY_STRUCT(WSMPSAMPLELOOP, 16) ///////////////////////////////////////////////////////////////////// // SF2 IFF Chunk IDs -#define IFFID_sfbk 0x6b626673 -#define IFFID_sdta 0x61746473 -#define IFFID_pdta 0x61746470 -#define IFFID_phdr 0x72646870 -#define IFFID_pbag 0x67616270 -#define IFFID_pgen 0x6E656770 -#define IFFID_inst 0x74736E69 -#define IFFID_ibag 0x67616269 -#define IFFID_igen 0x6E656769 -#define IFFID_shdr 0x72646873 +enum SF2ChunkID : uint32 +{ + IFFID_sfbk = 0x6b626673, + IFFID_sfpk = 0x6b706673, + IFFID_sdta = 0x61746473, + IFFID_pdta = 0x61746470, + IFFID_phdr = 0x72646870, + IFFID_pbag = 0x67616270, + IFFID_pgen = 0x6E656770, + IFFID_inst = 0x74736E69, + IFFID_ibag = 0x67616269, + IFFID_igen = 0x6E656769, + IFFID_shdr = 0x72646873, +}; /////////////////////////////////////////// // SF2 Generators IDs -enum SF2Generators +enum SF2Generators : uint16 { + SF2_GEN_START_LOOP_FINE = 2, + SF2_GEN_END_LOOP_FINE = 3, SF2_GEN_MODENVTOFILTERFC = 11, SF2_GEN_PAN = 17, SF2_GEN_DECAYMODENV = 28, @@ -341,11 +363,14 @@ enum SF2Generators SF2_GEN_RELEASEVOLENV = 38, SF2_GEN_INSTRUMENT = 41, SF2_GEN_KEYRANGE = 43, + SF2_GEN_START_LOOP_COARSE = 45, SF2_GEN_ATTENUATION = 48, + SF2_GEN_END_LOOP_COARSE = 50, SF2_GEN_COARSETUNE = 51, SF2_GEN_FINETUNE = 52, SF2_GEN_SAMPLEID = 53, SF2_GEN_SAMPLEMODES = 54, + SF2_GEN_SCALE_TUNING = 56, SF2_GEN_KEYGROUP = 57, SF2_GEN_UNITYNOTE = 58, }; @@ -426,18 +451,13 @@ MPT_BINARY_STRUCT(SFSAMPLE, 46) ///////////////////////////////////////////////////////////////////// -struct SF2LOADERINFO +struct SF2LoaderInfo { - uint32 nPresetBags; - const SFPRESETBAG *pPresetBags; - uint32 nPresetGens; - const SFGENLIST *pPresetGens; - uint32 nInsts; - const SFINST *pInsts; - uint32 nInstBags; - const SFINSTBAG *pInstBags; - uint32 nInstGens; - const SFINSTGENLIST *pInstGens; + FileReader presetBags; + FileReader presetGens; + FileReader insts; + FileReader instBags; + FileReader instGens; }; @@ -470,7 +490,7 @@ int32 CDLSBank::DLS32BitTimeCentsToMilliseconds(int32 lTimeCents) // tc = log2(time[secs]) * 1200*65536 // time[secs] = 2^(tc/(1200*65536)) if ((uint32)lTimeCents == 0x80000000) return 0; - double fmsecs = 1000.0 * pow(2.0, ((double)lTimeCents)/(1200.0*65536.0)); + double fmsecs = 1000.0 * std::pow(2.0, ((double)lTimeCents)/(1200.0*65536.0)); if (fmsecs < -32767) return -32767; if (fmsecs > 32767) return 32767; return (int32)fmsecs; @@ -481,7 +501,7 @@ int32 CDLSBank::DLS32BitTimeCentsToMilliseconds(int32 lTimeCents) int32 CDLSBank::DLS32BitRelativeGainToLinear(int32 lCentibels) { // v = 10^(cb/(200*65536)) * V - return (int32)(65536.0 * pow(10.0, ((double)lCentibels)/(200*65536.0)) ); + return (int32)(65536.0 * std::pow(10.0, ((double)lCentibels)/(200*65536.0)) ); } @@ -489,7 +509,7 @@ int32 CDLSBank::DLS32BitRelativeLinearToGain(int32 lGain) { // cb = log10(v/V) * 200 * 65536 if (lGain <= 0) return -960 * 65536; - return (int32)( 200*65536.0 * log10( ((double)lGain)/65536.0 ) ); + return (int32)(200 * 65536.0 * std::log10(((double)lGain) / 65536.0)); } @@ -526,7 +546,7 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename) // Miles Sound System do { - uint32 len = riff.riff_len_be; + uint32 len = mpt::bit_cast(riff.riff_len); if (len <= 4) break; if (riff.id_DLS == IFFID_XDLS) { @@ -621,9 +641,9 @@ bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, co 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))) + if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames))) { - mpt::String::CopyN(pIns->name, szMidiPercussionNames[key - 24]); + pIns->name = szMidiPercussionNames[key - 24]; } return true; } @@ -635,28 +655,28 @@ bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, co /////////////////////////////////////////////////////////////// // Update DLS instrument definition from an IFF chunk -bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK *pchunk, uint32 dwMaxLen) +bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk) { - if ((!pchunk->len) || (pchunk->len+8 > dwMaxLen)) return false; - if (pchunk->id == IFFID_LIST) + IFFCHUNK header; + chunk.ReadStruct(header); + if(!header.len || !chunk.CanRead(header.len)) + return false; + if(header.id == IFFID_LIST) { - LISTCHUNK *plist = (LISTCHUNK *)pchunk; - uint32 dwPos = 12; - while (dwPos < plist->len) + uint32 listid = chunk.ReadUint32LE(); + while(chunk.CanRead(sizeof(IFFCHUNK))) { - const IFFCHUNK *p = (const IFFCHUNK *)(((uint8 *)plist) + dwPos); - if (!(p->id & 0xFF)) + IFFCHUNK subHeader; + chunk.ReadStruct(subHeader); + chunk.SkipBack(sizeof(IFFCHUNK)); + FileReader subData = chunk.ReadChunk(subHeader.len + sizeof(IFFCHUNK)); + if(subHeader.len & 1) { - p = (const IFFCHUNK *)( ((uint8 *)p)+1 ); - dwPos++; + chunk.Skip(1); } - if (dwPos + p->len + 8 <= plist->len + 12) - { - UpdateInstrumentDefinition(pDlsIns, p, p->len+8); - } - dwPos += p->len + 8; + UpdateInstrumentDefinition(pDlsIns, subData); } - switch(plist->listid) + switch(listid) { case IFFID_rgn: // Level 1 region case IFFID_rgn2: // Level 2 region @@ -665,64 +685,75 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK } } else { - switch(pchunk->id) + switch(header.id) { case IFFID_insh: - pDlsIns->ulBank = ((INSHCHUNK *)pchunk)->ulBank; - pDlsIns->ulInstrument = ((INSHCHUNK *)pchunk)->ulInstrument; - //Log("%3d regions, bank 0x%04X instrument %3d\n", ((INSHCHUNK *)pchunk)->cRegions, pDlsIns->ulBank, pDlsIns->ulInstrument); + { + INSHChunk insh; + chunk.ReadStruct(insh); + pDlsIns->ulBank = insh.ulBank; + pDlsIns->ulInstrument = insh.ulInstrument; + //Log("%3d regions, bank 0x%04X instrument %3d\n", insh.cRegions, pDlsIns->ulBank, pDlsIns->ulInstrument); break; + } case IFFID_rgnh: if (pDlsIns->nRegions < DLSMAXREGIONS) { - RGNHCHUNK *p = (RGNHCHUNK *)pchunk; - DLSREGION *pregion = &pDlsIns->Regions[pDlsIns->nRegions]; - pregion->uKeyMin = (uint8)p->RangeKey.usLow; - pregion->uKeyMax = (uint8)p->RangeKey.usHigh; - pregion->fuOptions = (uint8)(p->usKeyGroup & DLSREGION_KEYGROUPMASK); - if (p->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE) pregion->fuOptions |= DLSREGION_SELFNONEXCLUSIVE; - //Log(" Region %d: fusOptions=0x%02X usKeyGroup=0x%04X ", pDlsIns->nRegions, p->fusOptions, p->usKeyGroup); - //Log("KeyRange[%3d,%3d] ", p->RangeKey.usLow, p->RangeKey.usHigh); + RGNHChunk rgnh; + chunk.ReadStruct(rgnh); + DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + region.uKeyMin = (uint8)rgnh.RangeKey.usLow; + region.uKeyMax = (uint8)rgnh.RangeKey.usHigh; + region.fuOptions = (uint8)(rgnh.usKeyGroup & DLSREGION_KEYGROUPMASK); + if(rgnh.fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE) + region.fuOptions |= DLSREGION_SELFNONEXCLUSIVE; + //Log(" Region %d: fusOptions=0x%02X usKeyGroup=0x%04X ", pDlsIns->nRegions, rgnh.fusOptions, rgnh.usKeyGroup); + //Log("KeyRange[%3d,%3d] ", rgnh.RangeKey.usLow, rgnh.RangeKey.usHigh); } break; case IFFID_wlnk: if (pDlsIns->nRegions < DLSMAXREGIONS) { - DLSREGION *pregion = &pDlsIns->Regions[pDlsIns->nRegions]; - WLNKCHUNK *p = (WLNKCHUNK *)pchunk; - pregion->nWaveLink = (uint16)p->ulTableIndex; - if ((pregion->nWaveLink < uint16_max) && (pregion->nWaveLink >= m_nMaxWaveLink)) m_nMaxWaveLink = pregion->nWaveLink + 1; - //Log(" WaveLink %d: fusOptions=0x%02X usPhaseGroup=0x%04X ", pDlsIns->nRegions, p->fusOptions, p->usPhaseGroup); - //Log("ulChannel=%d ulTableIndex=%4d\n", p->ulChannel, p->ulTableIndex); + WLNKChunk wlnk; + chunk.ReadStruct(wlnk); + DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + region.nWaveLink = (uint16)wlnk.ulTableIndex; + if((region.nWaveLink < Util::MaxValueOfType(region.nWaveLink)) && (region.nWaveLink >= m_nMaxWaveLink)) + m_nMaxWaveLink = region.nWaveLink + 1; + //Log(" WaveLink %d: fusOptions=0x%02X usPhaseGroup=0x%04X ", pDlsIns->nRegions, wlnk.fusOptions, wlnk.usPhaseGroup); + //Log("ulChannel=%d ulTableIndex=%4d\n", wlnk.ulChannel, wlnk.ulTableIndex); } break; case IFFID_wsmp: if (pDlsIns->nRegions < DLSMAXREGIONS) { - DLSREGION *pregion = &pDlsIns->Regions[pDlsIns->nRegions]; - WSMPCHUNK *p = (WSMPCHUNK *)pchunk; - pregion->fuOptions |= DLSREGION_OVERRIDEWSMP; - pregion->uUnityNote = (uint8)p->usUnityNote; - pregion->sFineTune = p->sFineTune; - int32 lVolume = DLS32BitRelativeGainToLinear(p->lAttenuation) / 256; + DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + WSMPCHUNK wsmp; + chunk.ReadStruct(wsmp); + region.fuOptions |= DLSREGION_OVERRIDEWSMP; + region.uUnityNote = (uint8)wsmp.usUnityNote; + region.sFineTune = wsmp.sFineTune; + int32 lVolume = DLS32BitRelativeGainToLinear(wsmp.lAttenuation) / 256; if (lVolume > 256) lVolume = 256; if (lVolume < 4) lVolume = 4; - pregion->usVolume = (uint16)lVolume; + region.usVolume = (uint16)lVolume; //Log(" WaveSample %d: usUnityNote=%2d sFineTune=%3d ", pDlsEnv->nRegions, p->usUnityNote, p->sFineTune); //Log("fulOptions=0x%04X loops=%d\n", p->fulOptions, p->cSampleLoops); - if ((p->cSampleLoops) && (p->cbSize + sizeof(WSMPSAMPLELOOP) <= p->len)) + if((wsmp.cSampleLoops) && (wsmp.cbSize + sizeof(WSMPSAMPLELOOP) <= header.len)) { - WSMPSAMPLELOOP *ploop = (WSMPSAMPLELOOP *)(((uint8 *)p)+8+p->cbSize); + WSMPSAMPLELOOP loop; + chunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize); + chunk.ReadStruct(loop); //Log("looptype=%2d loopstart=%5d loopend=%5d\n", ploop->ulLoopType, ploop->ulLoopStart, ploop->ulLoopLength); - if (ploop->ulLoopLength > 3) + if(loop.ulLoopLength > 3) { - pregion->fuOptions |= DLSREGION_SAMPLELOOP; - //if (ploop->ulLoopType) pregion->fuOptions |= DLSREGION_PINGPONGLOOP; - pregion->ulLoopStart = ploop->ulLoopStart; - pregion->ulLoopEnd = ploop->ulLoopStart + ploop->ulLoopLength; + region.fuOptions |= DLSREGION_SAMPLELOOP; + //if(loop.ulLoopType) region.fuOptions |= DLSREGION_PINGPONGLOOP; + region.ulLoopStart = loop.ulLoopStart; + region.ulLoopEnd = loop.ulLoopStart + loop.ulLoopLength; } } } @@ -731,7 +762,8 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case IFFID_art1: case IFFID_art2: { - ART1CHUNK *p = (ART1CHUNK *)pchunk; + ART1Chunk art1; + chunk.ReadStruct(art1); if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { if (pDlsIns->nRegions >= DLSMAXREGIONS) break; @@ -739,25 +771,28 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK { pDlsIns->nMelodicEnv = static_cast(m_Envelopes.size() + 1); } - if (p->cbSize+p->cConnectionBlocks*sizeof(CONNECTIONBLOCK) > p->len) break; + if(art1.cbSize + art1.cConnectionBlocks * sizeof(CONNECTIONBLOCK) > header.len) + break; DLSENVELOPE dlsEnv; MemsetZero(dlsEnv); dlsEnv.nDefPan = 128; dlsEnv.nVolSustainLevel = 128; //Log(" art1 (%3d bytes): cbSize=%d cConnectionBlocks=%d\n", p->len, p->cbSize, p->cConnectionBlocks); - CONNECTIONBLOCK *pblk = (CONNECTIONBLOCK *)( ((uint8 *)p)+8+p->cbSize ); - for (uint32 iblk=0; iblkcConnectionBlocks; iblk++, pblk++) + chunk.Seek(sizeof(IFFCHUNK) + art1.cbSize); + for (uint32 iblk = 0; iblk < art1.cConnectionBlocks; iblk++) { + CONNECTIONBLOCK blk; + chunk.ReadStruct(blk); // [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID - uint32 dwArticulation = pblk->usTransform; - dwArticulation = (dwArticulation << 12) | (pblk->usDestination & 0x0FFF); - dwArticulation = (dwArticulation << 8) | (pblk->usControl & 0x00FF); - dwArticulation = (dwArticulation << 8) | (pblk->usSource & 0x00FF); + uint32 dwArticulation = blk.usTransform; + dwArticulation = (dwArticulation << 12) | (blk.usDestination & 0x0FFF); + dwArticulation = (dwArticulation << 8) | (blk.usControl & 0x00FF); + dwArticulation = (dwArticulation << 8) | (blk.usSource & 0x00FF); switch(dwArticulation) { case ART_DEFAULTPAN: { - int32 pan = 128 + pblk->lScale / (65536000/128); + int32 pan = 128 + blk.lScale / (65536000/128); if (pan < 0) pan = 0; if (pan > 255) pan = 255; dlsEnv.nDefPan = (uint8)pan; @@ -767,9 +802,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_ATTACKTIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolAttack = 0; - if (pblk->lScale > -0x40000000) + if(blk.lScale > -0x40000000) { - int32 l = pblk->lScale - 78743200; // maximum velocity + int32 l = blk.lScale - 78743200; // maximum velocity if (l > 0) l = 0; int32 attacktime = DLS32BitTimeCentsToMilliseconds(l); if (attacktime < 0) attacktime = 0; @@ -782,9 +817,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_DECAYTIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolDecay = 0; - if (pblk->lScale > -0x40000000) + if(blk.lScale > -0x40000000) { - int32 decaytime = DLS32BitTimeCentsToMilliseconds(pblk->lScale); + int32 decaytime = DLS32BitTimeCentsToMilliseconds(blk.lScale); if (decaytime > 20000) decaytime = 20000; if (decaytime >= 20) dlsEnv.wVolDecay = (uint16)(decaytime / 20); //Log("%3d: Envelope Decay Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), decaytime, pblk->lScale); @@ -794,9 +829,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_RELEASETIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolRelease = 0; - if (pblk->lScale > -0x40000000) + if(blk.lScale > -0x40000000) { - int32 releasetime = DLS32BitTimeCentsToMilliseconds(pblk->lScale); + int32 releasetime = DLS32BitTimeCentsToMilliseconds(blk.lScale); if (releasetime > 20000) releasetime = 20000; if (releasetime >= 20) dlsEnv.wVolRelease = (uint16)(releasetime / 20); //Log("%3d: Envelope Release Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), dlsEnv.wVolRelease, pblk->lScale); @@ -805,14 +840,14 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_SUSTAINLEVEL: // 0.1% units - if (pblk->lScale >= 0) + if(blk.lScale >= 0) { - dlsEnv.nVolSustainLevel = DLSSustainLevelToLinear(pblk->lScale); + dlsEnv.nVolSustainLevel = DLSSustainLevelToLinear(blk.lScale); } break; //default: - // Log(" Articulation = 0x%08X value=%d\n", dwArticulation, pblk->lScale); + // Log(" Articulation = 0x%08X value=%d\n", dwArticulation, blk.lScale); } } m_Envelopes.push_back(dlsEnv); @@ -820,15 +855,15 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK break; case IFFID_INAM: - mpt::String::CopyN(pDlsIns->szName, ((const char *)pchunk) + 8, pchunk->len); + chunk.ReadString(pDlsIns->szName, header.len); break; #if 0 default: { char sid[5]; - memcpy(sid, &pchunk->id, 4); + memcpy(sid, &header.id, 4); sid[4] = 0; - Log(" \"%s\": %d bytes\n", (uint32)sid, pchunk->len.get()); + Log(" \"%s\": %d bytes\n", (uint32)sid, header.len.get()); } #endif } @@ -839,13 +874,13 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK /////////////////////////////////////////////////////////////// // Converts SF2 chunks to DLS -bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &header, FileReader &chunk) +bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk) { if (!chunk.IsValid()) return false; switch(header.id) { case IFFID_phdr: - if (m_Instruments.empty()) + if(m_Instruments.empty()) { uint32 numIns = static_cast(chunk.GetLength() / sizeof(SFPRESETHEADER)); if(numIns <= 1) @@ -855,13 +890,13 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade m_Instruments.resize(numIns); #ifdef DLSBANK_LOG - Log("phdr: %d instruments\n", m_Instruments.size()); + MPT_LOG(LogDebug, "DLSBank", mpt::format(U_("phdr: %1 instruments"))(m_Instruments.size())); #endif SFPRESETHEADER psfh; chunk.ReadStruct(psfh); for (auto &dlsIns : m_Instruments) { - mpt::String::Copy(dlsIns.szName, psfh.achPresetName); + mpt::String::WriteAutoBuf(dlsIns.szName) = mpt::String::ReadAutoBuf(psfh.achPresetName); dlsIns.ulInstrument = psfh.wPreset & 0x7F; dlsIns.ulBank = (psfh.wBank >= 128) ? F_INSTRUMENT_DRUMS : (psfh.wBank << 8); dlsIns.wPresetBagNdx = psfh.wPresetBagNdx; @@ -873,65 +908,43 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade break; case IFFID_pbag: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFPRESETBAG))) { - uint32 nBags = static_cast(chunk.GetLength() / sizeof(SFPRESETBAG)); - if (nBags) - { - sf2info.nPresetBags = nBags; - sf2info.pPresetBags = reinterpret_cast(chunk.GetRawData()); - } + sf2info.presetBags = chunk.GetChunk(chunk.BytesLeft()); } #ifdef DLSINSTR_LOG - else Log("pbag: no instruments!\n"); + else MPT_LOG(LogDebug, "DLSINSTR", U_("pbag: no instruments!")); #endif break; case IFFID_pgen: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFGENLIST))) { - uint32 nGens = static_cast(chunk.GetLength() / sizeof(SFGENLIST)); - if (nGens) - { - sf2info.nPresetGens = nGens; - sf2info.pPresetGens = reinterpret_cast(chunk.GetRawData()); - } + sf2info.presetGens = chunk.GetChunk(chunk.BytesLeft()); } #ifdef DLSINSTR_LOG - else Log("pgen: no instruments!\n"); + else MPT_LOG(LogDebug, "DLSINSTR", U_("pgen: no instruments!")); #endif break; case IFFID_inst: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINST))) { - uint32 nIns = static_cast(chunk.GetLength() / sizeof(SFINST)); - sf2info.nInsts = nIns; - sf2info.pInsts = reinterpret_cast(chunk.GetRawData()); + sf2info.insts = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_ibag: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINSTBAG))) { - uint32 nBags = static_cast(chunk.GetLength() / sizeof(SFINSTBAG)); - if (nBags) - { - sf2info.nInstBags = nBags; - sf2info.pInstBags = reinterpret_cast(chunk.GetRawData()); - } + sf2info.instBags = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_igen: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINSTGENLIST))) { - uint32 nGens = static_cast(chunk.GetLength() / sizeof(SFINSTGENLIST)); - if (nGens) - { - sf2info.nInstGens = nGens; - sf2info.pInstGens = reinterpret_cast(chunk.GetRawData()); - } + sf2info.instGens = chunk.GetChunk(chunk.BytesLeft()); } break; @@ -942,16 +955,16 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade if (numSmp < 1) break; m_SamplesEx.resize(numSmp); m_WaveForms.resize(numSmp); - #ifdef DLSINSTR_LOG - Log("shdr: %d samples\n", m_SamplesEx.size()); - #endif + #ifdef DLSINSTR_LOG + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("shdr: %1 samples"))(m_SamplesEx.size())); + #endif for (uint32 i = 0; i < numSmp; i++) { SFSAMPLE p; chunk.ReadStruct(p); DLSSAMPLEEX &dlsSmp = m_SamplesEx[i]; - mpt::String::Copy(dlsSmp.szName, p.achSampleName); + mpt::String::WriteAutoBuf(dlsSmp.szName) = mpt::String::ReadAutoBuf(p.achSampleName); dlsSmp.dwLen = 0; dlsSmp.dwSampleRate = p.dwSampleRate; dlsSmp.byOriginalPitch = p.byOriginalPitch; @@ -977,7 +990,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade char sdbg[5]; memcpy(sdbg, &header.id, 4); sdbg[4] = 0; - Log("Unsupported SF2 chunk: %s (%d bytes)\n", sdbg, header.len.get()); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("Unsupported SF2 chunk: %1 (%2 bytes)"))(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), header.len.get())); } #endif } @@ -993,16 +1006,22 @@ static int16 SF2TimeToDLS(int16 amount) // Convert all instruments to the DLS format -bool CDLSBank::ConvertSF2ToDLS(SF2LOADERINFO &sf2info) +bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) { if (m_Instruments.empty() || m_SamplesEx.empty()) return false; + const uint32 numPresetBags = static_cast(sf2info.presetBags.GetLength() / sizeof(SFPRESETBAG)); + const uint32 numPresetGens = static_cast(sf2info.presetGens.GetLength() / sizeof(SFGENLIST)); + const uint32 numInsts = static_cast(sf2info.insts.GetLength() / sizeof(SFINST)); + const uint32 numInstBags = static_cast(sf2info.instBags.GetLength() / sizeof(SFINSTBAG)); + const uint32 numInstGens = static_cast(sf2info.instGens.GetLength() / sizeof(SFINSTGENLIST)); + for (auto &dlsIns : m_Instruments) { DLSENVELOPE dlsEnv; - uint32 nInstrNdx = 0; - int32 lAttenuation = 0; + std::vector instruments; + int32 instrAttenuation = 0; // Default Envelope Values dlsEnv.wVolAttack = 0; dlsEnv.wVolDecay = 0; @@ -1010,44 +1029,50 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LOADERINFO &sf2info) dlsEnv.nVolSustainLevel = 128; dlsEnv.nDefPan = 128; // Load Preset Bags + sf2info.presetBags.Seek(dlsIns.wPresetBagNdx * sizeof(SFPRESETBAG)); for (uint32 ipbagcnt=0; ipbagcnt<(uint32)dlsIns.wPresetBagNum; ipbagcnt++) { - uint32 ipbagndx = dlsIns.wPresetBagNdx + ipbagcnt; - if ((ipbagndx+1 >= sf2info.nPresetBags) || (!sf2info.pPresetBags)) break; // Load generators for each preset bag - const SFPRESETBAG *pbag = sf2info.pPresetBags + ipbagndx; - for (uint32 ipgenndx=pbag[0].wGenNdx; ipgenndx= sf2info.nPresetGens)) break; - const SFGENLIST *pgen = sf2info.pPresetGens + ipgenndx; - switch(pgen->sfGenOper) + SFGENLIST gen; + if(!sf2info.presetGens.ReadStruct(gen)) + break; + switch(gen.sfGenOper) { case SF2_GEN_ATTACKVOLENV: - dlsEnv.wVolAttack = SF2TimeToDLS(pgen->genAmount); + dlsEnv.wVolAttack = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_DECAYVOLENV: - dlsEnv.wVolDecay = SF2TimeToDLS(pgen->genAmount); + dlsEnv.wVolDecay = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_SUSTAINVOLENV: // 0.1% units - if(pgen->genAmount >= 0) + if(gen.genAmount >= 0) { - dlsEnv.nVolSustainLevel = SF2SustainLevelToLinear(pgen->genAmount); + dlsEnv.nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount); } break; case SF2_GEN_RELEASEVOLENV: - dlsEnv.wVolRelease = SF2TimeToDLS(pgen->genAmount); + dlsEnv.wVolRelease = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_INSTRUMENT: - nInstrNdx = pgen->genAmount + 1; + if(std::find(instruments.begin(), instruments.end(), gen.genAmount) == instruments.end()) + instruments.push_back(gen.genAmount); break; case SF2_GEN_ATTENUATION: - lAttenuation = - (int)(uint16)(pgen->genAmount); + instrAttenuation = -static_cast(gen.genAmount); break; #if 0 default: Log("Ins %3d: bag %3d gen %3d: ", nIns, ipbagndx, ipgenndx); - Log("genoper=%d amount=0x%04X ", pgen->sfGenOper, pgen->genAmount); + Log("genoper=%d amount=0x%04X ", gen.sfGenOper, gen.genAmount); Log((pSmp->ulBank & F_INSTRUMENT_DRUMS) ? "(drum)\n" : "\n"); #endif } @@ -1060,110 +1085,151 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LOADERINFO &sf2info) dlsIns.nMelodicEnv = static_cast(m_Envelopes.size()); } // Load Instrument Bags - if ((!nInstrNdx) || (nInstrNdx >= sf2info.nInsts) || (!sf2info.pInsts)) continue; - nInstrNdx--; - dlsIns.nRegions = sf2info.pInsts[nInstrNdx+1].wInstBagNdx - sf2info.pInsts[nInstrNdx].wInstBagNdx; - //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions); - if (dlsIns.nRegions > DLSMAXREGIONS) dlsIns.nRegions = DLSMAXREGIONS; - DLSREGION *pRgn = dlsIns.Regions; - for (uint32 nRgn = 0; nRgn < dlsIns.nRegions; nRgn++, pRgn++) + dlsIns.nRegions = 0; + for(const auto nInstrNdx : instruments) { - uint32 ibagcnt = sf2info.pInsts[nInstrNdx].wInstBagNdx + nRgn; - if ((ibagcnt >= sf2info.nInstBags) || (!sf2info.pInstBags)) break; - // Create a new envelope for drums - DLSENVELOPE *pDlsEnv = &dlsEnv; - if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS) && dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size()) + if(nInstrNdx >= numInsts) + continue; + sf2info.insts.Seek(nInstrNdx * sizeof(SFINST)); + SFINST insts[2]; + sf2info.insts.ReadArray(insts); + const auto startRegion = dlsIns.nRegions; + dlsIns.nRegions += insts[1].wInstBagNdx - insts[0].wInstBagNdx; + //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions); + if (dlsIns.nRegions > DLSMAXREGIONS) dlsIns.nRegions = DLSMAXREGIONS; + DLSREGION *pRgn = &dlsIns.Regions[startRegion]; + bool hasGlobalZone = false; + for(uint32 nRgn = startRegion; nRgn < dlsIns.nRegions; nRgn++, pRgn++) { - pDlsEnv = &m_Envelopes[dlsIns.nMelodicEnv - 1]; - } - // Region Default Values - int32 lAttn = lAttenuation; - pRgn->uUnityNote = 0xFF; // 0xFF means undefined -> use sample - pRgn->sFineTune = 0; - pRgn->nWaveLink = Util::MaxValueOfType(pRgn->nWaveLink); - // Load Generators - const SFINSTBAG *pbag = sf2info.pInstBags + ibagcnt; - for (uint32 igenndx=pbag[0].wGenNdx; igenndx= sf2info.nInstGens) || (!sf2info.pInstGens)) break; - const SFINSTGENLIST *pgen = sf2info.pInstGens + igenndx; - uint16 value = pgen->genAmount; - switch(pgen->sfGenOper) + uint32 ibagcnt = insts[0].wInstBagNdx + nRgn - startRegion; + if(ibagcnt >= numInstBags) + break; + // Create a new envelope for drums + DLSENVELOPE *pDlsEnv = &dlsEnv; + if(!(dlsIns.ulBank & F_INSTRUMENT_DRUMS) && dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size()) { - case SF2_GEN_KEYRANGE: - pRgn->uKeyMin = (uint8)(value & 0xFF); - pRgn->uKeyMax = (uint8)(value >> 8); - if (pRgn->uKeyMin > pRgn->uKeyMax) - { - std::swap(pRgn->uKeyMin, pRgn->uKeyMax); - } - //if (nIns == 9) Log(" keyrange: %d-%d\n", pRgn->uKeyMin, pRgn->uKeyMax); - break; - case SF2_GEN_UNITYNOTE: - if (value < 128) pRgn->uUnityNote = (uint8)value; - break; - case SF2_GEN_ATTACKVOLENV: - pDlsEnv->wVolAttack = SF2TimeToDLS(pgen->genAmount); - break; - case SF2_GEN_DECAYVOLENV: - pDlsEnv->wVolDecay = SF2TimeToDLS(pgen->genAmount); - break; - case SF2_GEN_SUSTAINVOLENV: - // 0.1% units - if(pgen->genAmount >= 0) - { - pDlsEnv->nVolSustainLevel = SF2SustainLevelToLinear(pgen->genAmount); - } - break; - case SF2_GEN_RELEASEVOLENV: - pDlsEnv->wVolRelease = SF2TimeToDLS(pgen->genAmount); - break; - case SF2_GEN_PAN: - { - int pan = (short int)value; - pan = (((pan + 500) * 127) / 500) + 128; - if (pan < 0) pan = 0; - if (pan > 255) pan = 255; - pDlsEnv->nDefPan = (uint8)pan; - } - break; - case SF2_GEN_ATTENUATION: - lAttn = -(int)value; - break; - case SF2_GEN_SAMPLEID: - if (value < m_SamplesEx.size()) - { - pRgn->nWaveLink = value; - pRgn->ulLoopStart = m_SamplesEx[value].dwStartloop; - pRgn->ulLoopEnd = m_SamplesEx[value].dwEndloop; - } - break; - case SF2_GEN_SAMPLEMODES: - value &= 3; - pRgn->fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP)); - if (value == 1) pRgn->fuOptions |= DLSREGION_SAMPLELOOP; else - if (value == 2) pRgn->fuOptions |= DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP; else - if (value == 3) pRgn->fuOptions |= DLSREGION_SAMPLELOOP|DLSREGION_SUSTAINLOOP; - pRgn->fuOptions |= DLSREGION_OVERRIDEWSMP; - break; - case SF2_GEN_KEYGROUP: - pRgn->fuOptions |= (uint8)(value & DLSREGION_KEYGROUPMASK); - break; - case SF2_GEN_COARSETUNE: - pRgn->sFineTune += static_cast(value) * 128; - break; - case SF2_GEN_FINETUNE: - pRgn->sFineTune += static_cast(Util::muldiv(static_cast(value), 128, 100)); - break; - //default: - // Log(" gen=%d value=%04X\n", pgen->sfGenOper, pgen->genAmount); + pDlsEnv = &m_Envelopes[dlsIns.nMelodicEnv - 1]; } + // Region Default Values + int32 regionAttn = 0; + pRgn->uKeyMin = 0; + pRgn->uKeyMax = 127; + pRgn->uUnityNote = 0xFF; // 0xFF means undefined -> use sample root note + pRgn->tuning = 100; + pRgn->sFineTune = 0; + pRgn->nWaveLink = Util::MaxValueOfType(pRgn->nWaveLink); + if(hasGlobalZone) + *pRgn = dlsIns.Regions[startRegion]; + // Load Generators + sf2info.instBags.Seek(ibagcnt * sizeof(SFINSTBAG)); + SFINSTBAG bags[2]; + sf2info.instBags.ReadArray(bags); + sf2info.instGens.Seek(bags[0].wGenNdx * sizeof(SFINSTGENLIST)); + uint16 lastOp = SF2_GEN_SAMPLEID; + int32 loopStart = 0, loopEnd = 0; + for(uint32 igenndx = bags[0].wGenNdx; igenndx < bags[1].wGenNdx; igenndx++) + { + if(igenndx >= numInstGens) + break; + SFINSTGENLIST gen; + sf2info.instGens.ReadStruct(gen); + uint16 value = gen.genAmount; + lastOp = gen.sfGenOper; + + switch(gen.sfGenOper) + { + case SF2_GEN_KEYRANGE: + pRgn->uKeyMin = (uint8)(value & 0xFF); + pRgn->uKeyMax = (uint8)(value >> 8); + if(pRgn->uKeyMin > pRgn->uKeyMax) + std::swap(pRgn->uKeyMin, pRgn->uKeyMax); + break; + case SF2_GEN_UNITYNOTE: + if (value < 128) pRgn->uUnityNote = (uint8)value; + break; + case SF2_GEN_ATTACKVOLENV: + pDlsEnv->wVolAttack = SF2TimeToDLS(gen.genAmount); + break; + case SF2_GEN_DECAYVOLENV: + pDlsEnv->wVolDecay = SF2TimeToDLS(gen.genAmount); + break; + case SF2_GEN_SUSTAINVOLENV: + // 0.1% units + if(gen.genAmount >= 0) + { + pDlsEnv->nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount); + } + break; + case SF2_GEN_RELEASEVOLENV: + pDlsEnv->wVolRelease = SF2TimeToDLS(gen.genAmount); + break; + case SF2_GEN_PAN: + { + int32 pan = static_cast(value); + pan = (((pan + 500) * 127) / 500) + 128; + pDlsEnv->nDefPan = mpt::saturate_cast(pan); + } + break; + case SF2_GEN_ATTENUATION: + regionAttn = -static_cast(value); + break; + case SF2_GEN_SAMPLEID: + if (value < m_SamplesEx.size()) + { + pRgn->nWaveLink = value; + pRgn->ulLoopStart = mpt::saturate_cast(m_SamplesEx[value].dwStartloop + loopStart); + pRgn->ulLoopEnd = mpt::saturate_cast(m_SamplesEx[value].dwEndloop + loopEnd); + } + break; + case SF2_GEN_SAMPLEMODES: + value &= 3; + pRgn->fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP)); + if(value == 1) + pRgn->fuOptions |= DLSREGION_SAMPLELOOP; + else if(value == 2) + pRgn->fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_PINGPONGLOOP; + else if(value == 3) + pRgn->fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_SUSTAINLOOP; + pRgn->fuOptions |= DLSREGION_OVERRIDEWSMP; + break; + case SF2_GEN_KEYGROUP: + pRgn->fuOptions |= (value & DLSREGION_KEYGROUPMASK); + break; + case SF2_GEN_COARSETUNE: + pRgn->sFineTune += static_cast(value) * 128; + break; + case SF2_GEN_FINETUNE: + pRgn->sFineTune += static_cast(Util::muldiv(static_cast(value), 128, 100)); + break; + case SF2_GEN_SCALE_TUNING: + pRgn->tuning = mpt::saturate_cast(value); + break; + case SF2_GEN_START_LOOP_FINE: + loopStart += static_cast(value); + break; + case SF2_GEN_END_LOOP_FINE: + loopEnd += static_cast(value); + break; + case SF2_GEN_START_LOOP_COARSE: + loopStart += static_cast(value) * 32768; + break; + case SF2_GEN_END_LOOP_COARSE: + loopEnd += static_cast(value) * 32768; + break; + //default: + // Log(" gen=%d value=%04X\n", pgen->sfGenOper, pgen->genAmount); + } + } + if(lastOp != SF2_GEN_SAMPLEID && nRgn == startRegion) + { + hasGlobalZone = true; + pRgn->fuOptions |= DLSREGION_ISGLOBAL; + } + int32 linearVol = DLS32BitRelativeGainToLinear(((instrAttenuation + regionAttn) * 65536) / 10) / 256; + Limit(linearVol, 16, 256); + pRgn->usVolume = static_cast(linearVol); + //Log("\n"); } - int32 lVolume = DLS32BitRelativeGainToLinear((lAttn/10) << 16) / 256; - if (lVolume < 16) lVolume = 16; - if (lVolume > 256) lVolume = 256; - pRgn->usVolume = (uint16)lVolume; - //Log("\n"); } } return true; @@ -1177,7 +1243,7 @@ bool CDLSBank::Open(const mpt::PathString &filename) { if(filename.empty()) return false; m_szFileName = filename; - InputFile f(filename); + InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); if(!f.IsValid()) return false; return Open(GetFileReader(f)); } @@ -1185,7 +1251,6 @@ bool CDLSBank::Open(const mpt::PathString &filename) bool CDLSBank::Open(FileReader file) { - SF2LOADERINFO sf2info; uint32 nInsDef; if(!file.GetFileName().empty()) @@ -1229,7 +1294,7 @@ bool CDLSBank::Open(FileReader file) file.ReadStruct(riff); break; } - uint32 len = riff.riff_len_be; + uint32 len = mpt::bit_cast(riff.riff_len); if((len % 2u) != 0) len++; file.SkipBack(4); @@ -1241,11 +1306,11 @@ bool CDLSBank::Open(FileReader file) || !file.CanRead(riff.riff_len - 4)) { #ifdef DLSBANK_LOG - Log("Invalid DLS bank!\n"); + MPT_LOG(LogDebug, "DLSBANK", U_("Invalid DLS bank!")); #endif return false; } - MemsetZero(sf2info); + SF2LoaderInfo sf2info; m_nType = (riff.id_DLS == IFFID_sfbk) ? SOUNDBANK_TYPE_SF2 : SOUNDBANK_TYPE_DLS; m_dwWavePoolOffset = 0; m_Instruments.clear(); @@ -1270,13 +1335,13 @@ bool CDLSBank::Open(FileReader file) // DLS 1.0: Instruments Collection Header case IFFID_colh: #ifdef DLSBANK_LOG - Log("colh (%d bytes)\n", chunkHeader.len); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("colh (%1 bytes)"))(chunkHeader.len.get())); #endif if (m_Instruments.empty()) { m_Instruments.resize(chunk.ReadUint32LE()); #ifdef DLSBANK_LOG - Log(" %d instruments\n", m_Instruments.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_(" %1 instruments"))(m_Instruments.size())); #endif } break; @@ -1284,7 +1349,7 @@ bool CDLSBank::Open(FileReader file) // DLS 1.0: Instruments Pointers Table case IFFID_ptbl: #ifdef DLSBANK_LOG - Log("ptbl (%d bytes)\n", chunkHeader.len); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("ptbl (%1 bytes)"))(chunkHeader.len.get())); #endif if (m_WaveForms.empty()) { @@ -1298,7 +1363,7 @@ bool CDLSBank::Open(FileReader file) m_WaveForms.push_back(chunk.ReadUint32LE()); } #ifdef DLSBANK_LOG - Log(" %d waveforms\n", m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_(" %1 waveforms"))(m_WaveForms.size())); #endif } break; @@ -1306,7 +1371,7 @@ bool CDLSBank::Open(FileReader file) // DLS 1.0: LIST section case IFFID_LIST: #ifdef DLSBANK_LOG - Log("LIST\n"); + MPT_LOG(LogDebug, "DLSBANK", U_("LIST")); #endif { uint32 listid = chunk.ReadUint32LE(); @@ -1315,7 +1380,7 @@ bool CDLSBank::Open(FileReader file) { m_dwWavePoolOffset = dwMemPos + 4; #ifdef DLSBANK_LOG - Log("Wave Pool offset: %d\n", m_dwWavePoolOffset); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Wave Pool offset: %1"))(m_dwWavePoolOffset)); #endif break; } @@ -1323,12 +1388,12 @@ bool CDLSBank::Open(FileReader file) while (chunk.CanRead(12)) { IFFCHUNK listHeader; - const void *subData = chunk.GetRawData(); chunk.ReadStruct(listHeader); if(!chunk.CanRead(listHeader.len)) break; + FileReader subData = chunk.GetChunkAt(chunk.GetPosition() - sizeof(IFFCHUNK), listHeader.len + 8); FileReader listChunk = chunk.ReadChunk(listHeader.len); if(listHeader.len % 2u) chunk.Skip(1); @@ -1340,7 +1405,7 @@ bool CDLSBank::Open(FileReader file) { DLSINSTRUMENT *pDlsIns = &m_Instruments[nInsDef]; //Log("Instrument %d:\n", nInsDef); - UpdateInstrumentDefinition(pDlsIns, static_cast(subData), listHeader.len + 8); + UpdateInstrumentDefinition(pDlsIns, subData); nInsDef++; } } else @@ -1383,7 +1448,7 @@ bool CDLSBank::Open(FileReader file) char sdbg[5]; memcpy(sdbg, &chunkHeader.id, 4); sdbg[4] = 0; - Log("Unsupported chunk: %s (%d bytes)\n", sdbg, chunkHeader.len); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Unsupported chunk: %1 (%2 bytes)"))(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), chunkHeader.len.get())); } break; #endif @@ -1393,7 +1458,7 @@ bool CDLSBank::Open(FileReader file) if ((m_WaveForms.empty()) && (m_dwWavePoolOffset) && (m_nType & SOUNDBANK_TYPE_DLS) && (m_nMaxWaveLink > 0)) { #ifdef DLSBANK_LOG - Log("ptbl not present: building table (%d wavelinks)...\n", m_nMaxWaveLink); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("ptbl not present: building table (%1 wavelinks)..."))(m_nMaxWaveLink)); #endif m_WaveForms.reserve(m_nMaxWaveLink); file.Seek(m_dwWavePoolOffset); @@ -1406,7 +1471,7 @@ bool CDLSBank::Open(FileReader file) file.Skip(chunk.len); } #ifdef DLSBANK_LOG - Log("Found %d waveforms\n", m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Found %1 waveforms"))(m_WaveForms.size())); #endif } // Convert the SF2 data to DLS @@ -1415,7 +1480,7 @@ bool CDLSBank::Open(FileReader file) ConvertSF2ToDLS(sf2info); } #ifdef DLSBANK_LOG - Log("DLS bank closed\n"); + MPT_LOG(LogDebug, "DLSBANK", U_("DLS bank closed")); #endif return true; } @@ -1446,7 +1511,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav if (nIns >= m_Instruments.size() || !m_dwWavePoolOffset) { #ifdef DLSBANK_LOG - Log("ExtractWaveForm(%d) failed: m_Instruments.size()=%d m_dwWavePoolOffset=%d m_WaveForms.size()=%d\n", nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("ExtractWaveForm(%1) failed: m_Instruments.size()=%2 m_dwWavePoolOffset=%3 m_WaveForms.size()=%4"))(nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size())); #endif return false; } @@ -1454,7 +1519,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav if (nRgn >= dlsIns.nRegions) { #ifdef DLSBANK_LOG - Log("invalid waveform region: nIns=%d nRgn=%d pSmp->nRegions=%d\n", nIns, nRgn, pSmp->nRegions); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("invalid waveform region: nIns=%1 nRgn=%2 pSmp->nRegions=%3"))(nIns, nRgn, dlsIns.nRegions)); #endif return false; } @@ -1462,7 +1527,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav if(nWaveLink >= m_WaveForms.size()) { #ifdef DLSBANK_LOG - Log("Invalid wavelink id: nWaveLink=%d nWaveForms=%d\n", nWaveLink, m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Invalid wavelink id: nWaveLink=%1 nWaveForms=%2"))(nWaveLink, m_WaveForms.size())); #endif return false; } @@ -1494,17 +1559,17 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav } } else { - LISTCHUNK chunk; - if (mpt::IO::Read(f, chunk)) + LISTChunk chunk; + if(mpt::IO::Read(f, chunk)) { - if ((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4)) + if((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4)) { length = chunk.len + 8; try { - waveData.assign(chunk.len + 8, 0); - memcpy(waveData.data(), &chunk, 12); - mpt::IO::ReadRaw(f, &waveData[12], length - 12); + waveData.assign(chunk.len + sizeof(IFFCHUNK), 0); + memcpy(waveData.data(), &chunk, sizeof(chunk)); + mpt::IO::ReadRaw(f, &waveData[sizeof(chunk)], length - sizeof(chunk)); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); @@ -1521,14 +1586,14 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI { std::vector pWaveForm; uint32 dwLen = 0; - bool bOk, bWaveForm; + bool ok, hasWaveform; if (nIns >= m_Instruments.size()) return false; 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; - bOk = false; + ok = false; FileReader wsmpChunk; if (m_nType & SOUNDBANK_TYPE_SF2) @@ -1541,7 +1606,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI { const DLSSAMPLEEX &p = m_SamplesEx[nWaveLink]; #ifdef DLSINSTR_LOG - Log(" SF2 WaveLink #%3d: %5dHz\n", nWaveLink, p->dwSampleRate); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" SF2 WaveLink #%1: %2Hz"))(nWaveLink, p.dwSampleRate)); #endif sample.Initialize(); sample.nLength = dwLen / 2; @@ -1551,9 +1616,9 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI sample.RelativeTone = p.byOriginalPitch; sample.nFineTune = p.chPitchCorrection; if (p.szName[0]) - mpt::String::Copy(sndFile.m_szNames[nSample], p.szName); + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(p.szName); else if(pDlsIns->szName[0]) - mpt::String::Copy(sndFile.m_szNames[nSample], pDlsIns->szName); + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(pDlsIns->szName); FileReader chunk(mpt::as_span(pWaveForm.data(), dwLen)); SampleIO( @@ -1563,15 +1628,15 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI SampleIO::signedPCM) .ReadSample(sample, chunk); } - bWaveForm = sample.HasSampleData(); + hasWaveform = sample.HasSampleData(); } else { FileReader file(mpt::as_span(pWaveForm.data(), dwLen)); - bWaveForm = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk); + hasWaveform = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk); if(pDlsIns->szName[0]) - mpt::String::Copy(sndFile.m_szNames[nSample], pDlsIns->szName); + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(pDlsIns->szName); } - if (bWaveForm) + if (hasWaveform) { ModSample &sample = sndFile.GetSample(nSample); const DLSREGION &rgn = pDlsIns->Regions[nRgn]; @@ -1604,7 +1669,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI int lVolume = rgn.usVolume; WSMPCHUNK wsmp; - if(!(rgn.fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.ReadStructPartial(wsmp)) + if(!(rgn.fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.Skip(sizeof(IFFCHUNK)) && wsmpChunk.ReadStructPartial(wsmp)) { usUnityNote = wsmp.usUnityNote; sFineTune = wsmp.sFineTune; @@ -1612,7 +1677,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI if(wsmp.cSampleLoops) { WSMPSAMPLELOOP loop; - wsmpChunk.Skip(8 + wsmp.cbSize); + wsmpChunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize); wsmpChunk.ReadStruct(loop); if(loop.ulLoopLength > 3) { @@ -1628,7 +1693,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI sFineTune += sample.nFineTune; } #ifdef DLSINSTR_LOG - Log("WSMP: usUnityNote=%d.%d, %dHz (transp=%d)\n", usUnityNote, sFineTune, sample.nC5Speed, transpose); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("WSMP: usUnityNote=%1.%2, %3Hz (transp=%4)"))(usUnityNote, sFineTune, sample.nC5Speed, transpose)); #endif if (usUnityNote > 0x7F) usUnityNote = 60; int steps = (60 + transpose - usUnityNote) * 128 + sFineTune; @@ -1641,15 +1706,15 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI sample.Convert(MOD_TYPE_IT, sndFile.GetType()); sample.PrecomputeLoops(sndFile, false); - bOk = true; + ok = true; } - return bOk; + return ok; } static uint16 ScaleEnvelope(uint32 time, float tempoScale) { - return std::max(mpt::saturate_round(time * tempoScale), 1); + return std::max(mpt::saturate_round(time * tempoScale), uint16(1)); } @@ -1673,18 +1738,20 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui nRgnMax = pDlsIns->nRegions; nEnv = pDlsIns->nMelodicEnv; } + if(nRgnMin == 0 && (pDlsIns->Regions[0].fuOptions & DLSREGION_ISGLOBAL)) + nRgnMin++; #ifdef DLSINSTR_LOG - Log("DLS Instrument #%d: %s\n", nIns, pDlsIns->szName); - Log(" Bank=0x%04X Instrument=0x%04X\n", pDlsIns->ulBank, pDlsIns->ulInstrument); - Log(" %2d regions, nMelodicEnv=%d\n", pDlsIns->nRegions, pDlsIns->nMelodicEnv); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("DLS Instrument #%1: %2"))(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(pDlsIns->szName)))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Bank=0x%1 Instrument=0x%2"))(mpt::ufmt::HEX0<4>(pDlsIns->ulBank), mpt::ufmt::HEX0<4>(pDlsIns->ulInstrument))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(pDlsIns->nRegions, pDlsIns->nMelodicEnv)); for (uint32 iDbg=0; iDbgnRegions; 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); - Log(" fuOptions = 0x%04X\n", prgn->fuOptions); - Log(" usVolume = %3d, Unity Note = %d\n", prgn->usVolume, prgn->uUnityNote); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Region %1:"))(iDbg)); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" WaveLink = %1 (loop [%2, %3])"))(prgn->nWaveLink, prgn->ulLoopStart, prgn->ulLoopEnd)); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Key Range: [%1, %2]"))(prgn->uKeyMin, prgn->uKeyMax)); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" fuOptions = 0x%1"))(mpt::ufmt::HEX0<4>(prgn->fuOptions))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" usVolume = %1, Unity Note = %2"))(prgn->usVolume, prgn->uUnityNote)); } #endif @@ -1701,38 +1768,40 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui // Initializes Instrument if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { - char s[64] = ""; uint32 key = pDlsIns->Regions[nDrumRgn].uKeyMin; - if ((key >= 24) && (key <= 84)) lstrcpyA(s, szMidiPercussionNames[key-24]); - if (pDlsIns->szName[0]) + if((key >= 24) && (key <= 84)) { - sprintf(&s[strlen(s)], " (%s", pDlsIns->szName); - size_t n = strlen(s); - while ((n) && (s[n-1] == ' ')) + std::string s = szMidiPercussionNames[key-24]; + if(!mpt::String::ReadAutoBuf(pDlsIns->szName).empty()) { - n--; - s[n] = 0; + s += mpt::format(" (%1)")(mpt::String::RTrim(mpt::String::ReadAutoBuf(pDlsIns->szName))); } - lstrcatA(s, ")"); + pIns->name = s; + } else + { + pIns->name = mpt::String::ReadAutoBuf(pDlsIns->szName); } - mpt::String::Copy(pIns->name, s); } else { - mpt::String::Copy(pIns->name, pDlsIns->szName); + pIns->name = mpt::String::ReadAutoBuf(pDlsIns->szName); } int nTranspose = 0; - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + if(pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { - for (uint32 iNoteMap=0; iNoteMapRegions[nDrumRgn].uKeyMin) pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMin + 1); - if (iNoteMap > pDlsIns->Regions[nDrumRgn].uKeyMax) pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMax + 1); + // Format has instrument note mapping + if(pDlsIns->Regions[nDrumRgn].tuning == 0) + pIns->NoteMap[iNoteMap] = NOTE_MIDDLEC; + else if(iNoteMap < pDlsIns->Regions[nDrumRgn].uKeyMin) + pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMin + NOTE_MIN); + else if(iNoteMap > pDlsIns->Regions[nDrumRgn].uKeyMax) + pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMax + NOTE_MIN); } else { - if (iNoteMap == pDlsIns->Regions[nDrumRgn].uKeyMin) + if(iNoteMap == pDlsIns->Regions[nDrumRgn].uKeyMin) { nTranspose = (pDlsIns->Regions[nDrumRgn].uKeyMin + (pDlsIns->Regions[nDrumRgn].uKeyMax - pDlsIns->Regions[nDrumRgn].uKeyMin) / 2) - 60; } @@ -1752,7 +1821,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui // Extract Samples for (uint32 nRgn=nRgnMin; nRgnRegions[nRgn]; // Elimitate Duplicate Regions @@ -1766,13 +1835,13 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui || ((pRgn2->uKeyMin == pRgn->uKeyMin) && (pRgn2->uKeyMax == pRgn->uKeyMax))) { - bDupRgn = true; + duplicateRegion = true; nSmp = RgnToSmp[iDup]; break; } } // Create a new sample - if (!bDupRgn) + if (!duplicateRegion) { uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : 32; if (nLoadedSmp >= nmaxsmp) @@ -1800,7 +1869,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui } } // Load the sample - if(!bDupRgn || !sndFile.GetSample(nSmp).HasSampleData()) + if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData()) { ExtractSample(sndFile, nSmp, nIns, nRgn, nTranspose); } else if(sndFile.GetSample(nSmp).GetNumChannels() == 1) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h index 8b85ef8fc..bf49ad564 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h @@ -25,14 +25,6 @@ OPENMPT_NAMESPACE_BEGIN #define DLSMAXREGIONS 128 -// Region Flags -#define DLSREGION_KEYGROUPMASK 0x0F -#define DLSREGION_OVERRIDEWSMP 0x10 -#define DLSREGION_PINGPONGLOOP 0x20 -#define DLSREGION_SAMPLELOOP 0x40 -#define DLSREGION_SELFNONEXCLUSIVE 0x80 -#define DLSREGION_SUSTAINLOOP 0x100 - struct DLSREGION { uint32 ulLoopStart; @@ -45,6 +37,7 @@ struct DLSREGION uint8 uKeyMin; uint8 uKeyMax; uint8 uUnityNote; + uint8 tuning = 100; }; struct DLSENVELOPE @@ -97,7 +90,7 @@ struct SOUNDBANKINFO }; struct IFFCHUNK; -struct SF2LOADERINFO; +struct SF2LoaderInfo; class CDLSBank { @@ -141,9 +134,9 @@ public: // Internal Loader Functions protected: - bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK *pchunk, uint32 dwMaxLen); - bool UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &header, FileReader &chunk); - bool ConvertSF2ToDLS(SF2LOADERINFO &sf2info); + bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk); + bool UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk); + bool ConvertSF2ToDLS(SF2LoaderInfo &sf2info); public: // DLS Unit conversion diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp index 1266d64a7..4674339a5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp @@ -20,8 +20,8 @@ #include "Sndfile.h" #include "MixerLoops.h" #include "MixFuncTable.h" -#include // For FLT_EPSILON #include "plugins/PlugInterface.h" +#include // For FLT_EPSILON #include @@ -32,13 +32,16 @@ OPENMPT_NAMESPACE_BEGIN struct MixLoopState { - const int8 * samplePointer; - const int8 * lookaheadPointer; - SmpLength lookaheadStart; - uint32 maxSamples; + const int8 * samplePointer = nullptr; + const int8 * lookaheadPointer = nullptr; + SmpLength lookaheadStart = 0; + uint32 maxSamples = 0; MixLoopState(const ModChannel &chn) { + if(chn.pCurrentSample == nullptr) + return; + UpdateLookaheadPointers(chn); // For platforms that have no fast 64-bit division, precompute this constant @@ -47,7 +50,8 @@ struct MixLoopState if(increment.IsNegative()) increment.Negate(); maxSamples = 16384u / (increment.GetUInt() + 1u); - if(maxSamples < 2) maxSamples = 2; + if(maxSamples < 2) + maxSamples = 2; } // Calculate offset of loop wrap-around buffer for this sample. @@ -147,7 +151,7 @@ struct MixLoopState if ((chn.position.GetUInt() <= chn.nLoopStart) || (chn.position.GetUInt() >= chn.nLength)) { // Impulse Tracker's software mixer would put a -2 (instead of -1) in the following line (doesn't happen on a GUS) - chn.position.SetInt(chn.nLength - std::min(chn.nLength, ITPingPongMode ? 2 : 1)); + chn.position.SetInt(chn.nLength - std::min(chn.nLength, ITPingPongMode ? SmpLength(2) : SmpLength(1))); } } else { @@ -276,11 +280,13 @@ void CSoundFile::CreateStereoMix(int count) { mixsample_t *pOfsL, *pOfsR; - if (!count) return; + if(!count) + return; // Resetting sound buffer - StereoFill(MixSoundBuffer, count, gnDryROfsVol, gnDryLOfsVol); - if(m_MixerSettings.gnChannels > 2) InitMixBuffer(MixRearBuffer, count*2); + StereoFill(MixSoundBuffer, count, m_dryROfsVol, m_dryLOfsVol); + if(m_MixerSettings.gnChannels > 2) + StereoFill(MixRearBuffer, count, m_surroundROfsVol, m_surroundLOfsVol); CHANNELINDEX nchmixed = 0; @@ -290,9 +296,11 @@ void CSoundFile::CreateStereoMix(int count) { ModChannel &chn = m_PlayState.Chn[m_PlayState.ChnMix[nChn]]; - if(!chn.pCurrentSample) continue; - pOfsR = &gnDryROfsVol; - pOfsL = &gnDryLOfsVol; + if(!chn.pCurrentSample && !chn.nLOfs && !chn.nROfs) + continue; + + pOfsR = &m_dryROfsVol; + pOfsL = &m_dryLOfsVol; uint32 functionNdx = MixFuncTable::ResamplingModeToMixFlags(static_cast(chn.resamplingMode)); if(chn.dwFlags[CHN_16BIT]) functionNdx |= MixFuncTable::ndx16Bit; @@ -311,7 +319,11 @@ void CSoundFile::CreateStereoMix(int count) } #endif if(chn.dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels > 2) + { pbuffer = MixRearBuffer; + pOfsR = &m_surroundROfsVol; + pOfsL = &m_surroundLOfsVol; + } //Look for plugins associated with this implicit tracker channel. #ifndef NO_PLUGINS @@ -379,7 +391,7 @@ void CSoundFile::CreateStereoMix(int count) { // Detecting the longest play time for each sample for optimization chn.position += chn.increment * nSmpCount; - size_t smp = std::distance(Samples, chn.pModSample); + size_t smp = std::distance(static_cast(static_cast::type>(Samples)), chn.pModSample); if(smp < m_SamplePlayLengths->size()) { m_SamplePlayLengths->at(smp) = std::max(m_SamplePlayLengths->at(smp), chn.position.GetUInt()); @@ -428,31 +440,31 @@ void CSoundFile::CreateStereoMix(int count) } } - if(chn.position.GetUInt() >= chn.nLoopEnd && chn.dwFlags[CHN_LOOP]) + const bool pastLoopEnd = chn.position.GetUInt() >= chn.nLoopEnd && chn.dwFlags[CHN_LOOP]; + const bool pastSampleEnd = chn.position.GetUInt() >= chn.nLength && !chn.dwFlags[CHN_LOOP] && chn.nLength && !chn.nMasterChn; + const bool doSampleSwap = m_playBehaviour[kMODSampleSwap] && chn.nNewIns && chn.nNewIns <= GetNumSamples() && chn.pModSample != &Samples[chn.nNewIns]; + if((pastLoopEnd || pastSampleEnd) && doSampleSwap) { - if(m_playBehaviour[kMODSampleSwap] && chn.nNewIns && chn.nNewIns <= GetNumSamples() && chn.pModSample != &Samples[chn.nNewIns]) + // ProTracker compatibility: Instrument changes without a note do not happen instantly, but rather when the sample loop has finished playing. + // Test case: PTInstrSwap.mod, PTSwapNoLoop.mod + const ModSample &smp = Samples[chn.nNewIns]; + chn.pModSample = &smp; + chn.pCurrentSample = smp.samplev(); + chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | smp.uFlags; + chn.nLength = smp.uFlags[CHN_LOOP] ? smp.nLoopEnd : 0; // non-looping sample continue in oneshot mode (i.e. they will most probably just play silence) + chn.nLoopStart = smp.nLoopStart; + chn.nLoopEnd = smp.nLoopEnd; + chn.position.SetInt(chn.nLoopStart); + mixLoopState.UpdateLookaheadPointers(chn); + if(!chn.pCurrentSample) { - // ProTracker compatibility: Instrument changes without a note do not happen instantly, but rather when the sample loop has finished playing. - // Test case: PTInstrSwap.mod - const ModSample &smp = Samples[chn.nNewIns]; - chn.pModSample = &smp; - chn.pCurrentSample = smp.samplev(); - chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | smp.uFlags; - chn.nLength = smp.uFlags[CHN_LOOP] ? smp.nLoopEnd : smp.nLength; - chn.nLoopStart = smp.nLoopStart; - chn.nLoopEnd = smp.nLoopEnd; - chn.position.SetInt(chn.nLoopStart); - mixLoopState.UpdateLookaheadPointers(chn); - if(!chn.pCurrentSample) - { - break; - } - } else if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) - { - // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) - chn.position.SetInt(0); - chn.nLoopEnd = chn.nLength = chn.pModSample->nLoopEnd; + break; } + } else if(pastLoopEnd && !doSampleSwap && m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) + { + // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) + chn.position.SetInt(0); + chn.nLoopEnd = chn.nLength = chn.pModSample->nLoopEnd; } } while(nsamples > 0); @@ -467,7 +479,7 @@ void CSoundFile::CreateStereoMix(int count) } #endif // NO_PLUGINS } - m_nMixStat = std::max(m_nMixStat, nchmixed); + m_nMixStat = std::max(m_nMixStat, nchmixed); } @@ -511,7 +523,7 @@ void CSoundFile::ProcessPlugins(uint32 nCount) #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else - DeinterleaveStereo(pState->pMixBuffer, plugInputL, plugInputR, nCount); + DeinterleaveStereo(state.pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else if (state.nVolDecayR || state.nVolDecayL) { @@ -519,7 +531,7 @@ void CSoundFile::ProcessPlugins(uint32 nCount) #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else - DeinterleaveStereo(pState->pMixBuffer, plugInputL, plugInputR, nCount); + DeinterleaveStereo(state.pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp index ddf509b0a..324a4dcec 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp @@ -23,64 +23,53 @@ OPENMPT_NAMESPACE_BEGIN // Algorithm parameters for 16-Bit samples struct IT16BitParams { - typedef int16 sample_t; - static const int16 lowerTab[]; - static const int16 upperTab[]; - static const int8 fetchA; - static const int8 lowerB; - static const int8 upperB; - static const int8 defWidth; - static const int mask; + using sample_t = int16; + static constexpr int16 lowerTab[] = {0, -1, -3, -7, -15, -31, -56, -120, -248, -504, -1016, -2040, -4088, -8184, -16376, -32760, -32768}; + static constexpr int16 upperTab[] = {0, 1, 3, 7, 15, 31, 55, 119, 247, 503, 1015, 2039, 4087, 8183, 16375, 32759, 32767}; + static constexpr int8 fetchA = 4; + static constexpr int8 lowerB = -8; + static constexpr int8 upperB = 7; + static constexpr int8 defWidth = 17; + static constexpr int mask = 0xFFFF; }; -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 { - typedef int8 sample_t; - static const int8 lowerTab[]; - static const int8 upperTab[]; - static const int8 fetchA; - static const int8 lowerB; - static const int8 upperB; - static const int8 defWidth; - static const int mask; + using sample_t = int8; + static constexpr int8 lowerTab[] = {0, -1, -3, -7, -15, -31, -60, -124, -128}; + static constexpr int8 upperTab[] = {0, 1, 3, 7, 15, 31, 59, 123, 127}; + static constexpr int8 fetchA = 3; + static constexpr int8 lowerB = -4; + static constexpr int8 upperB = 3; + static constexpr int8 defWidth = 9; + static constexpr int mask = 0xFF; }; -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 }; +static constexpr int8 ITWidthChangeSize[] = { 4, 5, 6, 7, 8, 9, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; ////////////////////////////////////////////////////////////////////////////// // IT 2.14 compression ITCompression::ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength) - : file(f) - , mptSample(sample) - , is215(it215) + : file(f) + , mptSample(sample) + , is215(it215) { - packedData = new (std::nothrow) uint8[bufferSize]; - sampleData = new (std::nothrow) uint8[blockSize]; - packedTotalLength = 0; - if(packedData == nullptr || sampleData == nullptr) - { - return; - } + if(mptSample.GetElementarySampleSize() > 1) + Compress(mptSample.sample16(), maxLength); + else + Compress(mptSample.sample8(), maxLength); +} + +template +void ITCompression::Compress(const typename Properties::sample_t *mptSampleData, SmpLength maxLength) +{ + packedData.resize(bufferSize); + std::vector sampleData; + sampleData.resize(blockSize / sizeof(typename Properties::sample_t)); if(maxLength == 0 || maxLength > mptSample.nLength) maxLength = mptSample.nLength; for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++) @@ -95,29 +84,25 @@ ITCompression::ITCompression(const ModSample &sample, bool it215, std::ostream * remBits = 8; byteVal = 0; - if(mptSample.GetElementarySampleSize() > 1) - Compress(sample.sample16() + chn, offset, remain); - else - Compress(sample.sample8() + chn, offset, remain); + CompressBlock(mptSampleData + chn, offset, remain, sampleData.data()); - if(file) mpt::IO::WriteRaw(*file, packedData, packedLength); + if(file) mpt::IO::WriteRaw(*file, packedData.data(), packedLength); packedTotalLength += packedLength; offset += baseLength; remain -= baseLength; } } - - delete[] packedData; - delete[] reinterpret_cast(sampleData); + packedData.resize(0); + packedData.shrink_to_fit(); } template -void ITCompression::CopySample(void *target, const void *source, SmpLength offset, SmpLength length, SmpLength skip) +void ITCompression::CopySample(T *target, const T *source, SmpLength offset, SmpLength length, SmpLength skip) { - T *out = static_cast(target); - const T *in = static_cast(source) + offset * skip; + T *out = target; + const T *in = source + offset * skip; for(SmpLength i = 0, j = 0; j < length; i += skip, j++) { out[j] = in[i]; @@ -127,9 +112,9 @@ void ITCompression::CopySample(void *target, const void *source, SmpLength offse // Convert sample to delta values. template -void ITCompression::Deltafy() +void ITCompression::Deltafy(T *sampleData) { - T *p = static_cast(sampleData); + T *p = sampleData; int oldVal = 0; for(SmpLength i = 0; i < baseLength; i++) { @@ -141,26 +126,26 @@ void ITCompression::Deltafy() template -void ITCompression::Compress(const void *data, SmpLength offset, SmpLength actualLength) +void ITCompression::CompressBlock(const typename Properties::sample_t *data, SmpLength offset, SmpLength actualLength, typename Properties::sample_t *sampleData) { baseLength = std::min(actualLength, SmpLength(blockSize / sizeof(typename Properties::sample_t))); CopySample(sampleData, data, offset, baseLength, mptSample.GetNumChannels()); - Deltafy(); + Deltafy(sampleData); if(is215) { - Deltafy(); + Deltafy(sampleData); } // Initialise bit width table with initial values bwt.assign(baseLength, Properties::defWidth); // Recurse! - SquishRecurse(Properties::defWidth, Properties::defWidth, Properties::defWidth, Properties::defWidth - 2, 0, baseLength); + SquishRecurse(Properties::defWidth, Properties::defWidth, Properties::defWidth, Properties::defWidth - 2, 0, baseLength, sampleData); // Write those bits! - const typename Properties::sample_t *p = static_cast(sampleData); + const typename Properties::sample_t *p = sampleData; int8 width = Properties::defWidth; for(size_t i = 0; i < baseLength; i++) { @@ -207,7 +192,7 @@ int8 ITCompression::GetWidthChangeSize(int8 w, bool is16) template -void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length) +void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length, const typename Properties::sample_t *sampleData) { if(width + 1 < 1) { @@ -220,7 +205,7 @@ void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 wi SmpLength i = offset; SmpLength end = offset + length; - const typename Properties::sample_t *p = static_cast(sampleData); + const typename Properties::sample_t *p = sampleData; while(i < end) { @@ -264,7 +249,7 @@ void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 wi comparison = (keepDown <= levelLeft); } - SquishRecurse(comparison ? (width + 1) : sWidth, xlwidth, xrwidth, width - 1, start, blockLength); + SquishRecurse(comparison ? (width + 1) : sWidth, xlwidth, xrwidth, width - 1, start, blockLength, sampleData); } else { bwt[i] = sWidth; @@ -325,8 +310,8 @@ void ITCompression::WriteByte(uint8 v) ITDecompression::ITDecompression(FileReader &file, ModSample &sample, bool it215) - : mptSample(sample) - , is215(it215) + : mptSample(sample) + , is215(it215) { for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h index ca0f9b4f2..d9f60b78c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h @@ -28,39 +28,43 @@ public: ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength = 0); size_t GetCompressedSize() const { return packedTotalLength; } - 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 + static constexpr size_t bufferSize = 2 + 0xFFFF; // Our output buffer can't be longer than this. + static constexpr size_t blockSize = 0x8000; // Block size (in bytes) in which samples are being processed protected: - std::vector bwt; // Bit width table for each sampling point - uint8 *packedData; // Compressed data for current sample block - std::ostream *file; // File to which compressed data will be written (can be nullptr if you only want to find out the sample size) - void *sampleData; // Pre-processed sample data for currently compressed sample block - const ModSample &mptSample; // Sample that is being processed - size_t packedLength; // Size of currently compressed sample block - size_t packedTotalLength; // Size of all compressed data so far - SmpLength baseLength; // Length of the currently compressed sample block (in samples) + std::vector bwt; // Bit width table for each sampling point + std::vector packedData; // Compressed data for current sample block + std::ostream *file = nullptr; // File to which compressed data will be written (can be nullptr if you only want to find out the sample size) + std::vector sampleData8; // Pre-processed sample data for currently compressed sample block + std::vector sampleData16; // Pre-processed sample data for currently compressed sample block + const ModSample &mptSample; // Sample that is being processed + size_t packedLength = 0; // Size of currently compressed sample block + size_t packedTotalLength = 0; // Size of all compressed data so far + SmpLength baseLength = 0; // Length of the currently compressed sample block (in samples) // Bit writer - int8 bitPos; // Current bit position in this byte - int8 remBits; // Remaining bits in this byte - uint8 byteVal; // Current byte value to be written + int8 bitPos = 0; // Current bit position in this byte + int8 remBits = 0; // Remaining bits in this byte + uint8 byteVal = 0; // Current byte value to be written - bool is215; // Use IT2.15 compression (double deltas) - - template - static void CopySample(void *target, const void *source, SmpLength offset, SmpLength length, SmpLength skip); - - template - void Deltafy(); + const bool is215; // Use IT2.15 compression (double deltas) template - void Compress(const void *data, SmpLength offset, SmpLength actualLength); + void Compress(const typename Properties::sample_t *mptSampleData, SmpLength maxLength); + + template + static void CopySample(T *target, const T *source, SmpLength offset, SmpLength length, SmpLength skip); + + template + void Deltafy(T *sampleData); + + template + void CompressBlock(const typename Properties::sample_t *data, SmpLength offset, SmpLength actualLength, typename Properties::sample_t *sampleData); static int8 GetWidthChangeSize(int8 w, bool is16); template - void SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length); + void SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length, const typename Properties::sample_t *sampleData); static int8 ConvertWidth(int8 curWidth, int8 newWidth); void WriteBits(int8 width, int v); @@ -76,13 +80,14 @@ public: protected: BitReader bitFile; - ModSample &mptSample; // Sample that is being processed + 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 - bool is215; // Use IT2.15 compression (double deltas) + SmpLength writtenSamples = 0; // Number of samples so far written on this channel + SmpLength writePos = 0; // Absolut write position in sample (for stereo samples) + SmpLength curLength = 0; // Length of currently processed block + unsigned int mem1 = 0, mem2 = 0; // Integrator memory + + const bool is215; // Use IT2.15 compression (double deltas) template void Uncompress(typename Properties::sample_t *target); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp index 13a3923cc..1ea975a69 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp @@ -73,22 +73,19 @@ void ITEnvelope::ConvertToMPT(InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 // Envelope Data // Attention: Full MPTM envelope is stored in extended instrument properties - for(uint32 ev = 0; ev < std::min(25, num); ev++) + for(uint32 ev = 0; ev < std::min(uint8(25), num); ev++) { mptEnv[ev].value = Clamp(data[ev].value + envOffset, 0, 64); mptEnv[ev].tick = data[ev].tick; - if(ev > 0 && ev < num && mptEnv[ev].tick < mptEnv[ev - 1].tick) + if(ev > 0 && mptEnv[ev].tick < mptEnv[ev - 1].tick && !(mptEnv[ev].tick & 0xFF00)) { // Fix broken envelopes... Instruments 2 and 3 in NoGap.it by Werewolf have envelope points where the high byte of envelope nodes is missing. - // NoGap.it was saved with MPT 1.07 or MPT 1.09, which *normally* doesn't do this in IT files. + // NoGap.it was saved with MPT 1.07 - 1.09, which *normally* doesn't do this in IT files. // However... It turns out that MPT 1.07 omitted the high byte of envelope nodes when saving an XI instrument file, and it looks like // Instrument 2 and 3 in NoGap.it were loaded from XI files. - mptEnv[ev].tick &= 0xFF; - mptEnv[ev].tick |= (mptEnv[ev].tick & ~0xFF); + mptEnv[ev].tick |= mptEnv[ev - 1].tick & 0xFF00; if(mptEnv[ev].tick < mptEnv[ev - 1].tick) - { mptEnv[ev].tick += 0x100; - } } } } @@ -103,8 +100,8 @@ void ITOldInstrument::ConvertToMPT(ModInstrument &mptIns) const return; } - mpt::String::Read(mptIns.name, name); - mpt::String::Read(mptIns.filename, filename); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); + mptIns.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptIns.nFadeOut = fadeout << 6; @@ -170,13 +167,13 @@ uint32 ITInstrument::ConvertToIT(const ModInstrument &mptIns, bool compatExport, memcpy(id, "IMPI", 4); trkvers = 0x5000 | static_cast(Version::Current().GetRawVersion() >> 16); - mpt::String::Write(filename, mptIns.filename); - mpt::String::Write(name, mptIns.name); + mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = mptIns.filename; + mpt::String::WriteBuf(mpt::String::nullTerminated, name) = mptIns.name; // Volume / Panning - fadeout = static_cast(std::min(mptIns.nFadeOut >> 5, 256u)); - gbv = static_cast(std::min(mptIns.nGlobalVol * 2u, 128u)); - dfp = static_cast(std::min(mptIns.nPan / 4u, 64u)); + fadeout = static_cast(std::min(mptIns.nFadeOut >> 5, uint32(256))); + gbv = static_cast(std::min(mptIns.nGlobalVol * 2u, uint32(128))); + dfp = static_cast(std::min(mptIns.nPan / 4u, uint32(64))); if(!mptIns.dwFlags[INS_SETPANNING]) dfp |= ITInstrument::ignorePanning; // Random Variation @@ -261,8 +258,8 @@ uint32 ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) cons return 0; } - mpt::String::Read(mptIns.name, name); - mpt::String::Read(mptIns.filename, filename); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); + mptIns.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptIns.nFadeOut = fadeout << 5; @@ -273,8 +270,8 @@ uint32 ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) cons mptIns.dwFlags.set(INS_SETPANNING, !(dfp & ITInstrument::ignorePanning)); // Random Variation - mptIns.nVolSwing = std::min(rv, 100); - mptIns.nPanSwing = std::min(rp, 64); + mptIns.nVolSwing = std::min(static_cast(rv), uint8(100)); + mptIns.nPanSwing = std::min(static_cast(rp), uint8(64)); // NNA Stuff mptIns.nNNA = static_cast(nna.get()); @@ -441,8 +438,8 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr // Header memcpy(id, "IMPS", 4); - mpt::String::Write(filename, mptSmp.filename); - //mpt::String::Write(name, m_szNames[nsmp]); + mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = mptSmp.filename; + //mpt::String::WriteBuf(mpt::String::nullTerminated, name) = m_szNames[nsmp]; // Volume / Panning gvl = static_cast(mptSmp.nGlobalVol); @@ -511,14 +508,13 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr } else if(mptSmp.uFlags[SMP_KEEPONDISK]) { #ifndef MPT_EXTERNAL_SAMPLES - MPT_UNREFERENCED_PARAMETER(allowExternal); -#else + allowExternal = false; +#endif // MPT_EXTERNAL_SAMPLES // Save external sample (filename at sample pointer) if(allowExternal && mptSmp.HasSampleData()) { cvt = ITSample::cvtExternalSample; } else -#endif // MPT_EXTERNAL_SAMPLES { length = loopbegin = loopend = susloopbegin = susloopend = 0; } @@ -535,7 +531,7 @@ uint32 ITSample::ConvertToMPT(ModSample &mptSmp) const } mptSmp.Initialize(MOD_TYPE_IT); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptSmp.nVolume = vol * 4; @@ -673,7 +669,7 @@ uint32 DecodeITEditTimer(uint16 cwtv, uint32 editTime) { editTime ^= 0x4954524B; // 'ITRK' editTime = (editTime >> 7) | (editTime << (32 - 7)); - editTime = -(int32)editTime; + editTime = ~editTime + 1; editTime = (editTime << 4) | (editTime >> (32 - 4)); editTime ^= 0x4A54484C; // 'JTHL' } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h index 66759f246..64751f8e2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h @@ -275,9 +275,9 @@ struct FileHistory; // IT Header extension: Save history struct ITHistoryStruct { - uint16le fatdate; // DOS / FAT date when the file was opened / created in the editor. For details, read http://msdn.microsoft.com/en-us/library/ms724247(VS.85).aspx - uint16le fattime; // DOS / FAT time when the file was opened / created in the editor. - uint32le runtime; // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer) + uint16le fatdate; // DOS / FAT date when the file was opened / created in the editor. For details, read https://docs.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime + uint16le fattime; // DOS / FAT time when the file was opened / created in the editor. + uint32le runtime; // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer) // Convert an ITHistoryStruct to OpenMPT's internal edit history representation void ConvertToMPT(FileHistory &mptHistory) const; @@ -307,9 +307,9 @@ enum IT_ReaderBitMasks 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 mm = (m + 9) % 12; + static constexpr int32 yy = y - mm / 10; + static constexpr int32 date = yy * 365 + yy / 4 - yy / 100 + yy / 400 + (mm * 306 + 5) / 10 + (d - 1); static constexpr int32 Version(const int32 trackerID = 0x1000) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp index 87bb171f9..c985ef303 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp @@ -79,7 +79,7 @@ DT.. [EXT] nDefaultTempo; DTFR [EXT] Fractional part of default tempo DNA. nDNA; EBIH [EXT] embeded instrument header tag (ITP file format) -FM.. nFilterMode; +FM.. filterMode; fn[. filename[12]; FO.. nFadeOut; GV.. nGlobalVol; @@ -180,7 +180,7 @@ bool IsNegative(const T &val) } \ if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ - type tmp = input-> name; \ + type tmp = (type)(input-> name ); \ mpt::IO::WriteIntLE(file, tmp); \ } \ /**/ @@ -221,7 +221,7 @@ bool IsNegative(const T &val) // Convenient macro to help WRITE_HEADER declaration for array members ONLY // ------------------------------------------------------------------------ #define WRITE_MPTHEADER_array_member(name,type,code,arraysize) \ - STATIC_ASSERT(sizeof(type) == sizeof(input-> name [0])); \ + static_assert(sizeof(type) == sizeof(input-> name [0])); \ MPT_ASSERT(sizeof(input->name) >= sizeof(type) * arraysize);\ fcode = code;\ fsize = sizeof( type ) * arraysize;\ @@ -251,7 +251,7 @@ bool IsNegative(const T &val) #define WRITE_MPTHEADER_envelope_member(envType,envField,type,code) \ {\ const InstrumentEnvelope &env = input->GetEnvelope(envType); \ - STATIC_ASSERT(sizeof(type) == sizeof(env[0]. envField)); \ + static_assert(sizeof(type) == sizeof(env[0]. envField)); \ fcode = code;\ fsize = mpt::saturate_cast(sizeof( type ) * env.size());\ MPT_ASSERT(size_t(fsize) == sizeof( type ) * env.size()); \ @@ -266,7 +266,7 @@ bool IsNegative(const T &val) } \ if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ - uint32 maxNodes = std::min(fsize/sizeof(type), env.size()); \ + uint32 maxNodes = std::min(static_cast(fsize/sizeof(type)), static_cast(env.size())); \ for(uint32 i = 0; i < maxNodes; ++i) \ { \ type tmp; \ @@ -287,16 +287,16 @@ bool IsNegative(const T &val) // Write (in 'file') 'input' ModInstrument with 'code' & 'size' extra field infos for each member void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, uint16 fixedsize) { -uint32 fcode; -uint16 fsize; -// If true, all extension are written to the file; otherwise only the specified extension is written. -// writeAll is true iff we are saving an instrument (or, hypothetically, the legacy ITP format) -const bool writeAll = only_this_code == Util::MaxValueOfType(only_this_code); + uint32 fcode; + uint16 fsize; + // If true, all extension are written to the file; otherwise only the specified extension is written. + // writeAll is true iff we are saving an instrument (or, hypothetically, the legacy ITP format) + const bool writeAll = only_this_code == Util::MaxValueOfType(only_this_code); -if(!writeAll) -{ - MPT_ASSERT(fixedsize > 0); -} + if(!writeAll) + { + MPT_ASSERT(fixedsize > 0); + } WRITE_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") ) WRITE_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") ) @@ -317,7 +317,7 @@ if(!writeAll) WRITE_MPTHEADER_sized_member( resampling , uint8 , MagicBE("R...") ) WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) - WRITE_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) + WRITE_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") ) WRITE_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) WRITE_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MagicBE("PTTL") ) @@ -387,7 +387,7 @@ void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX numInstruments 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); + WritePropertyIfNeeded(*this, &ModInstrument::filterMode, MagicBE("FM.."), sizeof(ModInstrument::filterMode), f, numInstruments); if(IsPropertyNeeded(Instruments, &ModInstrument::pitchToTempoLock)) { WriteInstrumentPropertyForAllInstruments(MagicBE("PTTL"), sizeof(uint16), f, numInstruments); @@ -472,7 +472,7 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si { \ type tmp; \ tmp = file.ReadTruncatedIntLE(fsize); \ - STATIC_ASSERT(sizeof(tmp) == sizeof(input-> name )); \ + static_assert(sizeof(tmp) == sizeof(input-> name )); \ input-> name = decltype(input-> name )(tmp); \ result = true; \ } \ @@ -485,10 +485,10 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si #define GET_MPTHEADER_array_member(name,type,code) \ case code: \ {\ - if( fsize <= sizeof( type ) * CountOf(input-> name) ) \ + if( fsize <= sizeof( type ) * std::size(input-> name) ) \ { \ FileReader arrayChunk = file.ReadChunk(fsize); \ - for(std::size_t i = 0; i < CountOf(input-> name); ++i) \ + for(std::size_t i = 0; i < std::size(input-> name); ++i) \ { \ input-> name [i] = arrayChunk.ReadIntLE(); \ } \ @@ -497,18 +497,20 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si } break; // -------------------------------------------------------------------------------------------- -// Convenient macro to help GET_HEADER declaration for character array members ONLY +// Convenient macro to help GET_HEADER declaration for character buffer members ONLY // -------------------------------------------------------------------------------------------- -#define GET_MPTHEADER_chararray_member(name,type,code) \ +#define GET_MPTHEADER_charbuf_member(name,type,code) \ case code: \ {\ - if( fsize <= sizeof( type ) * CountOf(input-> name) ) \ + if( fsize <= sizeof( type ) * input-> name .static_length() ) \ { \ FileReader arrayChunk = file.ReadChunk(fsize); \ - for(std::size_t i = 0; i < CountOf(input-> name); ++i) \ + std::string tmp; \ + for(std::size_t i = 0; i < fsize; ++i) \ { \ - input-> name [i] = arrayChunk.ReadChar(); \ + tmp += arrayChunk.ReadChar(); \ } \ + input-> name = tmp; \ result = true; \ } \ } break; @@ -575,13 +577,13 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, 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_charbuf_member( name , char , MagicBE("n[..") ) + GET_MPTHEADER_charbuf_member( filename , char , MagicBE("fn[.") ) GET_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) GET_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) GET_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) - GET_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) + GET_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") ) GET_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) GET_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) @@ -614,15 +616,15 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, result = true; } break; case MagicBE("VE.."): - input->VolEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); + input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); result = true; break; case MagicBE("PE.."): - input->PanEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); + input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); result = true; break; case MagicBE("PiE."): - input->PitchEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); + input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); result = true; break; } @@ -695,11 +697,6 @@ void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const return; } - if(code == MagicBE("n[..")) - mpt::String::SetNullTerminator(pIns->name); - if(code == MagicBE("fn[.")) - mpt::String::SetNullTerminator(pIns->filename); - if(code == MagicBE("dF..")) // 'dF..' field requires additional processing. ConvertReadExtendedFlags(pIns); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h index bd7becb21..90810bd11 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h @@ -51,16 +51,16 @@ struct AmigaBlepInterpolation { SamplePosition subIncrement; Paula::State *paula; + const Paula::BlepArray *WinSincIntegral; int numSteps; - bool filter; - MPT_FORCEINLINE void Start(ModChannel &chn, const CResampler &) + MPT_FORCEINLINE void Start(ModChannel &chn, const CResampler &resampler) { paula = &chn.paulaState; numSteps = paula->numSteps; - filter = chn.dwFlags[CHN_AMIGAFILTER]; + WinSincIntegral = &resampler.blepTables.GetAmigaTable(resampler.m_Settings.emulateAmiga, chn.dwFlags[CHN_AMIGAFILTER]); if(numSteps) - subIncrement = chn.increment / paula->numSteps; + subIncrement = chn.increment / numSteps; } MPT_FORCEINLINE void End(const ModChannel &) { } @@ -94,8 +94,8 @@ struct AmigaBlepInterpolation paula->remainder.RemoveInt(); } - auto out = paula->OutputSample(filter); - for(unsigned int i = 0; i < Traits::numChannelsOut; i++) + auto out = paula->OutputSample(*WinSincIntegral); + for(int i = 0; i < Traits::numChannelsOut; i++) outSample[i] = out; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp index 007d4463c..55d22a73e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp @@ -148,7 +148,7 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) 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_modFormat.charset = mpt::Charset::CP437; m_nSamples = fileHeader.samples; for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) @@ -160,13 +160,13 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) if(sampleHeader.length >= 0x4000000) return false; sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.filename); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); } // Copy first song message line into song title - mpt::String::Read(m_songName, fileHeader.songMessage, 36); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songMessage, 36); // Song Message - m_songMessage.ReadFixedLineLength(mpt::byte_cast(fileHeader.songMessage), 108, 36, 0); + m_songMessage.ReadFixedLineLength(mpt::byte_cast(fileHeader.songMessage), 108, 36, 0); // Reading Orders ReadOrderFromArray(Order(), fileHeader.orders, MPT_ARRAY_COUNT(fileHeader.orders), 0xFF, 0xFE); @@ -209,29 +209,28 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 8; chn++, m++) { - uint8 data[3]; - file.ReadArray(data); + const auto [noteInstr, instrVol, effParam] = file.ReadArray(); - uint8 note = data[0] >> 2; - uint8 instr = ((data[0] & 0x03) << 4) | (data[1] >> 4); - uint8 vol = data[1] & 0x0F; - if(data[0] < 0xFE) + uint8 note = noteInstr >> 2; + uint8 instr = ((noteInstr & 0x03) << 4) | (instrVol >> 4); + uint8 vol = instrVol & 0x0F; + if(noteInstr < 0xFE) { m->note = note + 36 + NOTE_MIN; m->instr = instr + 1; effect[chn] = 0xFF; } - if(data[0] <= 0xFE) + if(noteInstr <= 0xFE) { m->volcmd = VOLCMD_VOLUME; m->vol = ((vol * 64 + 8) / 15); } - if(data[2] != 0xFF) + if(effParam != 0xFF) { - effect[chn] = data[2]; + effect[chn] = effParam; } - if((data[2] & 0x0F) == 0 && data[2] != 0x30) + if((effParam & 0x0F) == 0 && effParam != 0x30) { // A param value of 0 resets the effect. effect[chn] = 0xFF; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp index df683325a..f986ca2b8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp @@ -49,7 +49,7 @@ struct AsylumSampleHeader { mptSmp.Initialize(); mptSmp.nFineTune = MOD2XMFineTune(finetune); - mptSmp.nVolume = std::min(defaultVolume, 64) * 4u; + mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4u; mptSmp.RelativeTone = transpose; mptSmp.nLength = length; @@ -149,7 +149,7 @@ bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("ASYLUM Music Format"); m_modFormat.type = U_("amf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; uint8 orders[256]; file.ReadArray(orders); @@ -161,7 +161,7 @@ bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) AsylumSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } file.Skip((64 - fileHeader.numSamples) * sizeof(AsylumSampleHeader)); @@ -178,16 +178,14 @@ bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) for(auto &m : Patterns[pat]) { - uint8 data[4]; - file.ReadArray(data); - - if(data[0] && data[0] + 12 + NOTE_MIN <= NOTE_MAX) + const auto [note, instr, command, param] = file.ReadArray(); + if(note && note + 12 + NOTE_MIN <= NOTE_MAX) { - m.note = data[0] + 12 + NOTE_MIN; + m.note = note + 12 + NOTE_MIN; } - m.instr = data[1]; - m.command = data[2]; - m.param = data[3]; + m.instr = instr; + m.command = command; + m.param = param; ConvertModCommand(m); #ifdef MODPLUG_TRACKER if(m.command == CMD_PANNING8) @@ -225,9 +223,7 @@ static void AMFReadPattern(CPattern &pattern, CHANNELINDEX chn, FileReader &file ModCommand::INSTR lastInstr = 0; while(fileChunk.CanRead(3)) { - const uint8 row = fileChunk.ReadUint8(); - const uint8 command = fileChunk.ReadUint8(); - const uint8 value = fileChunk.ReadUint8(); + const auto [row, command, value] = fileChunk.ReadArray(); if(row >= pattern.GetNumRows()) { break; @@ -267,7 +263,7 @@ static void AMFReadPattern(CPattern &pattern, CHANNELINDEX chn, FileReader &file } else { // Effect - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_SPEED, CMD_VOLUMESLIDE, CMD_VOLUME, CMD_PORTAMENTOUP, CMD_NONE, CMD_TONEPORTAMENTO, CMD_TREMOR, @@ -441,12 +437,12 @@ bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = mpt::format(U_("DSMI v%1"))(fileHeader.version); m_modFormat.type = U_("amf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nChannels = fileHeader.numChannels; m_nSamples = fileHeader.numSamples; - mpt::String::Read(m_songName, fileHeader.title); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.title); if(fileHeader.version < 10) { @@ -486,10 +482,10 @@ bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) // Get Tempo/Speed if(fileHeader.version >= 13) { - uint8 tempo = file.ReadUint8(); + auto [tempo, speed] = file.ReadArray(); if(tempo < 32) tempo = 125; m_nDefaultTempo.Set(tempo); - m_nDefaultSpeed = file.ReadUint8(); + m_nDefaultSpeed = speed; } else { m_nDefaultTempo.Set(125); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp index 47aa9795c..c9b31a54a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp @@ -42,7 +42,7 @@ static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patte }; // Effect translation table for extended (non-Protracker) effects - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_S3MCMDEX, // Forward / Backward CMD_PORTAMENTOUP, // Extra fine slide up @@ -157,7 +157,7 @@ static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patte } break; } - } else if(effect - 0x10 < (int)CountOf(effTrans)) + } else if(effect < 0x10 + CountOf(effTrans)) { // Extended commands m.command = effTrans[effect - 0x10]; @@ -299,7 +299,7 @@ struct AMSSampleHeader mptSmp.nLoopStart = std::min(loopStart, length); mptSmp.nLoopEnd = std::min(loopEnd, length); - mptSmp.nVolume = (std::min(127, volume) * 256 + 64) / 127; + mptSmp.nVolume = (std::min(uint8(127), volume.get()) * 256 + 64) / 127; if(panFinetune & 0xF0) { mptSmp.nPan = (panFinetune & 0xF0); @@ -409,11 +409,11 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::CP437; std::vector packSample(fileHeader.numSamps); - STATIC_ASSERT(MAX_SAMPLES > 255); + static_assert(MAX_SAMPLES > 255); for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { AMSSampleHeader sampleHeader; @@ -471,10 +471,10 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) } } - textOut = mpt::ToCharset(mpt::CharsetCP437, mpt::CharsetCP437AMS, textOut); + textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS, textOut); // Packed text doesn't include any line breaks! - m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 76, 0); + m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 76, 0); } // Read Order List @@ -556,7 +556,7 @@ struct AMS2Envelope return; } - STATIC_ASSERT(MAX_ENVPOINTS >= CountOf(data)); + static_assert(MAX_ENVPOINTS >= CountOf(data)); mptEnv.resize(std::min(numPoints, uint8(CountOf(data)))); mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; @@ -661,7 +661,7 @@ struct AMS2SampleHeader uint32 newC4speed = ModSample::TransposeToFrequency(relativeTone, MOD2XMFineTune(panFinetune & 0x0F)); mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363; - mptSmp.nVolume = (std::min(volume, 127) * 256 + 64) / 127; + mptSmp.nVolume = (std::min(volume.get(), uint8(127)) * 256 + 64) / 127; if(panFinetune & 0xF0) { mptSmp.nPan = (panFinetune & 0xF0); @@ -784,7 +784,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::CP437; uint16 headerFlags; if(fileHeader.versionLow >= 2) @@ -814,7 +814,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) packStatusMask = 0x8000, // If bit is set, sample is packed }; - STATIC_ASSERT(MAX_INSTRUMENTS > 255); + static_assert(MAX_INSTRUMENTS > 255); for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) { ModInstrument *instrument = AllocateInstrument(ins); @@ -835,7 +835,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) continue; } - STATIC_ASSERT(CountOf(instrument->Keyboard) >= CountOf(sampleAssignment)); + static_assert(mpt::array_sizeKeyboard)>::size >= std::size(sampleAssignment)); for(size_t i = 0; i < 120; i++) { instrument->Keyboard[i] = sampleAssignment[i] + GetNumSamples() + 1; @@ -908,7 +908,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) { std::string str; file.ReadString(str, composerLength); - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437AMS2, str); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, str); } // Channel names @@ -946,9 +946,9 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) textOut.push_back(c); } } - textOut = mpt::ToCharset(mpt::CharsetCP437, mpt::CharsetCP437AMS2, textOut); + textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS2, textOut); // Packed text doesn't include any line breaks! - m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 74, 0); + m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 74, 0); } // Read Order List diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp index 495812d76..9d5d70b82 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp @@ -104,7 +104,7 @@ 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] = + static constexpr uint8 fmVolume[16] = { 0x08, 0x10, 0x18, 0x20, 0x28, 0x2C, 0x30, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, @@ -160,7 +160,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("CDFM"); m_modFormat.type = U_("c67"); m_modFormat.madeWithTracker = U_("Composer 670"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nDefaultSpeed = fileHeader.speed; m_nDefaultTempo.Set(143); @@ -180,7 +180,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) { ModSample &mptSmp = Samples[smp + 1]; mptSmp.Initialize(MOD_TYPE_S3M); - mpt::String::Read(m_szNames[smp + 1], fileHeader.sampleNames[smp]); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.sampleNames[smp]); mptSmp.nLength = fileHeader.samples[smp].length; if(fileHeader.samples[smp].loopEnd <= fileHeader.samples[smp].length) { @@ -195,7 +195,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) { ModSample &mptSmp = Samples[smp + 33]; mptSmp.Initialize(MOD_TYPE_S3M); - mpt::String::Read(m_szNames[smp + 33], fileHeader.fmInstrNames[smp]); + m_szNames[smp + 33] = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.fmInstrNames[smp]); // Reorder OPL patch bytes (interleave modulator and carrier) const auto &fm = fileHeader.fmInstr[smp]; OPLPatch patch{{}}; @@ -227,9 +227,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) { // Note, instrument, volume ModCommand &m = *pattern.GetpModCommand(row, cmd); - uint8 data[2]; - patChunk.ReadArray(data); - uint8 note = data[0], instrVol = data[1]; + const auto [note, instrVol] = patChunk.ReadArray(); bool fmChn = (cmd >= 4); m.note = NOTE_MIN + (fmChn ? 12 : 36) + (note & 0x0F) + ((note >> 4) & 0x07) * 12; m.instr = (fmChn ? 33 : 1) + (instrVol >> 4) + ((note & 0x80) >> 3); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp index d7b7bb25c..74dc4ba08 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp @@ -127,7 +127,7 @@ MPT_BINARY_STRUCT(DBMEnvelope, 136) // Note: Unlike in MOD, 1Fx, 2Fx, 5Fx / 5xF, 6Fx / 6xF and AFx / AxF are fine slides. -static const ModCommand::COMMAND dbmEffects[] = +static constexpr ModCommand::COMMAND dbmEffects[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, @@ -262,7 +262,7 @@ static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundF if(dbmEnv.flags & DBMEnvelope::envLoop) mptEnv.dwFlags.set(ENV_LOOP); } - uint8 numPoints = std::min(dbmEnv.numSegments, 31) + 1; + uint8 numPoints = std::min(dbmEnv.numSegments.get(), uint8(31)) + 1; mptEnv.resize(numPoints); mptEnv.nLoopStart = dbmEnv.loopBegin; @@ -345,8 +345,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) InitializeChannels(); m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = Clamp(infoData.channels, 1, MAX_BASECHANNELS); // note: MAX_BASECHANNELS is currently 127, but DBPro 2 supports up to 128 channels, DBPro 3 apparently up to 254. - m_nInstruments = std::min(infoData.instruments, MAX_INSTRUMENTS - 1); - m_nSamples = std::min(infoData.samples, MAX_SAMPLES - 1); + m_nInstruments = std::min(static_cast(infoData.instruments), static_cast(MAX_INSTRUMENTS - 1)); + m_nSamples = std::min(static_cast(infoData.samples), static_cast(MAX_SAMPLES - 1)); m_playBehaviour.set(kSlidesAtSpeed1); m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITArpeggio); @@ -354,7 +354,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::ISO8859_1; // Name chunk FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME); @@ -378,10 +378,10 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(!Order().empty()) { // Add a new sequence for this song - if(Order.AddSequence(false) == SEQUENCEINDEX_INVALID) + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; } - Order().SetName(name); + Order().SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, name)); ReadOrderFromFile(Order(), songChunk, numOrders); #else const ORDERINDEX startIndex = Order().GetLength(); @@ -413,10 +413,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) { continue; } - ModSample &mptSmp = Samples[instrHeader.sample]; - mpt::String::Read(mptIns->name, instrHeader.name); - mpt::String::Read(m_szNames[instrHeader.sample], instrHeader.name); + mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); + m_szNames[instrHeader.sample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); mptIns->nFadeOut = 0; mptIns->nPan = static_cast(instrHeader.panning + 128); @@ -424,8 +423,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) mptIns->dwFlags.set(INS_SETPANNING); // Sample Info + ModSample &mptSmp = Samples[instrHeader.sample]; mptSmp.Initialize(); - mptSmp.nVolume = std::min(instrHeader.volume, 64) * 4u; + mptSmp.nVolume = std::min(static_cast(instrHeader.volume), uint16(64)) * 4u; mptSmp.nC5Speed = instrHeader.sampleRate; if(instrHeader.loopLength && (instrHeader.flags & (DBMInstrument::smpLoop | DBMInstrument::smpPingPongLoop))) @@ -433,7 +433,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) mptSmp.nLoopStart = instrHeader.loopStart; mptSmp.nLoopEnd = mptSmp.nLoopStart + instrHeader.loopLength; mptSmp.uFlags.set(CHN_LOOP); - if(instrHeader.flags & DBMInstrument::smpPingPongLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); + if(instrHeader.flags & DBMInstrument::smpPingPongLoop) + mptSmp.uFlags.set(CHN_PINGPONGLOOP); } } @@ -462,6 +463,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) patternNameChunk.Skip(1); // Encoding, should be UTF-8 or ASCII Patterns.ResizeArray(infoData.patterns); + std::vector> lostGlobalCommands; for(PATTERNINDEX pat = 0; pat < infoData.patterns; pat++) { uint16 numRows = patternChunk.ReadUint16BE(); @@ -469,9 +471,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) FileReader chunk = patternChunk.ReadChunk(packedSize); if(!Patterns.Insert(pat, numRows)) - { continue; - } std::string patName; patternNameChunk.ReadSizedString(patName); @@ -479,6 +479,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) PatternRow patRow = Patterns[pat].GetRow(0); ROWINDEX row = 0; + lostGlobalCommands.clear(); while(chunk.CanRead(1)) { const uint8 ch = chunk.ReadUint8(); @@ -486,6 +487,12 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(!ch) { // End Of Row + for(const auto &cmd : lostGlobalCommands) + { + Patterns[pat].WriteEffect(EffectWriter(cmd.first, cmd.second).Row(row)); + } + lostGlobalCommands.clear(); + if(++row >= numRows) break; @@ -503,12 +510,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) uint8 note = chunk.ReadUint8(); if(note == 0x1F) - note = NOTE_KEYOFF; + m.note = NOTE_KEYOFF; else if(note > 0 && note < 0xFE) - { - note = ((note >> 4) * 12) + (note & 0x0F) + 13; - } - m.note = note; + m.note = ((note >> 4) * 12) + (note & 0x0F) + 13; } if(b & 0x02) { @@ -516,8 +520,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) } if(b & 0x3C) { - uint8 cmd1 = CMD_NONE, cmd2 = CMD_NONE; - uint8 param1 = 0, param2 = 0; + uint8 cmd1 = 0, cmd2 = 0, param1 = 0, param2 = 0; if(b & 0x04) cmd2 = chunk.ReadUint8(); if(b & 0x08) param2 = chunk.ReadUint8(); if(b & 0x10) cmd1 = chunk.ReadUint8(); @@ -531,7 +534,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) std::swap(param1, param2); } - ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2); + const auto lostCommand = ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2); + if(ModCommand::IsGlobalCommand(lostCommand.first, lostCommand.second)) + lostGlobalCommands.insert(lostGlobalCommands.begin(), lostCommand); // Insert at front so that the last command of same type "wins" m.volcmd = cmd1; m.vol = param1; @@ -604,9 +609,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) plugin.Info.gain = 10; plugin.Info.reserved = 0; plugin.Info.dwOutputRouting = 0; - std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + mpt::size(plugin.Info.dwReserved), 0); - mpt::String::Write(plugin.Info.szName, "Echo"); - mpt::String::Write(plugin.Info.szLibraryName, "DigiBooster Pro Echo"); + std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + std::size(plugin.Info.dwReserved), 0); + plugin.Info.szName = "Echo"; + plugin.Info.szLibraryName = "DigiBooster Pro Echo"; plugin.pluginData.resize(sizeof(DigiBoosterEcho::PluginChunk)); DigiBoosterEcho::PluginChunk chunk = DigiBoosterEcho::PluginChunk::Create(settings[1], settings[3], settings[5], settings[7]); @@ -637,11 +642,11 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) uint32 sampleFlags = sampleChunk.ReadUint32BE(); uint32 sampleLength = sampleChunk.ReadUint32BE(); - ModSample &sample = Samples[smp]; - sample.nLength = sampleLength; - if(sampleFlags & 7) { + ModSample &sample = Samples[smp]; + sample.nLength = sampleLength; + SampleIO( (sampleFlags & 4) ? SampleIO::_32bit : ((sampleFlags & 2) ? SampleIO::_16bit : SampleIO::_8bit), SampleIO::mono, @@ -668,7 +673,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(ReadMP3Sample(0, chunk, true)) { ModSample &srcSample = Samples[0]; - const mpt::byte *smpData = srcSample.sampleb(); + const std::byte *smpData = srcSample.sampleb(); SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000); LimitMax(predelay, srcSample.nLength); smpData += predelay * srcSample.GetBytesPerSample(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp index 5029f67e8..71125c312 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp @@ -130,7 +130,7 @@ bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::ISO8859_1; ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.lastOrdIndex + 1); @@ -148,7 +148,7 @@ bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) } sample.SanitizeLoops(); - sample.nVolume = std::min(fileHeader.smpVolume[smp], 64) * 4; + sample.nVolume = std::min(fileHeader.smpVolume[smp].get(), uint8(64)) * 4; sample.nFineTune = MOD2XMFineTune(fileHeader.smpFinetune[smp]); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp index b345243e0..a09d1bde1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp @@ -195,17 +195,17 @@ static uint8 DMFporta2MPT(uint8 val, const uint8 internalTicks, const bool hasFi else if((val <= 0x0F && hasFine) || internalTicks < 2) return (val | 0xF0); else - return std::max(1, (val / (internalTicks - 1))); // no porta on first tick! + return std::max(uint8(1), static_cast((val / (internalTicks - 1)))); // no porta on first tick! } // Convert portamento / volume slide value (not very accurate due to X-Tracker's higher granularity, to say the least) static uint8 DMFslide2MPT(uint8 val, const uint8 internalTicks, const bool up) { - val = std::max(1, val / 4); + val = std::max(uint8(1), static_cast(val / 4)); const bool isFine = (val < 0x0F) || (internalTicks < 2); if(!isFine) - val = std::max(1, (val + internalTicks - 2) / (internalTicks - 1)); // no slides on first tick! "+ internalTicks - 2" for rounding precision + val = std::max(uint8(1), static_cast((val + internalTicks - 2) / (internalTicks - 1))); // no slides on first tick! "+ internalTicks - 2" for rounding precision if(up) return (isFine ? 0x0F : 0x00) | (val << 4); @@ -242,7 +242,7 @@ static uint8 DMFvibrato2MPT(uint8 val, const uint8 internalTicks) // X-Tracker: Period length specified in rows! const int periodInTicks = std::max(1, (val >> 4)) * internalTicks; const uint8 matchingPeriod = static_cast(Clamp((128 / periodInTicks), 1, 15)); - return (matchingPeriod << 4) | std::max(1, (val & 0x0F)); + return (matchingPeriod << 4) | std::max(uint8(1), static_cast(val & 0x0F)); } @@ -340,7 +340,7 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett } PatternRow m = sndFile.Patterns[pat].GetRow(0); - const CHANNELINDEX numChannels = std::min(sndFile.GetNumChannels() - 1, patHead.numTracks); + const CHANNELINDEX numChannels = std::min(static_cast(sndFile.GetNumChannels() - 1), static_cast(patHead.numTracks)); // When breaking to a pattern with less channels that the previous pattern, // all voices in the now unused channels are killed: @@ -463,21 +463,19 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett // => Tempo = 60 * Rows per Second * Speed / 24 // For some reason, using settings.tempoTicks + 1 gives more accurate results than just settings.tempoTicks... (same problem in the old libmodplug DMF loader) // Original unoptimized formula: - //const int tickspeed = (tempoRealBPMmode) ? MAX(1, (tempoData * beat * 4) / 60) : tempoData; + //const int tickspeed = (tempoRealBPMmode) ? std::max(1, (tempoData * beat * 4) / 60) : tempoData; const int tickspeed = (settings.realBPMmode) ? std::max(1, settings.tempoBPM * settings.beat * 2) : ((settings.tempoTicks + 1) * 30); // Try to find matching speed - try higher speeds first, so that effects like arpeggio and tremor work better. - for(speed = 255; speed > 2; speed--) + for(speed = 255; speed >= 1; speed--) { // Original unoptimized formula: // tempo = 30 * tickspeed * speed / 48; tempo = tickspeed * speed / 48; if(tempo >= 32 && tempo <= 255) - { break; - } } Limit(tempo, 32, 255); - settings.internalTicks = (uint8)speed; + settings.internalTicks = static_cast(std::max(1, speed)); } else { tempoChange = false; @@ -915,14 +913,10 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = mpt::format(U_("X-Tracker v%1"))(fileHeader.version); m_modFormat.type = U_("dmf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, fileHeader.songname); - { - std::string artist; - mpt::String::Read(artist, fileHeader.composer); - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437, artist); - } + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songname); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.composer)); FileHistory mptHistory; mptHistory.loadDate.tm_mday = Clamp(fileHeader.creationDay, uint8(1), uint8(31)); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp index 3f01635dd..cf6ce188a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp @@ -73,14 +73,14 @@ struct DSMSampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nC5Speed = sampleRate; mptSmp.uFlags.set(CHN_LOOP, (flags & 1) != 0); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; - mptSmp.nVolume = std::min(volume, 64) * 4; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; } // Retrieve the internal sample format flags for this sample. @@ -210,13 +210,13 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("DSIK Format"); m_modFormat.type = U_("dsm"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, songHeader.songName); - m_nChannels = std::max(songHeader.numChannels, 1); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, songHeader.songName); + m_nChannels = std::max(songHeader.numChannels.get(), uint16(1)); m_nDefaultSpeed = songHeader.speed; m_nDefaultTempo.Set(songHeader.bpm); - m_nDefaultGlobalVolume = std::min(songHeader.globalVol, 64) * 4u; + m_nDefaultGlobalVolume = std::min(songHeader.globalVol.get(), uint8(64)) * 4u; if(!m_nDefaultGlobalVolume) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; if(songHeader.mastervol == 0x80) { @@ -289,8 +289,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) } if(flag & 0x10) { - uint8 command = chunk.ReadUint8(); - uint8 param = chunk.ReadUint8(); + auto [command, param] = chunk.ReadArray(); switch(command) { // Portamentos @@ -324,7 +323,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) chunk.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(sample); - mpt::String::Read(m_szNames[m_nSamples], sampleHeader.sampleName); + m_szNames[m_nSamples] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.sampleName); if(loadFlags & loadSampleData) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp index 85de0e0cb..1b717fece 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp @@ -107,7 +107,7 @@ struct DTMSample transposeAmount += (48 - transpose) * 128; } mptSmp.Transpose(transposeAmount * (1.0 / (12.0 * 128.0))); - mptSmp.nVolume = std::min(volume, 64u) * 4u; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; if(stereo & 1) { mptSmp.uFlags.set(CHN_STEREO); @@ -290,14 +290,14 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 32 && chn < GetNumChannels(); chn++) { // Panning is in range 0...180, 90 = center - ChnSettings[chn].nPan = static_cast(128 + Util::muldivr(std::min(panning[chn], 180) - 90, 128, 90)); + ChnSettings[chn].nPan = static_cast(128 + Util::muldivr(std::min(static_cast(panning[chn]), int(180)) - 90, 128, 90)); } chunk.Skip(16); // Chunk ends here for old DTM modules if(chunk.CanRead(2)) { - m_nDefaultGlobalVolume = std::min(chunk.ReadUint16BE(), MAX_GLOBAL_VOLUME); + m_nDefaultGlobalVolume = std::min(chunk.ReadUint16BE(), static_cast(MAX_GLOBAL_VOLUME)); } chunk.Skip(128); uint16be volume[32]; @@ -306,7 +306,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 32 && chn < GetNumChannels(); chn++) { // Volume is in range 0...128, 64 = normal - ChnSettings[chn].nVolume = static_cast(std::min(volume[chn], 128) / 2); + ChnSettings[chn].nVolume = static_cast(std::min(static_cast(volume[chn]), int(128)) / 2); } m_nSamplePreAmp *= 2; // Compensate for channel volume range } @@ -348,13 +348,13 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) m_nSamples = std::max(m_nSamples, realSample); ModSample &mptSmp = Samples[realSample]; dtmSample.ConvertToMPT(mptSmp, fileHeader.forcedSampleRate, patternFormat); - mpt::String::Read(m_szNames[realSample], dtmSample.name); + m_szNames[realSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, dtmSample.name); } if(chunk.ReadUint16BE() == 0x0004) { // Digital Home Studio instruments - m_nInstruments = std::min(m_nSamples, MAX_INSTRUMENTS - 1); + m_nInstruments = std::min(static_cast(m_nSamples), static_cast(MAX_INSTRUMENTS - 1)); FileReader envChunk = chunks.GetChunk(DTMChunk::idIENV); while(chunk.CanRead(sizeof(DTMInstrument))) @@ -372,16 +372,16 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) if(mptIns != nullptr) { InstrumentEnvelope &mptEnv = mptIns->VolEnv; - mptIns->nFadeOut = std::min(instr.fadeout, 0xFFF); + mptIns->nFadeOut = std::min(static_cast(instr.fadeout), uint16(0xFFF)); if(instr.envelope != 0xFF && envChunk.Seek(2 + sizeof(DTMEnvelope) * instr.envelope)) { DTMEnvelope env; envChunk.ReadStruct(env); mptEnv.dwFlags.set(ENV_ENABLED); - mptEnv.resize(std::min({ env.numPoints, mpt::size(env.points), MAX_ENVPOINTS })); + mptEnv.resize(std::min({ static_cast(env.numPoints), std::size(env.points), static_cast(MAX_ENVPOINTS) })); for(size_t i = 0; i < mptEnv.size(); i++) { - mptEnv[i].value = std::min(64, env.points[i].value); + mptEnv[i].value = std::min(uint8(64), static_cast(env.points[i].value)); mptEnv[i].tick = env.points[i].tick; } @@ -432,17 +432,16 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) { ModCommand *m = Patterns[patNum].GetpModCommand(position.quot, chn); - uint8 data[6]; - rowChunk.ReadArray(data); - if(data[0] > 0 && data[0] <= 96) + const auto [note, volume, instr, command, param, delay] = rowChunk.ReadArray(); + if(note > 0 && note <= 96) { - m->note = data[0] + NOTE_MIN + 12; + m->note = note + NOTE_MIN + 12; if(position.rem) { m->command = CMD_MODCMDEX; m->param = 0xD0 | static_cast(std::min(position.rem, 15)); } - } else if(data[0] & 0x80) + } else if(note & 0x80) { // Lower 7 bits contain note, probably intended for MIDI-like note-on/note-off events if(position.rem) @@ -454,19 +453,19 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) m->note = NOTE_NOTECUT; } } - if(data[1]) + if(volume) { m->volcmd = VOLCMD_VOLUME; - m->vol = std::min(data[1], uint8(64)); // Volume can go up to 255, but we do not support over-amplification at the moment. + m->vol = std::min(volume, uint8(64)); // Volume can go up to 255, but we do not support over-amplification at the moment. } - if(data[2]) + if(instr) { - m->instr = data[2]; + m->instr = instr; } - if(data[3] || data[4]) + if(command || param) { - m->command = data[3]; - m->param = data[4]; + m->command = command; + m->param = param; ConvertModCommand(*m); #ifdef MODPLUG_TRACKER m->Convert(MOD_TYPE_MOD, MOD_TYPE_IT, *this); @@ -474,10 +473,10 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) // G is 8-bit volume // P is tremor (need to disable oldfx) } - if(data[5] & 0x80) - tick += (data[5] & 0x7F) * 0x100 + rowChunk.ReadUint8(); + if(delay & 0x80) + tick += (delay & 0x7F) * 0x100 + rowChunk.ReadUint8(); else - tick += data[5]; + tick += delay; position = std::div(tick, m_nDefaultSpeed); } } @@ -488,23 +487,23 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) { for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, m++) { - uint8 data[4]; - chunk.ReadArray(data); + const auto data = chunk.ReadArray(); if(patternFormat == DTM_204_PATTERN_FORMAT) { - if(data[0] > 0 && data[0] < 0x80) + const auto [note, instrVol, instrCmd, param] = data; + if(note > 0 && note < 0x80) { - m->note = (data[0] >> 4) * 12 + (data[0] & 0x0F) + NOTE_MIN + 11; + m->note = (note >> 4) * 12 + (note & 0x0F) + NOTE_MIN + 11; } - uint8 vol = data[1] >> 2; + uint8 vol = instrVol >> 2; if(vol) { m->volcmd = VOLCMD_VOLUME; m->vol = vol - 1u; } - m->instr = ((data[1] & 0x03) << 4) | (data[2] >> 4); - m->command = data[2] & 0x0F; - m->param = data[3]; + m->instr = ((instrVol & 0x03) << 4) | (instrCmd >> 4); + m->command = instrCmd & 0x0F; + m->param = param; } else { ReadMODPatternEntry(data, *m); @@ -564,7 +563,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) while(chunk.CanRead(1) && chn < GetNumChannels()) { chunk.ReadNullString(name, 32); - mpt::String::Copy(ChnSettings[chn].szName, name); + ChnSettings[chn].szName = name; chn++; } } @@ -601,7 +600,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Digital Tracker"); m_modFormat.type = U_("dtm"); m_modFormat.madeWithTracker = std::move(tracker); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp index d902c74f1..da53d566a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp @@ -171,9 +171,9 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Farandole Composer"); m_modFormat.type = U_("far"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); // Read channel settings for(CHANNELINDEX chn = 0; chn < 16; chn++) @@ -201,7 +201,7 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) file.Seek(fileHeader.headerLength); // Pattern effect LUT - static const EffectCommand farEffects[] = + static constexpr EffectCommand farEffects[] = { CMD_NONE, CMD_PORTAMENTOUP, @@ -257,24 +257,23 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) { ModCommand &m = rowBase[chn]; - uint8 data[4]; - patternChunk.ReadArray(data); + const auto [note, instr, volume, effect] = patternChunk.ReadArray(); - if(data[0] > 0 && data[0] < 85) + if(note > 0 && note <= 72) { - m.note = data[0] + 35 + NOTE_MIN; - m.instr = data[1] + 1; + m.note = note + 35 + NOTE_MIN; + m.instr = instr + 1; } - if(m.note != NOTE_NONE || data[2] > 0) + if(m.note != NOTE_NONE || volume > 0) { m.volcmd = VOLCMD_VOLUME; - m.vol = (Clamp(data[2], uint8(1), uint8(16)) - 1u) * 4u; + m.vol = (Clamp(volume, uint8(1), uint8(16)) - 1u) * 4u; } - m.param = data[3] & 0x0F; + m.param = effect & 0x0F; - switch(data[3] >> 4) + switch(effect >> 4) { case 0x03: // Porta to note m.param <<= 2; @@ -297,7 +296,7 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) m.param = 6 / (1 + m.param) + 1; m.param |= 0x0D; } - m.command = farEffects[data[3] >> 4]; + m.command = farEffects[effect >> 4]; } } @@ -328,7 +327,7 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) m_nSamples = smp + 1; ModSample &sample = Samples[m_nSamples]; - mpt::String::Read(m_szNames[m_nSamples], sampleHeader.name); + m_szNames[m_nSamples] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(sample); sampleHeader.GetSampleFormat().ReadSample(sample, file); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp index 33492ce04..7171d8b06 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp @@ -90,15 +90,15 @@ struct GDMSampleHeader MPT_BINARY_STRUCT(GDMSampleHeader, 62) -static const MODTYPE gdmFormatOrigin[] = +static constexpr MODTYPE gdmFormatOrigin[] = { MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM }; -static const MPT_UCHAR_TYPE gdmFormatOriginType[][4] = +static constexpr mpt::uchar gdmFormatOriginType[][4] = { UL_(""), UL_("mod"), UL_("mtm"), UL_("s3m"), UL_("669"), UL_("far"), UL_("ult"), UL_("stm"), UL_("med"), UL_("psm") }; -static const MPT_UCHAR_TYPE * const gdmFormatOriginFormat[] = +static constexpr const mpt::uchar * gdmFormatOriginFormat[] = { UL_(""), UL_("Generic MOD"), @@ -119,7 +119,7 @@ static bool ValidateHeader(const GDMFileHeader &fileHeader) || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26 || std::memcmp(fileHeader.magic2, "GMFS", 4) || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0 - || fileHeader.originalFormat >= mpt::size(gdmFormatOrigin) + || fileHeader.originalFormat >= std::size(gdmFormatOrigin) || fileHeader.originalFormat == 0) { return false; @@ -169,18 +169,17 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::CP437; // Song name - mpt::String::Read(m_songName, fileHeader.songTitle); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songTitle); // Artist name { - std::string artist; - mpt::String::Read(artist, fileHeader.songMusician); + std::string artist = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songMusician); if(artist != "Unknown") { - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437, artist); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, artist); } } @@ -236,10 +235,25 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[smp]; sample.Initialize(); - mpt::String::Read(m_szNames[smp], gdmSample.name); - mpt::String::Read(sample.filename, gdmSample.fileName); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.name); + sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.fileName); sample.nC5Speed = gdmSample.c4Hertz; + if(UseFinetuneAndTranspose()) + { + // Use the same inaccurate table as 2GDM for translating back to finetune, as our own routines + // give slightly different results for the provided sample rates that may result in transpose != 0. + static constexpr uint16 rate2finetune[] = { 8363, 8424, 8485, 8547, 8608, 8671, 8734, 8797, 7894, 7951, 8009, 8067, 8125, 8184, 8244, 8303 }; + for(uint8 i = 0; i < 16; i++) + { + if(sample.nC5Speed == rate2finetune[i]) + { + sample.nFineTune = MOD2XMFineTune(i); + break; + } + } + } + sample.nGlobalVol = 64; // Not supported in this format sample.nLength = gdmSample.length; // in bytes @@ -254,26 +268,12 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) sample.nLoopStart = gdmSample.loopBegin; sample.nLoopEnd = gdmSample.loopEnd - 1; - if(UseFinetuneAndTranspose()) - { - // Use the same inaccurate table as 2GDM for translating back to finetune, as our own routines - // give slightly different results for the provided sample rates that may result in transpose != 0. - static 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++) - { - if(sample.nC5Speed == rate2finetune[i]) - { - sample.nFineTune = MOD2XMFineTune(i); - } - } - } - if(gdmSample.flags & GDMSampleHeader::smpLoop) sample.uFlags.set(CHN_LOOP); // Loop sample if(gdmSample.flags & GDMSampleHeader::smpVolume) { // Default volume is used... 0...64, 255 = no default volume - sample.nVolume = std::min(gdmSample.volume, 64) * 4; + sample.nVolume = std::min(static_cast(gdmSample.volume), uint8(64)) * 4; } else { sample.uFlags.set(SMP_NODEFAULTVOLUME); @@ -357,20 +357,18 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) if(channelByte & noteFlag) { // Note and sample follows - uint8 noteByte = chunk.ReadUint8(); - uint8 noteSample = chunk.ReadUint8(); + auto [note, instr] = chunk.ReadArray(); - if(noteByte) + if(note) { - noteByte = (noteByte & 0x7F) - 1; // This format doesn't have note cuts - if(noteByte < 0xF0) noteByte = (noteByte & 0x0F) + 12 * (noteByte >> 4) + 12 + NOTE_MIN; - m.note = noteByte; + note = (note & 0x7F) - 1; // High bit = no-retrig flag (notes with portamento have this set) + m.note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN; if(!m.IsAmigaNote()) { onlyAmigaNotes = false; } } - m.instr = noteSample; + m.instr = instr; } if(channelByte & effectFlag) @@ -384,11 +382,11 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) // We may want to restore the old command in some cases. const ModCommand oldCmd = m; - uint8 effByte = chunk.ReadUint8(); - m.param = chunk.ReadUint8(); + const auto [effByte, param] = chunk.ReadArray(); + m.param = param; // Effect translation LUT - static const EffectCommand gdmEffTrans[] = + static constexpr EffectCommand gdmEffTrans[] = { CMD_NONE, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, @@ -431,7 +429,7 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) break; case CMD_VOLUME: - m.param = MIN(m.param, 64); + m.param = std::min(m.param, uint8(64)); if(modSpecs.HasVolCommand(VOLCMD_VOLUME)) { m.volcmd = VOLCMD_VOLUME; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp index 72da4c02d..23c72a15d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp @@ -110,7 +110,7 @@ struct IMFInstrument uint16 minTick = 0; // minimum tick value for next node for(uint32 n = 0; n < mptEnv.size(); n++) { - minTick = mptEnv[n].tick = std::max(minTick, nodes[e][n].tick); + mptEnv[n].tick = minTick = std::max(minTick, nodes[e][n].tick.get()); minTick++; mptEnv[n].value = static_cast(std::min(nodes[e][n].value >> shift, ENVELOPE_MAX)); } @@ -119,12 +119,12 @@ struct IMFInstrument // Convert an IMFInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX firstSample) const { - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::nullTerminated, name); if(smpNum) { - STATIC_ASSERT(CountOf(mptIns.Keyboard) >= CountOf(map)); - for(size_t note = 0; note < CountOf(map); note++) + static_assert(mpt::array_size::size >= mpt::array_size::size); + for(size_t note = 0; note < std::size(map); note++) { mptIns.Keyboard[note] = firstSample + map[note]; } @@ -175,7 +175,7 @@ struct IMFSample void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; @@ -202,7 +202,7 @@ struct IMFSample MPT_BINARY_STRUCT(IMFSample, 64) -static const EffectCommand imfEffects[] = +static constexpr EffectCommand imfEffects[] = { CMD_NONE, CMD_SPEED, // 0x01 1xx Set Tempo @@ -273,13 +273,13 @@ static void ImportIMFEffect(ModCommand &m) break; case 0xF: // set finetune // we don't implement this, but let's at least import the value - m.param = 0x20 | MIN(m.param >> 4, 0x0F); + m.param = 0x20 | std::min(static_cast(m.param >> 4), uint8(0x0F)); break; case 0x14: // fine slide up case 0x15: // fine slide down // this is about as close as we can do... if(m.param >> 4) - m.param = 0xF0 | MIN(m.param >> 4, 0x0F); + m.param = 0xF0 | std::min(static_cast(m.param >> 4), uint8(0x0F)); else m.param |= 0xE0; break; @@ -287,7 +287,7 @@ static void ImportIMFEffect(ModCommand &m) m.param = (0xFF - m.param) / 2u; break; case 0x1F: // set global volume - m.param = MIN(m.param << 1, 0xFF); + m.param = mpt::saturate_cast(m.param * 2); break; case 0x21: n = 0; @@ -435,7 +435,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) ChnSettings[chn].Reset(); ChnSettings[chn].nPan = fileHeader.channels[chn].panning * 256 / 255; - mpt::String::Read(ChnSettings[chn].szName, fileHeader.channels[chn].name); + ChnSettings[chn].szName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.channels[chn].name); // TODO: reverb/chorus? switch(fileHeader.channels[chn].status) @@ -461,7 +461,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Imago Orpheus"); m_modFormat.type = U_("imf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; //From mikmod: work around an Orpheus bug if(fileHeader.channels[0].status == 0) @@ -476,7 +476,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) } // Song Name - mpt::String::Read(m_songName, fileHeader.title); + m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.title); m_SongFlags.set(SONG_LINEARSLIDES, fileHeader.flags & IMFFileHeader::linearSlides); m_nDefaultSpeed = fileHeader.tempo; @@ -521,8 +521,9 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) if(mask & 0x20) { // Read note/instrument - m.note = patternChunk.ReadUint8(); - m.instr = patternChunk.ReadUint8(); + const auto [note, instr] = patternChunk.ReadArray(); + m.note = note; + m.instr = instr; if(m.note == 160) { @@ -542,20 +543,17 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) if((mask & 0xC0) == 0xC0) { // Read both effects and figure out what to do with them - uint8 e1c = patternChunk.ReadUint8(); // Command 1 - uint8 e1d = patternChunk.ReadUint8(); // Data 1 - uint8 e2c = patternChunk.ReadUint8(); // Command 2 - uint8 e2d = patternChunk.ReadUint8(); // Data 2 + const auto [e1c, e1d, e2c, e2d] = patternChunk.ReadArray(); // Command 1, Data 1, Command 2, Data 2 if(e1c == 0x0C) { - m.vol = MIN(e1d, 0x40); + m.vol = std::min(e1d, uint8(0x40)); m.volcmd = VOLCMD_VOLUME; m.command = e2c; m.param = e2d; } else if(e2c == 0x0C) { - m.vol = MIN(e2d, 0x40); + m.vol = std::min(e2d, uint8(0x40)); m.volcmd = VOLCMD_VOLUME; m.command = e1c; m.param = e1d; @@ -582,8 +580,9 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) } else if(mask & 0xC0) { // There's one effect, just stick it in the effect column - m.command = patternChunk.ReadUint8(); - m.param = patternChunk.ReadUint8(); + const auto [command, param] = patternChunk.ReadArray(); + m.command = command; + m.param = param; } if(m.command) ImportIMFEffect(m); @@ -625,7 +624,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[smpID]; sampleHeader.ConvertToMPT(sample); - mpt::String::Copy(m_szNames[smpID], sample.filename); + m_szNames[smpID] = sample.filename; if(sampleHeader.length) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp index e149e1d2e..89f281f73 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp @@ -26,7 +26,7 @@ #include "../common/mptFileIO.h" #endif // MODPLUG_NO_FILESAVE #include "plugins/PlugInterface.h" -#include "../common/mptBufferIO.h" +#include #include "../common/version.h" #include "ITTools.h" @@ -59,12 +59,12 @@ MPTM version history for cwtv-field in "IT" header (only for MPTM files!): #ifndef MODPLUG_NO_FILESAVE -static bool AreNonDefaultTuningsUsed(CSoundFile& sf) +static bool AreNonDefaultTuningsUsed(const CSoundFile& sf) { - const INSTRUMENTINDEX iCount = sf.GetNumInstruments(); - for(INSTRUMENTINDEX i = 1; i <= iCount; i++) + const INSTRUMENTINDEX numIns = sf.GetNumInstruments(); + for(INSTRUMENTINDEX i = 1; i <= numIns; i++) { - if(sf.Instruments[i] != nullptr && sf.Instruments[i]->pTuning != 0) + if(sf.Instruments[i] != nullptr && sf.Instruments[i]->pTuning != nullptr) return true; } return false; @@ -72,7 +72,7 @@ static bool AreNonDefaultTuningsUsed(CSoundFile& sf) static void WriteTuningCollection(std::ostream& oStrm, const CTuningCollection& tc) { - tc.Serialize(oStrm, "Tune specific tunings"); + tc.Serialize(oStrm, U_("Tune specific tunings")); } static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf) @@ -113,7 +113,7 @@ static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf) for(auto &iter : tNameToShort_Map) { if(iter.first) - mpt::IO::WriteSizedStringLE(oStrm, iter.first->GetName()); + mpt::IO::WriteSizedStringLE(oStrm, mpt::ToCharset(mpt::Charset::UTF8, iter.first->GetName())); else //Case: Using original IT tuning. mpt::IO::WriteSizedStringLE(oStrm, "->MPT_ORIGINAL_IT<-"); @@ -142,15 +142,16 @@ static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf) #endif // MODPLUG_NO_FILESAVE -static void ReadTuningCollection(std::istream& iStrm, CTuningCollection& tc, const size_t) +static void ReadTuningCollection(std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy, mpt::Charset defaultCharset) { - std::string name; - tc.Deserialize(iStrm, name); + MPT_UNREFERENCED_PARAMETER(dummy); + mpt::ustring name; + tc.Deserialize(iStrm, name, defaultCharset); } template -static bool ReadTuningMapTemplate(std::istream& iStrm, std::map& shortToTNameMap, const size_t maxNum = 500) +static bool ReadTuningMapTemplate(std::istream& iStrm, std::map &shortToTNameMap, mpt::Charset charset, const size_t maxNum = 500) { TUNNUMTYPE numTuning = 0; mpt::IO::ReadIntLE(iStrm, numTuning); @@ -165,7 +166,7 @@ static bool ReadTuningMapTemplate(std::istream& iStrm, std::map(iStrm, ui); - shortToTNameMap[ui] = temp; + shortToTNameMap[ui] = mpt::ToUnicode(charset, temp); } if(iStrm.good()) return false; @@ -174,19 +175,19 @@ static bool ReadTuningMapTemplate(std::istream& iStrm, std::map shortToTNameMap; + std::map shortToTNameMap; if(old) { - ReadTuningMapTemplate(iStrm, shortToTNameMap); + ReadTuningMapTemplate(iStrm, shortToTNameMap, charset); } else { - ReadTuningMapTemplate(iStrm, shortToTNameMap); + ReadTuningMapTemplate(iStrm, shortToTNameMap, charset); } // Read & set tunings for instruments - std::vector notFoundTunings; + std::vector notFoundTunings; for(INSTRUMENTINDEX i = 1; i<=csf.GetNumInstruments(); i++) { uint16 ui = 0; @@ -194,9 +195,9 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t auto iter = shortToTNameMap.find(ui); if(csf.Instruments[i] && iter != shortToTNameMap.end()) { - const std::string str = iter->second; + const mpt::ustring str = iter->second; - if(str == "->MPT_ORIGINAL_IT<-") + if(str == U_("->MPT_ORIGINAL_IT<-")) { csf.Instruments[i]->pTuning = nullptr; continue; @@ -210,11 +211,12 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str); if(localTuning) { - CTuning* pNewTuning = new CTuning(*localTuning); - if(!csf.GetTuneSpecificTunings().AddTuning(pNewTuning)) + std::unique_ptr pNewTuning = std::unique_ptr(new CTuning(*localTuning)); + CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning)); + if(pT) { - csf.AddToLog("Local tunings are deprecated and no longer supported. Tuning '" + str + "' found in Local tunings has been copied to Tune-specific tunings and will be saved in the module file."); - csf.Instruments[i]->pTuning = pNewTuning; + csf.AddToLog(U_("Local tunings are deprecated and no longer supported. Tuning '") + str + U_("' found in Local tunings has been copied to Tune-specific tunings and will be saved in the module file.")); + csf.Instruments[i]->pTuning = pT; if(csf.GetpModDoc() != nullptr) { csf.GetpModDoc()->SetModified(); @@ -222,20 +224,20 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t continue; } else { - delete pNewTuning; - csf.AddToLog("Copying Local tuning '" + str + "' to Tune-specific tunings failed."); + csf.AddToLog(U_("Copying Local tuning '") + str + U_("' to Tune-specific tunings failed.")); } } #endif - if(str == "12TET [[fs15 1.17.02.49]]" || str == "12TET") + if(str == U_("12TET [[fs15 1.17.02.49]]") || str == U_("12TET")) { - CTuning* pNewTuning = csf.CreateTuning12TET(str); - if(!csf.GetTuneSpecificTunings().AddTuning(pNewTuning)) + std::unique_ptr pNewTuning = csf.CreateTuning12TET(str); + CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning)); + if(pT) { #ifdef MODPLUG_TRACKER - csf.AddToLog("Built-in tunings will no longer be used. Tuning '" + str + "' has been copied to Tune-specific tunings and will be saved in the module file."); - csf.Instruments[i]->pTuning = pNewTuning; + csf.AddToLog(U_("Built-in tunings will no longer be used. Tuning '") + str + U_("' has been copied to Tune-specific tunings and will be saved in the module file.")); + csf.Instruments[i]->pTuning = pT; if(csf.GetpModDoc() != nullptr) { csf.GetpModDoc()->SetModified(); @@ -244,9 +246,8 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t continue; } else { - delete pNewTuning; #ifdef MODPLUG_TRACKER - csf.AddToLog("Copying Built-in tuning '" + str + "' to Tune-specific tunings failed."); + csf.AddToLog(U_("Copying Built-in tuning '") + str + U_("' to Tune-specific tunings failed.")); #endif } } @@ -255,7 +256,7 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t if(std::find(notFoundTunings.begin(), notFoundTunings.end(), str) == notFoundTunings.end()) { notFoundTunings.push_back(str); - csf.AddToLog("Tuning '" + str + "' used by the module was not found."); + csf.AddToLog(U_("Tuning '") + str + U_("' used by the module was not found.")); #ifdef MODPLUG_TRACKER if(csf.GetpModDoc() != nullptr) { @@ -278,9 +279,9 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t } -static void ReadTuningMap(std::istream& iStrm, CSoundFile& csf, const size_t dummy = 0) +static void ReadTuningMap(std::istream& iStrm, CSoundFile& csf, const size_t dummy, mpt::Charset charset) { - ReadTuningMapImpl(iStrm, csf, dummy, false); + ReadTuningMapImpl(iStrm, csf, charset, dummy, false); } @@ -485,32 +486,32 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // OpenMPT 1.17.02.26 (r122) to 1.18 (raped IT format) // Exact version number will be determined later. interpretModPlugMade = true; - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } else if(fileHeader.cwtv == 0x0217 && fileHeader.cmwt == 0x0200 && fileHeader.reserved == 0) { if(memchr(fileHeader.chnpan, 0xFF, sizeof(fileHeader.chnpan)) != nullptr) { // ModPlug Tracker 1.16 (semi-raped IT format) or BeRoTracker (will be determined later) - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker 1.09 - 1.16"); } else { // OpenMPT 1.17 disguised as this in compatible mode, // but never writes 0xFF in the pan map for unused channels (which is an invalid value). - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); madeWithTracker = U_("OpenMPT 1.17 (compatibility export)"); } interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && fileHeader.reserved == 0) { // ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.09.00.00"); madeWithTracker = U_("ModPlug Tracker b3.3 - 1.09"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0300 && fileHeader.cmwt == 0x0300 && fileHeader.reserved == 0 && fileHeader.ordnum == 256 && fileHeader.sep == 128 && fileHeader.pwd == 0) { // A rare variant used from OpenMPT 1.17.02.20 (r113) to 1.17.02.25 (r121), found e.g. in xTr1m-SD.it - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 02, 20); + m_dwLastSavedWithVersion = MPT_V("1.17.02.20"); interpretModPlugMade = true; } } @@ -521,7 +522,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags.set(SONG_ITCOMPATGXX, (fileHeader.flags & ITFileHeader::itCompatGxx) != 0); m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & ITFileHeader::extendedFilterRange) != 0); - mpt::String::Read(m_songName, fileHeader.songname); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songname); // Read row highlights if((fileHeader.special & ITFileHeader::embedPatternHighlights)) @@ -536,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 || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02)) + if(!m_dwLastSavedWithVersion || m_dwLastSavedWithVersion >= MPT_V("1.17.03.02")) { m_nDefaultRowsPerBeat = fileHeader.highlight_minor; m_nDefaultRowsPerMeasure = fileHeader.highlight_major; @@ -545,10 +546,12 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Global Volume m_nDefaultGlobalVolume = fileHeader.globalvol << 1; - if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; - if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; - m_nDefaultTempo.Set(std::max(31, fileHeader.tempo)); - m_nSamplePreAmp = std::min(fileHeader.mv, 128); + if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) + m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; + if(fileHeader.speed) + m_nDefaultSpeed = fileHeader.speed; + m_nDefaultTempo.Set(std::max(uint8(31), static_cast(fileHeader.tempo))); + m_nSamplePreAmp = std::min(static_cast(fileHeader.mv), uint8(128)); // Reading Channels Pan Positions for(CHANNELINDEX i = 0; i < 64; i++) if(fileHeader.chnpan[i] != 0xFF) @@ -590,22 +593,25 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // This is used for finding out whether the edit history is actually stored in the file or not, // as some early versions of Schism Tracker set the history flag, but didn't save anything. // We will consider the history invalid if it ends after the first parapointer. - uint32 minPtr = Util::MaxValueOfType(minPtr); + uint32 minPtr = std::numeric_limits::max(); for(uint32 pos : insPos) { - if(pos > 0 && pos < minPtr) minPtr = pos; + if(pos > 0 && pos < minPtr) + minPtr = pos; } for(uint32 pos : smpPos) { - if(pos > 0 && pos < minPtr) minPtr = pos; + if(pos > 0 && pos < minPtr) + minPtr = pos; } for(uint32 pos : patPos) { - if(pos > 0 && pos < minPtr) minPtr = pos; + if(pos > 0 && pos < minPtr) + minPtr = pos; } if(fileHeader.special & ITFileHeader::embedSongMessage) { - minPtr = std::min(minPtr, fileHeader.msgoffset); + minPtr = std::min(minPtr, fileHeader.msgoffset.get()); } const bool possiblyUNMO3 = fileHeader.cmwt == 0x0214 && (fileHeader.cwtv == 0x0214 || fileHeader.cwtv == 0) @@ -663,9 +669,9 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(possiblyUNMO3 && nflt == 0) { if(fileHeader.special & ITFileHeader::embedPatternHighlights) - madeWithTracker = U_("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 - madeWithTracker = U_("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 { @@ -741,7 +747,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_nInstruments = 0; if(fileHeader.flags & ITFileHeader::instrumentMode) { - m_nInstruments = std::min(fileHeader.insnum, MAX_INSTRUMENTS - 1); + m_nInstruments = std::min(static_cast(fileHeader.insnum), static_cast(MAX_INSTRUMENTS - 1)); } for(INSTRUMENTINDEX i = 0; i < GetNumInstruments(); i++) { @@ -769,7 +775,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) bool possibleXMconversion = false; // Reading Samples - m_nSamples = std::min(fileHeader.smpnum, MAX_SAMPLES - 1); + m_nSamples = std::min(static_cast(fileHeader.smpnum), static_cast(MAX_SAMPLES - 1)); bool lastSampleCompressed = false; for(SAMPLEINDEX i = 0; i < GetNumSamples(); i++) { @@ -780,7 +786,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[i + 1]; size_t sampleOffset = sampleHeader.ConvertToMPT(sample); - mpt::String::Read(m_szNames[i + 1], sampleHeader.name); + m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); if(!file.Seek(sampleOffset)) continue; @@ -825,8 +831,8 @@ 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(U_("Loading external sample %1 ('%2') failed: External samples are not supported."))(i + 1, mpt::ToUnicode(mpt::CharsetUTF8, filenameU8))); -#endif // MPT_EXTERNAL_SAMPLES + AddToLog(LogWarning, mpt::format(U_("Loading external sample %1 ('%2') failed: External samples are not supported."))(i + 1, mpt::ToUnicode(mpt::Charset::UTF8, filenameU8))); +#endif // MPT_EXTERNAL_SAMPLES } else { file.Skip(strLen); @@ -851,7 +857,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(vol != 0x40) possibleXMconversion = false; } - for(size_t i = 20; i < mpt::size(fileHeader.songname); i++) + for(size_t i = 20; i < std::size(fileHeader.songname); i++) { if(fileHeader.songname[i] != 0) possibleXMconversion = false; @@ -928,13 +934,17 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } // Now we actually update the pattern-row entry the note,instrument etc. // Note - if(chnMask[ch] & 1) patternData.Skip(1); + if(chnMask[ch] & 1) + patternData.Skip(1); // Instrument - if(chnMask[ch] & 2) patternData.Skip(1); + if(chnMask[ch] & 2) + patternData.Skip(1); // Volume - if(chnMask[ch] & 4) patternData.Skip(1); + if(chnMask[ch] & 4) + patternData.Skip(1); // Effect - if(chnMask[ch] & 8) patternData.Skip(2); + if(chnMask[ch] & 8) + patternData.Skip(2); } lastSampleOffset = std::max(lastSampleOffset, file.GetPosition()); } @@ -998,7 +1008,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(!file.Skip(4) || !Patterns.Insert(pat, numRows)) continue; - + FileReader patternData = file.ReadChunk(len); // Now (after the Insert() call), we can read the pattern name. @@ -1063,7 +1073,8 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(chnMask[ch] & 1) // Note { uint8 note = patternData.ReadUint8(); - if(note < 0x80) note += NOTE_MIN; + if(note < 0x80) + note += NOTE_MIN; if(!(GetType() & MOD_TYPE_MPT)) { if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE; @@ -1105,7 +1116,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m.volcmd = VOLCMD_VIBRATODEPTH; m.vol = vol - 203; // Old versions of ModPlug saved this as vibrato speed instead, so let's fix that. - if(m.vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 17, 02, 54)) + if(m.vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MPT_V("1.17.02.54")) m.volcmd = VOLCMD_VIBRATOSPEED; } else // 213-222: Unused (was velocity) @@ -1117,8 +1128,9 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Reading command/param if(chnMask[ch] & 8) { - m.command = patternData.ReadUint8(); - m.param = patternData.ReadUint8(); + const auto [command, param] = patternData.ReadArray(); + m.command = command; + m.param = param; S3MConvert(m, true); // In some IT-compatible trackers, it is possible to input a parameter without a command. // In this case, we still need to update the last value memory. OpenMPT didn't do this until v1.25.01.07. @@ -1133,7 +1145,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) { // Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0 // when saving a file in OpenMPT for the first time. - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } if(m_dwLastSavedWithVersion && madeWithTracker.empty()) @@ -1165,15 +1177,16 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } 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_dwLastSavedWithVersion = MPT_V("1.00.00.A5"); madeWithTracker = U_("ModPlug Tracker 1.00a5"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(&fileHeader.reserved, "CHBI", 4)) { madeWithTracker = U_("ChibiTracker"); + m_playBehaviour.reset(kITShortSampleRetrig); } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && fileHeader.special <= 1 && fileHeader.pwd == 0 && fileHeader.reserved == 0 && (fileHeader.flags & (ITFileHeader::vol0Optimisations | ITFileHeader::instrumentMode | ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig | ITFileHeader::extendedFilterRange)) == ITFileHeader::instrumentMode - && m_nSamples > 0 && !strcmp(Samples[1].filename, "XXXXXXXX.YYY")) + && m_nSamples > 0 && (Samples[1].filename == "XXXXXXXX.YYY")) { madeWithTracker = U_("CheeseTracker"); } else if(fileHeader.cwtv == 0 && madeWithTracker.empty()) @@ -1244,7 +1257,7 @@ 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; + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; return true; } @@ -1252,17 +1265,21 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) { - mpt::istringstream iStrm(file.GetRawDataAsString()); + std::istringstream iStrm(file.GetRawDataAsString()); if(cwtv >= 0x88D) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead("mptm", Version::Current().GetRawVersion()); - ssb.ReadItem(GetTuneSpecificTunings(), "0", &ReadTuningCollection); - ssb.ReadItem(*this, "1", &ReadTuningMap); + int8 useUTF8Tuning = 0; + ssb.ReadItem(useUTF8Tuning, "UTF8Tuning"); + mpt::Charset TuningCharset = useUTF8Tuning ? mpt::Charset::UTF8 : GetCharsetInternal(); + ssb.ReadItem(GetTuneSpecificTunings(), "0", [TuningCharset](std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy){ return ReadTuningCollection(iStrm, tc, dummy, TuningCharset); }); + ssb.ReadItem(*this, "1", [TuningCharset](std::istream& iStrm, CSoundFile& csf, const std::size_t dummy){ return ReadTuningMap(iStrm, csf, dummy, TuningCharset); }); ssb.ReadItem(Order, "2", &ReadModSequenceOld); ssb.ReadItem(Patterns, FileIdPatterns, &ReadModPatterns); - ssb.ReadItem(Order, FileIdSequences, &ReadModSequences); + mpt::Charset sequenceDefaultCharset = GetCharsetInternal(); + ssb.ReadItem(Order, FileIdSequences, [sequenceDefaultCharset](std::istream &iStrm, ModSequenceSet &seq, std::size_t nSize){ return ReadModSequences(iStrm, seq, nSize, sequenceDefaultCharset); }); if(ssb.GetStatus() & srlztn::SNT_FAILURE) { @@ -1271,13 +1288,13 @@ void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) } else { // Loading for older files. - std::string name; - if(GetTuneSpecificTunings().Deserialize(iStrm, name) != Tuning::SerializationResult::Success) + mpt::ustring name; + if(GetTuneSpecificTunings().Deserialize(iStrm, name, GetCharsetInternal()) != Tuning::SerializationResult::Success) { AddToLog(LogError, U_("Loading tune specific tunings failed.")); } else { - ReadTuningMapImpl(iStrm, *this, 0, cwtv < 0x88C); + ReadTuningMapImpl(iStrm, *this, GetCharsetInternal(), 0, cwtv < 0x88C); } } } @@ -1359,7 +1376,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c MemsetZero(itHeader); dwChnNamLen = 0; memcpy(itHeader.id, "IMPM", 4); - mpt::String::Write(itHeader.songname, m_songName); + mpt::String::WriteBuf(mpt::String::nullTerminated, itHeader.songname) = m_songName; itHeader.highlight_minor = (uint8)std::min(m_nDefaultRowsPerBeat, ROWINDEX(uint8_max)); itHeader.highlight_major = (uint8)std::min(m_nDefaultRowsPerMeasure, ROWINDEX(uint8_max)); @@ -1377,7 +1394,8 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c // An additional "---" pattern is appended so Impulse Tracker won't ignore the last order item. // Interestingly, this can exceed IT's 256 order limit. Also, IT will always save at least two orders. itHeader.ordnum = std::min(Order().GetLengthTailTrimmed(), specs.ordersMax) + 1; - if(itHeader.ordnum < 2) itHeader.ordnum = 2; + if(itHeader.ordnum < 2) + itHeader.ordnum = 2; } itHeader.insnum = std::min(m_nInstruments, specs.instrumentsMax); @@ -1436,7 +1454,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c { if(Instruments[ins] != nullptr && Instruments[ins]->midiPWD != 0) { - itHeader.pwd = static_cast(mpt::abs(Instruments[ins]->midiPWD)); + itHeader.pwd = static_cast(std::abs(Instruments[ins]->midiPWD)); break; } } @@ -1528,7 +1546,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c for(PATTERNINDEX pat = 0; pat < numNamedPats; pat++) { char name[MAX_PATTERNNAME]; - mpt::String::Write(name, Patterns[pat].GetName()); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = Patterns[pat].GetName(); mpt::IO::Write(f, name); } } @@ -1542,7 +1560,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c for(uint32 inam = 0; inam < nChnNames; inam++) { char name[MAX_CHANNELNAME]; - mpt::String::Write(name, ChnSettings[inam].szName); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = ChnSettings[inam].szName; mpt::IO::Write(f, name); } } @@ -1792,7 +1810,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c 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]); + mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp]; itss.samplepointer = static_cast(dwPos); if(dwPos > uint32_max) @@ -1813,7 +1831,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c mpt::IO::SeekAbsolute(f, dwPos); if(!isExternal) { - if(sample.nLength > smpLength) + if(sample.nLength > smpLength && smpLength != 0) { // Sample length does not fit into IT header! AddToLog(mpt::format("Truncating sample %1: Length exceeds exceeds 4 gigasamples.")(smp)); @@ -1865,10 +1883,12 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c mpt::IO::SeekEnd(f); const mpt::IO::Offset MPTStartPos = mpt::IO::TellWrite(f); - + srlztn::SsbWrite ssb(f); ssb.BeginWrite("mptm", Version::Current().GetRawVersion()); + if(GetTuneSpecificTunings().GetNumTunings() > 0 || AreNonDefaultTuningsUsed(*this)) + ssb.WriteItem(int8(1), "UTF8Tuning"); if(GetTuneSpecificTunings().GetNumTunings() > 0) ssb.WriteItem(GetTuneSpecificTunings(), "0", &WriteTuningCollection); if(AreNonDefaultTuningsUsed(*this)) @@ -1959,13 +1979,13 @@ uint32 CSoundFile::SaveMixPlugins(std::ostream *file, bool updatePlugData) // Dry/Wet ratio mpt::IO::WriteRaw(f, "DWRT", 4); // DWRT chunk does not include a size, so better make sure we always write 4 bytes here. - STATIC_ASSERT(sizeof(IEEE754binary32LE) == 4); + static_assert(sizeof(IEEE754binary32LE) == 4); mpt::IO::Write(f, IEEE754binary32LE(m_MixPlugins[i].fDryRatio)); // Default program mpt::IO::WriteRaw(f, "PROG", 4); // PROG chunk does not include a size, so better make sure we always write 4 bytes here. - STATIC_ASSERT(sizeof(m_MixPlugins[i].defaultProgram) == sizeof(int32)); + 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? @@ -2013,11 +2033,11 @@ bool CSoundFile::LoadMixPlugins(FileReader &file) char code[4]; file.ReadArray(code); const uint32 chunkSize = file.ReadUint32LE(); - if(!memcmp(code, "IMPI", 4) // IT instrument, we definitely read too far - || !memcmp(code, "IMPS", 4) // IT sample, ditto - || !memcmp(code, "XTPM", 4) // Instrument extensions, ditto - || !memcmp(code, "STPM", 4) // Song extensions, ditto - || !file.CanRead(chunkSize)) + if(!memcmp(code, "IMPI", 4) // IT instrument, we definitely read too far + || !memcmp(code, "IMPS", 4) // IT sample, ditto + || !memcmp(code, "XTPM", 4) // Instrument extensions, ditto + || !memcmp(code, "STPM", 4) // Song extensions, ditto + || !file.CanRead(chunkSize)) { file.SkipBack(8); return isBeRoTracker; @@ -2027,9 +2047,9 @@ bool CSoundFile::LoadMixPlugins(FileReader &file) // Channel FX if(!memcmp(code, "CHFX", 4)) { - for (size_t ch = 0; ch < MAX_BASECHANNELS; ch++) + for(auto &chn : ChnSettings) { - ChnSettings[ch].nMixPlugin = static_cast(chunk.ReadUint32LE()); + chn.nMixPlugin = static_cast(chunk.ReadUint32LE()); } #ifndef NO_PLUGINS } @@ -2061,8 +2081,8 @@ void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin) { // MPT's standard plugin data. Size not specified in file.. grrr.. file.ReadStruct(plugin.Info); - mpt::String::SetNullTerminator(plugin.Info.szName); - mpt::String::SetNullTerminator(plugin.Info.szLibraryName); + mpt::String::SetNullTerminator(plugin.Info.szName.buf); + mpt::String::SetNullTerminator(plugin.Info.szLibraryName.buf); plugin.editorX = plugin.editorY = int32_min; // Plugin user data @@ -2215,7 +2235,7 @@ void CSoundFile::SaveExtendedSongProperties(std::ostream &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(MagicLE("CUES"), 2 + CountOf(sample.cues) * 4); + WRITEMODULARHEADER(MagicLE("CUES"), static_cast(2 + std::size(sample.cues) * 4)); mpt::IO::WriteIntLE(f, smp); for(auto cue : sample.cues) { @@ -2228,7 +2248,7 @@ void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const // Tempo Swing Factors if(!m_tempoSwing.empty()) { - mpt::ostringstream oStrm; + std::ostringstream oStrm; TempoSwing::Serialize(oStrm, m_tempoSwing); std::string data = oStrm.str(); uint16 length = mpt::saturate_cast(data.size()); @@ -2256,7 +2276,7 @@ void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const if(!m_songArtist.empty() && specs.hasArtistName) { - std::string songArtistU8 = mpt::ToCharset(mpt::CharsetUTF8, m_songArtist); + std::string songArtistU8 = mpt::ToCharset(mpt::Charset::UTF8, m_songArtist); uint16 length = mpt::saturate_cast(songArtistU8.length()); WRITEMODULARHEADER(MagicLE("AUTH"), length); mpt::IO::WriteRaw(f, songArtistU8.c_str(), length); @@ -2296,7 +2316,7 @@ void ReadField(FileReader &chunk, std::size_t size, T &field) template void ReadFieldCast(FileReader &chunk, std::size_t size, T &field) { - STATIC_ASSERT(sizeof(T) <= sizeof(int32)); + static_assert(sizeof(T) <= sizeof(int32)); field = static_cast(chunk.ReadSizedIntLE(size)); } @@ -2359,19 +2379,19 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel { std::string artist; chunk.ReadString(artist, chunk.GetLength()); - m_songArtist = mpt::ToUnicode(mpt::CharsetUTF8, artist); + m_songArtist = mpt::ToUnicode(mpt::Charset::UTF8, artist); } break; case MagicBE("ChnS"): // Channel settings for channels 65+ if(size <= (MAX_BASECHANNELS - 64) * 2 && (size % 2u) == 0) { - STATIC_ASSERT(CountOf(ChnSettings) >= 64); - const CHANNELINDEX loopLimit = std::min(uint16(64 + size / 2), uint16(CountOf(ChnSettings))); + static_assert(CountOf(ChnSettings) >= 64); + const CHANNELINDEX loopLimit = std::min(uint16(64 + size / 2), uint16(std::size(ChnSettings))); for(CHANNELINDEX chn = 64; chn < loopLimit; chn++) { - uint8 pan = chunk.ReadUint8(), vol = chunk.ReadUint8(); + auto [pan, vol] = chunk.ReadArray(); if(pan != 0xFF) { ChnSettings[chn].nVolume = vol; @@ -2406,7 +2426,7 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel // Tempo Swing Factors if(size > 2) { - mpt::istringstream iStrm(chunk.ReadRawDataAsString()); + std::istringstream iStrm(chunk.ReadRawDataAsString()); TempoSwing::Deserialize(iStrm, m_tempoSwing, chunk.GetLength()); } break; @@ -2430,12 +2450,10 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel } break; } - } // Validate read values. Limit(m_nDefaultTempo, GetModSpecifications().GetTempoMin(), GetModSpecifications().GetTempoMax()); - if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; if(m_nTempoMode >= tempoModeMax) m_nTempoMode = tempoModeClassic; if(m_nMixLevels >= mixLevelsMax) m_nMixLevels = mixLevelsOriginal; //m_dwCreatedWithVersion diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp index 7c4f982b6..db85fee72 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp @@ -47,7 +47,7 @@ struct ITPModCommand operator ModCommand() const { ModCommand result; - result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? note : NOTE_NONE; + result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? static_cast(note.get()) : static_cast(NOTE_NONE); result.instr = instr; result.command = (command < MAX_EFFECTS) ? static_cast(command.get()) : CMD_NONE; result.volcmd = (volcmd < MAX_VOLCMDS) ? static_cast(volcmd.get()) : VOLCMD_NONE; @@ -237,9 +237,9 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) instrPaths[ins] = mpt::PathString::FromUTF8(path); } #ifdef MODPLUG_TRACKER - if(!file.GetFileName().empty()) + if(const auto fileName = file.GetFileName(); !fileName.empty()) { - instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(file.GetFileName().GetPath()); + instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(fileName.GetPath()); } else if(GetpModDoc() != nullptr) { instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath()); @@ -327,7 +327,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) && !memcmp(sampleHeader.id, "IMPS", 4)) { sampleHeader.ConvertToMPT(Samples[realSample]); - mpt::String::Read(m_szNames[realSample], sampleHeader.name); + m_szNames[realSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); // Read sample data sampleHeader.GetSampleFormat().ReadSample(Samples[realSample], sampleData); @@ -341,7 +341,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) continue; #ifdef MPT_EXTERNAL_SAMPLES - InputFile f(instrPaths[ins]); + InputFile f(instrPaths[ins], SettingCacheCompleteFileBeforeLoading()); FileReader instrFile = GetFileReader(f); if(!ReadInstrumentFromFile(ins + 1, instrFile, true)) { @@ -390,7 +390,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) m_nMinPeriod = 8; // Before OpenMPT 1.20.01.09, the MIDI macros were always read from the file, even if the "embed" flag was not set. - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1,20,01,09) && !(songFlags & ITP_EMBEDMIDICFG)) + if(m_dwLastSavedWithVersion >= MPT_V("1.20.01.09") && !(songFlags & ITP_EMBEDMIDICFG)) { m_MidiCfg.Reset(); } @@ -398,7 +398,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::Windows1252; 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 6401aa334..e383294b3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp @@ -154,9 +154,9 @@ enum }; -static const VibratoType MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE }; +static constexpr VibratoType MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE }; -static const ModCommand::COMMAND MDLEffTrans[] = +static constexpr ModCommand::COMMAND MDLEffTrans[] = { /* 0 */ CMD_NONE, /* 1st column only */ @@ -187,7 +187,7 @@ static const ModCommand::COMMAND MDLEffTrans[] = // receive an MDL effect, give back a 'normal' one. -static void ConvertMDLCommand(uint8_t &cmd, uint8_t ¶m) +static void ConvertMDLCommand(uint8 &cmd, uint8 ¶m) { if(cmd >= CountOf(MDLEffTrans)) return; @@ -224,11 +224,11 @@ static void ConvertMDLCommand(uint8_t &cmd, uint8_t ¶m) break; case 0x1: // Pan Slide Left cmd = CMD_PANNINGSLIDE; - param = (std::min(param & 0x0F, 0x0E) << 4) | 0x0F; + param = (std::min(static_cast(param & 0x0F), uint8(0x0E)) << 4) | 0x0F; break; case 0x2: // Pan Slide Right cmd = CMD_PANNINGSLIDE; - param = 0xF0 | std::min(param & 0x0F, 0x0E); + param = 0xF0 | std::min(static_cast(param & 0x0F), uint8(0x0E)); break; case 0x4: // Vibrato Waveform param = 0x30 | (param & 0x0F); @@ -361,7 +361,7 @@ static bool ImportMDLCommands(ModCommand &m, uint8 vol, uint8 e1, uint8 e2, uint e1 = CMD_NONE; } else if(!vol) { - lostCommand |= !ModCommand::TwoRegularCommandsToMPT(e1, p1, e2, p2); + lostCommand |= (ModCommand::TwoRegularCommandsToMPT(e1, p1, e2, p2).first != CMD_NONE); m.volcmd = e1; m.vol = p1; } else @@ -473,14 +473,10 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) : (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; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, info.title); - { - std::string artist; - mpt::String::Read(artist, info.composer); - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437, artist); - } + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, info.title); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, info.composer)); m_nDefaultGlobalVolume = info.globalVol + 1; m_nDefaultSpeed = Clamp(info.speed, 1, 255); @@ -582,8 +578,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) uint8 numInstruments = chunk.ReadUint8(); for(uint8 i = 0; i < numInstruments; i++) { - uint8 ins = chunk.ReadUint8(); - uint8 numSamples = chunk.ReadUint8(); + const auto [ins, numSamples] = chunk.ReadArray(); uint8 firstNote = 0; ModInstrument *mptIns = nullptr; if(ins == 0 @@ -595,27 +590,14 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) } chunk.ReadString(mptIns->name, 32); - while(numSamples--) + for(uint8 smp = 0; smp < numSamples; smp++) { MDLSampleHeader sampleHeader; chunk.ReadStruct(sampleHeader); - if(sampleHeader.smpNum == 0) + if(sampleHeader.smpNum == 0 || sampleHeader.smpNum > GetNumSamples()) continue; - #if 1 - #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; - #endif - LimitMax(sampleHeader.lastNote, static_cast(CountOf(mptIns->Keyboard))); + LimitMax(sampleHeader.lastNote, static_cast(std::size(mptIns->Keyboard))); for(uint8 n = firstNote; n <= sampleHeader.lastNote; n++) { mptIns->Keyboard[n] = sampleHeader.smpNum; @@ -650,7 +632,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) mptSmp.nVolume = sampleHeader.volume; else mptSmp.uFlags.set(SMP_NODEFAULTVOLUME); - mptSmp.nPan = std::min(sampleHeader.panning * 2, 254); + mptSmp.nPan = std::min(static_cast(sampleHeader.panning * 2), uint16(254)); mptSmp.nVibType = MDLVibratoType[sampleHeader.vibType & 3]; mptSmp.nVibSweep = sampleHeader.vibSweep; mptSmp.nVibDepth = sampleHeader.vibDepth; @@ -703,14 +685,14 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) { CHANNELINDEX numChans = 32; ROWINDEX numRows = 64; - char name[17] = ""; + std::string name; if(fileHeader.version >= 0x10) { MDLPatternHeader patHead; chunk.ReadStruct(patHead); numChans = patHead.channels; numRows = patHead.lastRow + 1; - mpt::String::Read(name, patHead.name); + name = mpt::String::ReadBuf(mpt::String::spacePadded, patHead.name); } if(!Patterns.Insert(pat, numRows)) @@ -766,7 +748,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) if(x & MDLNOTE_NOTE) { b = track.ReadUint8(); - m->note = (b > 120) ? NOTE_KEYOFF : b; + m->note = (b > 120) ? static_cast(NOTE_KEYOFF) : static_cast(b); } if(x & MDLNOTE_SAMPLE) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp index 64d1ec257..82f69a15b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp @@ -2,502 +2,625 @@ * Load_med.cpp * ------------ * Purpose: OctaMED / MED Soundstudio module loader - * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs + * Notes : Support for synthesized instruments is still missing. + * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ - #include "stdafx.h" #include "Loaders.h" -#include "../common/mptStringBuffer.h" + +#ifndef NO_VST +#include "../mptrack/Vstplug.h" +#include "plugins/PluginManager.h" +#endif + +#include OPENMPT_NAMESPACE_BEGIN -//#define MED_LOG - -#define MED_MAX_COMMENT_LENGTH 5*1024 //: Is 5 kB enough? - -// flags -#define MMD_FLAG_FILTERON 0x1 -#define MMD_FLAG_JUMPINGON 0x2 -#define MMD_FLAG_JUMP8TH 0x4 -#define MMD_FLAG_INSTRSATT 0x8 // instruments are attached (this is a module) -#define MMD_FLAG_VOLHEX 0x10 -#define MMD_FLAG_STSLIDE 0x20 // SoundTracker mode for slides -#define MMD_FLAG_8CHANNEL 0x40 // OctaMED 8 channel song -#define MMD_FLAG_SLOWHQ 0x80 // HQ slows playing speed (V2-V4 compatibility) -// flags2 -#define MMD_FLAG2_BMASK 0x1F -#define MMD_FLAG2_BPM 0x20 -#define MMD_FLAG2_MIX 0x80 // uses Mixing (V7+) -// flags3: -#define MMD_FLAG3_STEREO 0x1 // mixing in Stereo mode -#define MMD_FLAG3_FREEPAN 0x2 // free panning -#define MMD_FLAG3_GM 0x4 // module designed for GM/XG compatibility - - -// generic MMD tags -#define MMDTAG_END 0 -#define MMDTAG_PTR 0x80000000 // data needs relocation -#define MMDTAG_MUSTKNOW 0x40000000 // loader must fail if this isn't recognized -#define MMDTAG_MUSTWARN 0x20000000 // loader must warn if this isn't recognized - -// ExpData tags -// # of effect groups, including the global group (will -// override settings in MMDSong struct), default = 1 -#define MMDTAG_EXP_NUMFXGROUPS 1 -#define MMDTAG_TRK_NAME (MMDTAG_PTR|1) // trackinfo tags -#define MMDTAG_TRK_NAMELEN 2 // namelen includes zero term. -#define MMDTAG_TRK_FXGROUP 3 -// effectinfo tags -#define MMDTAG_FX_ECHOTYPE 1 -#define MMDTAG_FX_ECHOLEN 2 -#define MMDTAG_FX_ECHODEPTH 3 -#define MMDTAG_FX_STEREOSEP 4 -#define MMDTAG_FX_GROUPNAME (MMDTAG_PTR|5) // the Global Effects group shouldn't have name saved! -#define MMDTAG_FX_GRPNAMELEN 6 // namelen includes zero term. - - -struct MEDMODULEHEADER +struct MMD0FileHeader { - char id[4]; // MMD1-MMD3 - uint32be modlen; // Size of file - uint32be song; // Position in file for this song - uint16be psecnum; - uint16be pseq; - uint32be blockarr; // Position in file for blocks - uint32be mmdflags; - uint32be smplarr; // Position in file for samples - uint32be reserved; - uint32be expdata; // Absolute offset in file for ExpData (0 if not present) + char mmd[3]; // "MMD" for the first song in file, "MCN" for the rest + uint8be version; // '0'-'3' + uint32be modLength; // Size of file + uint32be songOffset; // Position in file for the first song + uint16be playerSettings1[2]; // Internal variables for the play routine + uint32be blockArrOffset; // Position in file for blocks (patterns) + uint8be flags; + uint8be reserved1[3]; + uint32be sampleArrOffset; // Position in file for samples (should be identical between songs) uint32be reserved2; - uint16be pstate; - uint16be pblock; - uint16be pline; - uint16be pseqnum; - uint16be actplayline; - uint8be counter; - uint8be extra_songs; // # of songs - 1 + uint32be expDataOffset; // Absolute offset in file for ExpData (0 if not present) + uint32be reserved3; + char playerSettings2[11]; // Internal variables for the play routine + uint8be extraSongs; // Number of songs - 1 }; -MPT_BINARY_STRUCT(MEDMODULEHEADER, 52) +MPT_BINARY_STRUCT(MMD0FileHeader, 52) -struct MMD0SAMPLE +struct MMD0Sample { - uint16be rep, replen; - uint8be midich; - uint8be midipreset; - uint8be svol; - int8be strans; + uint16be loopStart; + uint16be loopLength; + uint8be midiChannel; + uint8be midiPreset; + uint8be sampleVolume; + int8be sampleTranspose; }; -MPT_BINARY_STRUCT(MMD0SAMPLE, 8) +MPT_BINARY_STRUCT(MMD0Sample, 8) -// Sample header is immediately followed by sample data... -struct MMDSAMPLEHEADER +// Song header for MMD0/MMD1 +struct MMD0Song { - uint32be length; // length of *one* *unpacked* channel in *bytes* - uint16be type; - // if non-negative - // bits 0-3 reserved for multi-octave instruments, not supported on the PC - // 0x10: 16 bit (otherwise 8 bit) - // 0x20: Stereo (otherwise mono) - // 0x40: Uses DeltaCode - // 0x80: Packed data - // -1: Synth - // -2: Hybrid - // if type indicates packed data, these fields follow, otherwise we go right to the data - uint16be packtype; // Only 1 = ADPCM is supported - uint16be subtype; // Packing subtype - // ADPCM subtype - // 1: g723_40 - // 2: g721 - // 3: g723_24 - uint8be commonflags; // flags common to all packtypes (none defined so far) - uint8be packerflags; // flags for the specific packtype - uint32be leftchlen; // packed length of left channel in bytes - uint32be rightchlen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES) - uint8be SampleData[1]; // Sample Data + uint8be sequence[256]; }; -MPT_BINARY_STRUCT(MMDSAMPLEHEADER, 21) +MPT_BINARY_STRUCT(MMD0Song, 256) -// MMD0/MMD1 song header -struct MMD0SONGHEADER +// Song header for MMD2/MMD3 +struct MMD2Song { - MMD0SAMPLE sample[63]; - uint16be numblocks; // # of blocks - uint16be songlen; // # of entries used in playseq - uint8be playseq[256]; // Play sequence - uint16be deftempo; // BPM tempo - int8be playtransp; // Play transpose - uint8be flags; // 0x10: Hex Volumes | 0x20: ST/NT/PT Slides | 0x40: 8 Channels song - uint8be flags2; // [b4-b0]+1: Tempo LPB, 0x20: tempo mode, 0x80: mix_conv=on - uint8be tempo2; // tempo TPL - uint8be trkvol[16]; // track volumes - uint8be mastervol; // master volume - uint8be numsamples; // # of samples (max=63) + enum Flags3 + { + FLAG3_STEREO = 0x01, // Mixing in stereo + FLAG3_FREEPAN = 0x02, // Mixing flag: free pan + }; + + uint32be playSeqTableOffset; + uint32be sectionTableOffset; + uint32be trackVolsOffset; + uint16be numTracks; + uint16be numPlaySeqs; + uint32be trackPanOffset; // 0: all centered (according to docs, MED Soundstudio uses Amiga hard-panning instead) + uint32be flags3; + uint16be volAdjust; // Volume adjust (%) + uint16be mixChannels; // Mixing channels, 0 means 4 + uint8be mixEchoType; // 0 = nothing, 1 = normal, 2 = cross + uint8be mixEchoDepth; // 1 - 6, 0 = default + uint16be mixEchoLength; // Echo length in milliseconds + int8be mixStereoSep; // Stereo separation + char pad0[223]; }; -MPT_BINARY_STRUCT(MMD0SONGHEADER, 788) +MPT_BINARY_STRUCT(MMD2Song, 256) -// MMD2/MMD3 song header -struct MMD2SONGHEADER +// Common song header +struct MMDSong { - MMD0SAMPLE sample[63]; - uint16be numblocks; // # of blocks - uint16be numsections; // # of sections - uint32be playseqtable; // filepos of play sequence - uint32be sectiontable; // filepos of sections table (uint16_be array) - uint32be trackvols; // filepos of tracks volume (uint8_be array) - uint16be numtracks; // # of tracks (max 64) - uint16be numpseqs; // # of play sequences - uint32be trackpans; // filepos of tracks pan values (uint8_be array) - int32be flags3; // 0x1:stereo_mix, 0x2:free_panning, 0x4:GM/XG compatibility - uint16be voladj; // vol_adjust (set to 100 if 0) - uint16be channels; // # of channels (4 if =0) - uint8be mix_echotype; // 1:normal,2:xecho - uint8be mix_echodepth; // 1..6 - uint16be mix_echolen; // > 0 - int8be mix_stereosep; // -4..4 - uint8be pad0[223]; - uint16be deftempo; // BPM tempo - int8be playtransp; // play transpose - uint8be flags; // 0x1:filteron, 0x2:jumpingon, 0x4:jump8th, 0x8:instr_attached, 0x10:hex_vol, 0x20:PT_slides, 0x40:8ch_conv,0x80:hq slows playing speed - uint8be flags2; // 0x80:mix_conv=on, [b4-b0]+1:tempo LPB, 0x20:tempo_mode - uint8be tempo2; // tempo TPL - uint8be pad1[16]; - uint8be mastervol; // master volume - uint8be numsamples; // # of samples (max 63) + enum Flags + { + FLAG_FILTERON = 0x01, // The hardware audio filter is on + FLAG_JUMPINGON = 0x02, // Mouse pointer jumping on + FLAG_JUMP8TH = 0x04, // ump every 8th line (not in OctaMED Pro) + FLAG_INSTRSATT = 0x08, // sng+samples indicator (not useful in MMDs) + FLAG_VOLHEX = 0x10, // volumes are HEX + FLAG_STSLIDE = 0x20, // use ST/NT/PT compatible sliding + FLAG_8CHANNEL = 0x40, // this is OctaMED 5-8 channel song + FLAG_SLOWHQ = 0x80, // HQ V2-4 compatibility mode + }; + + enum Flags2 + { + FLAG2_BMASK = 0x1F, // (bits 0-4) BPM beat length (in lines) + FLAG2_BPM = 0x20, // BPM mode on + FLAG2_MIX = 0x80, // Module uses mixing + }; + + uint16be numBlocks; // Number of blocks in current song + uint16be songLength; // MMD0: Number of sequence numbers in the play sequence list, MMD2: Number of sections + char song[256]; + MMD0Song GetMMD0Song() const + { + static_assert(sizeof(MMD0Song) == sizeof(song)); + MMD0Song result; + std::memcpy(&result, song, sizeof(result)); + return result; + } + MMD2Song GetMMD2Song() const + { + static_assert(sizeof(MMD2Song) == sizeof(song)); + MMD2Song result; + std::memcpy(&result, song, sizeof(result)); + return result; + } + uint16be defaultTempo; + int8be playTranspose; // The global play transpose value for current song + uint8be flags; + uint8be flags2; + uint8be tempo2; // Timing pulses per line (ticks) + uint8be trackVol[16]; // 1...64 in MMD0/MMD1, reserved in MMD2 + uint8be masterVol; // 1...64 + uint8be numSamples; }; -MPT_BINARY_STRUCT(MMD2SONGHEADER, 788) +MPT_BINARY_STRUCT(MMDSong, 284) -// For MMD0 the note information is held in 3 bytes, byte0, byte1, byte2. For reference we -// number the bits in each byte 0..7, where 0 is the low bit. -// The note is held as bits 5..0 of byte0 -// The instrument is encoded in 6 bits, bits 7 and 6 of byte0 and bits 7,6,5,4 of byte1 -// The command number is bits 3,2,1,0 of byte1, command data is in byte2: -// For command 0, byte2 represents the second data byte, otherwise byte2 -// represents the first data byte. -struct MMD0BLOCK -{ - uint8be numtracks; - uint8be lines; // File value is 1 less than actual, so 0 -> 1 line -}; // uint8_be data[lines+1][tracks][3]; - -MPT_BINARY_STRUCT(MMD0BLOCK, 2) - - -// For MMD1,MMD2,MMD3 the note information is carried in 4 bytes, byte0, byte1, -// byte2 and byte3 -// The note is held as byte0 (values above 0x84 are ignored) -// The instrument is held as byte1 -// The command number is held as byte2, command data is in byte3 -// For commands 0 and 0x19 byte3 represents the second data byte, -// otherwise byte2 represents the first data byte. -struct MMD1BLOCK -{ - uint16be numtracks; // Number of tracks, may be > 64, but then that data is skipped. - uint16be lines; // Stored value is 1 less than actual, so 0 -> 1 line - uint32be info; // Offset of BlockInfo (if 0, no block_info is present) -}; - -MPT_BINARY_STRUCT(MMD1BLOCK, 8) - - -struct MMD1BLOCKINFO -{ - uint32be hlmask; // Unimplemented - ignore - uint32be blockname; // file offset of block name - uint32be blocknamelen; // length of block name (including term. 0) - uint32be pagetable; // file offset of command page table - uint32be cmdexttable; // file offset of command extension table - uint32be reserved[4]; // future expansion -}; - -MPT_BINARY_STRUCT(MMD1BLOCKINFO, 36) - - -// A set of play sequences is stored as an array of uint32_be files offsets -// Each offset points to the play sequence itself. -struct MMD2PLAYSEQ +struct MMD2PlaySeq { char name[32]; - uint32be command_offs; // filepos of command table + uint32be commandTableOffset; uint32be reserved; - uint16be length; - uint16be seq[512]; // skip if > 0x8000 + uint16be length; // Number of entries }; -MPT_BINARY_STRUCT(MMD2PLAYSEQ, 1066) +MPT_BINARY_STRUCT(MMD2PlaySeq, 42) -// A command table contains commands that effect a particular play sequence -// entry. The only commands read in are STOP or POSJUMP, all others are ignored -// POSJUMP is presumed to have extra bytes containing a uint16 for the position -struct MMDCOMMAND +struct MMD0PatternHeader { - uint16be offset; // Offset within current sequence entry - uint8be cmdnumber; // STOP (537) or POSJUMP (538) (others skipped) - uint8be extra_count; - uint8be extra_bytes[4]; // [extra_count]; -}; // Last entry has offset == 0xFFFF, cmd_number == 0 and 0 extrabytes - -MPT_BINARY_STRUCT(MMDCOMMAND, 8) - - -struct MMD0EXP -{ - uint32be nextmod; // File offset of next Hdr - uint32be exp_smp; // Pointer to extra instrument data - uint16be s_ext_entries; // Number of extra instrument entries - uint16be s_ext_entrsz; // Size of extra instrument data - uint32be annotxt; - uint32be annolen; - uint32be iinfo; // Instrument names - uint16be i_ext_entries; - uint16be i_ext_entrsz; - uint32be jumpmask; - uint32be rgbtable; - uint8be channelsplit[4]; // Only used if 8ch_conv (extra channel for every nonzero entry) - uint32be n_info; - uint32be songname; // Song name - uint32be songnamelen; - uint32be dumps; - uint32be mmdinfo; - uint32be mmdrexx; - uint32be mmdcmd3x; - uint32be trackinfo_ofs; // ptr to song->numtracks ptrs to tag lists - uint32be effectinfo_ofs; // ptr to group ptrs - uint32be tag_end; + uint8be numTracks; + uint8be numRows; }; -MPT_BINARY_STRUCT(MMD0EXP, 80) +MPT_BINARY_STRUCT(MMD0PatternHeader, 2) -static const uint8 bpmvals[9] = { 179,164,152,141,131,123,116,110,104}; - -static void MedConvert(ModCommand &p, const MMD0SONGHEADER *pmsh) +struct MMD1PatternHeader { - ModCommand::COMMAND command = p.command; - uint32 param = p.param; - switch(command) + uint16be numTracks; + uint16be numRows; + uint32be blockInfoOffset; +}; + +MPT_BINARY_STRUCT(MMD1PatternHeader, 8) + + +struct MMDPlaySeqCommand +{ + enum Command { - case 0x00: if (param) command = CMD_ARPEGGIO; else command = CMD_NONE; break; - case 0x01: command = CMD_PORTAMENTOUP; break; - case 0x02: command = CMD_PORTAMENTODOWN; break; - case 0x03: command = CMD_TONEPORTAMENTO; break; - case 0x04: command = CMD_VIBRATO; break; - case 0x05: command = CMD_TONEPORTAVOL; break; - case 0x06: command = CMD_VIBRATOVOL; break; - case 0x07: command = CMD_TREMOLO; break; - case 0x0A: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = CMD_NONE; break; - case 0x0B: command = CMD_POSITIONJUMP; break; - case 0x0C: command = CMD_VOLUME; - if (pmsh->flags & MMD_FLAG_VOLHEX) - { - if (param < 0x80) - { - param = (param+1) / 2; - } else command = CMD_NONE; - } else - { - if (param <= 0x99) - { - param = (param >> 4)*10+((param & 0x0F) % 10); - if (param > 64) param = 64; - } else command = CMD_NONE; - } - break; - case 0x09: command = static_cast((param <= 0x20) ? CMD_SPEED : CMD_TEMPO); break; - case 0x0D: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = CMD_NONE; break; - case 0x0F: // Set Tempo / Special - // F.00 = Pattern Break - if (!param) command = CMD_PATTERNBREAK; else - // F.01 - F.F0: Set tempo/speed - if (param <= 0xF0) - { - if (pmsh->flags & MMD_FLAG_8CHANNEL) - { - param = (param == 0 || param >= 10) ? 99 : bpmvals[param-1]; - } else - // F.01 - F.0A: Set Speed - if (param <= 0x0A) - { - command = CMD_SPEED; - } else - // Old tempo - if (!(pmsh->flags2 & MMD_FLAG2_BPM)) - { - param = Util::muldiv(param, 5*715909, 2*474326); - } - // F.0B - F.F0: Set Tempo (assumes LPB=4) - if (param > 0x0A) - { - command = CMD_TEMPO; - if (param < 0x21) param = 0x21; - if (param > 240) param = 240; - } - } else - switch(param) - { - // F.F1: Retrig 2x - case 0xF1: - command = CMD_MODCMDEX; - param = 0x93; - break; - // F.F2: Note Delay 2x - case 0xF2: - command = CMD_MODCMDEX; - param = 0xD3; - break; - // F.F3: Retrig 3x - case 0xF3: - command = CMD_MODCMDEX; - param = 0x92; - break; - // F.F4: Note Delay 1/3 - case 0xF4: - command = CMD_MODCMDEX; - param = 0xD2; - break; - // F.F5: Note Delay 2/3 - case 0xF5: - command = CMD_MODCMDEX; - param = 0xD4; - break; - // F.F8: Filter Off - case 0xF8: - command = CMD_MODCMDEX; - param = 0x00; - break; - // F.F9: Filter On - case 0xF9: - command = CMD_MODCMDEX; - param = 0x01; - break; - // F.FD: Very fast tone-portamento - case 0xFD: - command = CMD_TONEPORTAMENTO; - param = 0xFF; - break; - // F.FE: End Song - case 0xFE: - command = CMD_SPEED; - param = 0; - break; - // F.FF: Note Cut - case 0xFF: - command = CMD_MODCMDEX; - param = 0xC0; - break; - default: -#ifdef MED_LOG - Log("Unknown Fxx command: cmd=0x%02X param=0x%02X\n", command, param); -#endif - command = CMD_NONE; - param = 0; - } - break; - // 11.0x: Fine Slide Up - case 0x11: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x10; - break; - // 12.0x: Fine Slide Down - case 0x12: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x20; - break; - // 14.xx: Vibrato - case 0x14: - command = CMD_VIBRATO; - break; - // 15.xx: FineTune - case 0x15: - command = CMD_MODCMDEX; - param &= 0x0F; - param |= 0x50; - break; - // 16.xx: Pattern Loop - case 0x16: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x60; - break; - // 18.xx: Note Cut - case 0x18: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xC0; - break; - // 19.xx: Sample Offset - case 0x19: - command = CMD_OFFSET; - break; - // 1A.0x: Fine Volume Up - case 0x1A: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xA0; - break; - // 1B.0x: Fine Volume Down - case 0x1B: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xB0; - break; - // 1D.xx: Pattern Break - case 0x1D: - command = CMD_PATTERNBREAK; - break; - // 1E.0x: Pattern Delay - case 0x1E: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xE0; - break; - // 1F.xy: Retrig - case 0x1F: - command = CMD_RETRIG; - param &= 0x0F; - break; - // 2E.xx: set panning - case 0x2E: - command = CMD_MODCMDEX; - param = ((param + 0x10) & 0xFF) >> 1; - if (param > 0x0F) param = 0x0F; - param |= 0x80; - break; - default: -#ifdef MED_LOG - // 0x2E ? - Log("Unknown command: cmd=0x%02X param=0x%02X\n", command, param); -#endif - command = CMD_NONE; - param = 0; + kStop = 1, + kJump = 2, + }; + + uint16be offset; // Offset within current play sequence, 0xFFFF = end of list + uint8be command; // Stop = 1, Jump = 2 + uint8be extraSize; +}; + +MPT_BINARY_STRUCT(MMDPlaySeqCommand, 4) + + +struct MMDBlockInfo +{ + uint32be highlightMaskOffset; + uint32be nameOffset; + uint32be nameLength; + uint32be pageTableOffset; // File offset of command page table + uint32be cmdExtTableOffset; // File offset of command extension table (second parameter) + uint32be reserved[4]; +}; + +MPT_BINARY_STRUCT(MMDBlockInfo, 36) + + +struct MMDInstrHeader +{ + enum Types + { + VSTI = -4, + HIGHLIFE = -3, + HYBRID = -2, + SYNTHETIC = -1, + SAMPLE = 0, // an ordinary 1-octave sample (or MIDI) + IFF5OCT = 1, // 5 octaves + IFF3OCT = 2, // 3 octaves + // The following ones are recognized by OctaMED Pro only + IFF2OCT = 3, // 2 octaves + IFF4OCT = 4, // 4 octaves + IFF6OCT = 5, // 6 octaves + IFF7OCT = 6, // 7 octaves + // OctaMED Pro V5 + later + EXTSAMPLE = 7, // two extra-low octaves + + TYPEMASK = 0x0F, + + S_16 = 0x10, + STEREO = 0x20, + DELTA = 0x40, + PACKED = 0x80, // MMDPackedSampleHeader follows + OBSOLETE_MD16 = 0x18, + }; + + uint32be length; + int16be type; +}; + +MPT_BINARY_STRUCT(MMDInstrHeader, 6) + + +struct MMDPackedSampleHeader +{ + uint16be packType; // Only 1 = ADPCM is supported + uint16be subType; // Packing subtype + // ADPCM subtype + // 1: g723_40 + // 2: g721 + // 3: g723_24 + uint8be commonFlags; // flags common to all packtypes (none defined so far) + uint8be packerFlags; // flags for the specific packtype + uint32be leftChLen; // packed length of left channel in bytes + uint32be rightChLen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES) +}; + +MPT_BINARY_STRUCT(MMDPackedSampleHeader, 14) + + +struct MMDInstrExt +{ + enum + { + SSFLG_LOOP = 0x01, // Loop On / Off + SSFLG_EXTPSET = 0x02, // Ext.Preset + SSFLG_DISABLED = 0x04, // Disabled + SSFLG_PINGPONG = 0x08, // Ping-pong looping + }; + + uint8be hold; // 0...127 + uint8be decay; // 0...127 + uint8be suppressMidiOff; + int8be finetune; + // Below fields saved by >= V5 + uint8be defaultPitch; + uint8be instrFlags; + uint16be longMidiPreset; + // Below fields saved by >= V5.02 + uint8be outputDevice; + uint8be reserved; + // Below fields saved by >= V7 + uint32be loopStart; + uint32be loopLength; +}; + +MPT_BINARY_STRUCT(MMDInstrExt, 18) + + +struct MMDInstrInfo +{ + char name[40]; +}; + +MPT_BINARY_STRUCT(MMDInstrInfo, 40) + + +struct MMD0Exp +{ + uint32be nextModOffset; + uint32be instrExtOffset; + uint16be instrExtEntries; + uint16be instrExtEntrySize; + uint32be annoText; + uint32be annoLength; + uint32be instrInfoOffset; + uint16be instrInfoEntries; + uint16be instrInfoEntrySize; + uint32be jumpMask; + uint32be rgbTable; + uint8be channelSplit[4]; + uint32be notationInfoOffset; + uint32be songNameOffset; + uint32be songNameLength; + uint32be midiDumpOffset; + uint32be mmdInfoOffset; + uint32be arexxOffset; + uint32be midiCmd3xOffset; + uint32be trackInfoOffset; // Pointer to song->numtracks pointers to tag lists + uint32be effectInfoOffset; // Pointers to group pointers + uint32be tagEnd; +}; + +MPT_BINARY_STRUCT(MMD0Exp, 80) + + +struct MMDTag +{ + enum TagType + { + // Generic MMD tags + MMDTAG_END = 0x00000000, + MMDTAG_PTR = 0x80000000, // Data needs relocation + MMDTAG_MUSTKNOW = 0x40000000, // Loader must fail if this isn't recognized + MMDTAG_MUSTWARN = 0x20000000, // Loader must warn if this isn't recognized + MMDTAG_MASK = 0x1FFFFFFF, + + // ExpData tags + // # of effect groups, including the global group (will override settings in MMDSong struct), default = 1 + MMDTAG_EXP_NUMFXGROUPS = 1, + MMDTAG_TRK_FXGROUP = 3, + + MMDTAG_TRK_NAME = 1, // trackinfo tags + MMDTAG_TRK_NAMELEN = 2, // namelen includes zero term. + + // effectinfo tags + MMDTAG_FX_ECHOTYPE = 1, + MMDTAG_FX_ECHOLEN = 2, + MMDTAG_FX_ECHODEPTH = 3, + MMDTAG_FX_STEREOSEP = 4, + MMDTAG_FX_GROUPNAME = 5, // the Global Effects group shouldn't have name saved! + MMDTAG_FX_GRPNAMELEN = 6, // namelen includes zero term. + }; + + uint32be type; + uint32be data; +}; + +MPT_BINARY_STRUCT(MMDTag, 8) + + +static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPerBeat) +{ + if(bpmMode) + { + // You would have thought that we could use modern tempo mode here. + // Alas, the number of ticks per row still influences the tempo. :( + return TEMPO((tempo * rowsPerBeat) / 4.0); } - p.command = command; - p.param = static_cast(param); + if(is8Ch && tempo > 0) + { + LimitMax(tempo, 10u); + static constexpr uint8 tempos[10] = { 47, 43, 40, 37, 35, 32, 30, 29, 27, 26 }; + tempo = tempos[tempo - 1]; + } else if(tempo > 0 && tempo <= 10) + { + // SoundTracker compatible tempo + return TEMPO((6.0 * 1773447.0 / 14500.0) / tempo); + } + + return TEMPO(tempo / 0.264); } -static bool ValidateHeader(const MEDMODULEHEADER &pmmh) +static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rowsPerBeat, bool volHex) { - if(std::memcmp(pmmh.id, "MMD", 3) - || pmmh.id[3] < '0' || pmmh.id[3] > '3' - || pmmh.song == 0 - ) + switch(m.command) + { + case 0x04: // Vibrato (twice as deep as in ProTracker) + m.command = CMD_VIBRATO; + m.param = (std::min(m.param >> 3, 0x0F) << 4) | std::min((m.param & 0x0F) * 2, 0x0F); + break; + case 0x08: // Hold and decay + m.command = CMD_NONE; + break; + case 0x09: // Set secondary speed + if(m.param > 0 && m.param <= 20) + m.command = CMD_SPEED; + else + m.command = CMD_NONE; + break; + case 0x0C: // Set Volume + m.command = CMD_VOLUME; + if(!volHex && m.param < 0x99) + m.param = (m.param >> 4) * 10 + (m.param & 0x0F); + else if(volHex) + m.param = ((m.param & 0x7F) + 1) / 2; + else + m.command = CMD_NONE; + break; + case 0x0D: + m.command = CMD_VOLUMESLIDE; + break; + case 0x0E: // Synth jump + m.command = CMD_NONE; + break; + case 0x0F: // Misc + if(m.param == 0) + { + m.command = CMD_PATTERNBREAK; + } else if(m.param <= 0xF0) + { + m.command = CMD_TEMPO; + m.param = mpt::saturate_round(MMDTempoToBPM(m.param, is8ch, bpmMode, rowsPerBeat).ToDouble()); + } else switch(m.command) + { + case 0xF1: // Play note twice + m.command = CMD_MODCMDEX; + m.param = 0x93; + break; + case 0xF2: // Delay note + m.command = CMD_MODCMDEX; + m.param = 0xD3; + break; + case 0xF3: // Play note three times + m.command = CMD_MODCMDEX; + m.param = 0x92; + break; + case 0xF8: // Turn filter off + case 0xF9: // Turn filter on + m.command = CMD_MODCMDEX; + m.param = 0xF9 - m.param; + break; + case 0xFA: // MIDI pedal on + case 0xFB: // MIDI pedal off + case 0xFD: // Set pitch + case 0xFE: // End of song + m.command = CMD_NONE; + break; + case 0xFF: // Turn note off + m.note = NOTE_NOTECUT; + m.command = CMD_NONE; + break; + default: + m.command = CMD_NONE; + break; + } + break; + case 0x11: // Slide pitch up + m.command = CMD_MODCMDEX; + m.param = 0x10 | std::min(m.param, 0x0F); + break; + case 0x12: // Slide pitch down + m.command = CMD_MODCMDEX; + m.param = 0x20 | std::min(m.param, 0x0F); + break; + case 0x14: // Vibrato (ProTracker compatible depth, but faster) + m.command = CMD_VIBRATO; + m.param = (std::min((m.param >> 4) + 1, 0x0F) << 4) | (m.param & 0x0F); + break; + case 0x15: // Set finetune + m.command = CMD_MODCMDEX; + m.param = 0x50 | (m.param & 0x0F); + break; + case 0x16: // Loop + m.command = CMD_MODCMDEX; + m.param = 0x60 | std::min(m.param, 0x0F); + break; + case 0x18: // Stop note + m.command = CMD_MODCMDEX; + m.param = 0xC0 | std::min(m.param, 0x0F); + break; + case 0x19: // Sample Offset + m.command = CMD_OFFSET; + break; + case 0x1A: // Slide volume up once + m.command = CMD_MODCMDEX; + m.param = 0xA0 | std::min(m.param, 0x0F); + break; + case 0x1B: // Slide volume down once + m.command = CMD_MODCMDEX; + m.param = 0xB0 | std::min(m.param, 0x0F); + break; + case 0x1D: // Pattern break (in hex) + m.command = CMD_PATTERNBREAK; + break; + case 0x1E: // Repeat row + m.command = CMD_MODCMDEX; + m.param = 0xE0 | std::min(m.param, 0x0F); + break; + case 0x1F: // Note delay and retrigger + { + if(m.param & 0xF0) + { + m.command = CMD_MODCMDEX; + m.param = 0xD0 | (m.param >> 4); + } else if(m.param & 0x0F) + { + m.command = CMD_MODCMDEX; + m.param = 0x90 | m.param; + } else + { + m.command = CMD_NONE; + } + break; + } + case 0x20: // Reverse sample + skip samples + if(m.param == 0 && m.vol == 0) + { + m.command = CMD_S3MCMDEX; + m.param = 0x9F; + } else + { + // Skip given number of samples + m.command = CMD_NONE; + } + break; + case 0x29: // Relative sample offset + if(m.vol > 0) + { + m.command = CMD_OFFSETPERCENTAGE; + m.param = mpt::saturate_cast(Util::muldiv_unsigned(m.param, 0x100, m.vol)); + } else + { + m.command = CMD_NONE; + } + break; + case 0x2E: // Set panning + if(m.param <= 0x10 || m.param >= 0xF0) + { + m.command = CMD_PANNING8; + m.param = mpt::saturate_cast(((m.param ^ 0x80) - 0x70) * 8); + } else + { + m.command = CMD_NONE; + } + break; + default: + if(m.command < 0x10) + CSoundFile::ConvertModCommand(m); + else + m.command = CMD_NONE; + break; + } +} + +#ifndef NO_VST +static std::wstring ReadMEDStringUTF16BE(FileReader &file) +{ + auto chunk = file.ReadChunk(file.ReadUint32BE()); + std::wstring s(chunk.GetLength() / 2u, L'\0'); + for(auto &c : s) + { + c = chunk.ReadUint16BE(); + } + return s; +} +#endif + + +static void MEDReadNextSong(FileReader &file, MMD0FileHeader &fileHeader, MMD0Exp &expData, MMDSong &songHeader) +{ + file.ReadStruct(fileHeader); + file.Seek(fileHeader.songOffset + 63 * sizeof(MMD0Sample)); + file.ReadStruct(songHeader); + if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset)) + file.ReadStruct(expData); + else + expData = {}; +} + + +static CHANNELINDEX MEDScanNumChannels(FileReader &file, const uint8 version) +{ + MMD0FileHeader fileHeader; + MMD0Exp expData; + MMDSong songHeader; + + file.Rewind(); + MEDReadNextSong(file, fileHeader, expData, songHeader); + + auto numSongs = fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1; + + CHANNELINDEX numChannels = 4; + // Scan patterns for max number of channels + for(SEQUENCEINDEX song = 0; song < numSongs; song++) + { + const PATTERNINDEX numPatterns = songHeader.numBlocks; + if(songHeader.numSamples > 63 || numPatterns > 0x7FFF) + return 0; + + for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) + { + if(!file.Seek(fileHeader.blockArrOffset + pat * 4u) + || !file.Seek(file.ReadUint32BE())) + { + continue; + } + numChannels = std::max(numChannels, static_cast(version < 1 ? file.ReadUint8() : file.ReadUint16BE())); + } + + if(!expData.nextModOffset || !file.Seek(expData.nextModOffset)) + break; + MEDReadNextSong(file, fileHeader, expData, songHeader); + } + return numChannels; +} + + +static bool ValidateHeader(const MMD0FileHeader &fileHeader) +{ + if(std::memcmp(fileHeader.mmd, "MMD", 3) + || fileHeader.version < '0' || fileHeader.version > '3' + || fileHeader.songOffset < sizeof(MMD0FileHeader) + || fileHeader.songOffset > uint32_max - 63 * sizeof(MMD0Sample) - sizeof(MMDSong) + || fileHeader.blockArrOffset < sizeof(MMD0FileHeader) + || fileHeader.sampleArrOffset < sizeof(MMD0FileHeader) + || fileHeader.expDataOffset > uint32_max - sizeof(MMD0Exp)) { return false; } @@ -505,295 +628,226 @@ static bool ValidateHeader(const MEDMODULEHEADER &pmmh) } -static uint64 GetHeaderMinimumAdditionalSize(const MEDMODULEHEADER &pmmh) +static uint64 GetHeaderMinimumAdditionalSize(const MMD0FileHeader &fileHeader) { - MPT_UNREFERENCED_PARAMETER(pmmh); - return sizeof(MMD0SONGHEADER); + return std::max({ fileHeader.songOffset + 63 * sizeof(MMD0Sample) + sizeof(MMDSong), + fileHeader.blockArrOffset, + fileHeader.sampleArrOffset, + fileHeader.expDataOffset + sizeof(MMD0Exp) }) - sizeof(MMD0FileHeader); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize) { - MEDMODULEHEADER pmmh; - if(!file.ReadStruct(pmmh)) - { + MMD0FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) return ProbeWantMoreData; - } - if(!ValidateHeader(pmmh)) - { + if(!ValidateHeader(fileHeader)) return ProbeFailure; - } - return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(pmmh)); + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); - MEDMODULEHEADER pmmh; - if(!file.ReadStruct(pmmh)) - { + MMD0FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) return false; - } - if(!ValidateHeader(pmmh)) - { + if(!ValidateHeader(fileHeader)) return false; - } - if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(pmmh)))) - { + if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) return false; - } - const uint32 dwSong = pmmh.song; - if(!file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) - { - return false; - } if(loadFlags == onlyVerifyHeader) - { return true; - } - - file.Rewind(); - const FileReader::off_t dwMemLength = file.GetLength(); - const uint8 *lpStream = file.GetRawData(); - const MMD0SONGHEADER *pmsh; - const MMD2SONGHEADER *pmsh2; - const MMD0EXP *pmex; - uint32 dwBlockArr, dwSmplArr, dwExpData; - const_unaligned_ptr_be pdwTable; - int8 version = pmmh.id[3]; - uint32 deftempo; - int playtransp = 0; - + InitializeGlobals(MOD_TYPE_MED); InitializeChannels(); - // Setup channel pan positions and volume - SetupMODPanning(true); + const uint8 version = fileHeader.version - '0'; - const MPT_UCHAR_TYPE *madeWithTracker = UL_(""); - switch(version) + file.Seek(fileHeader.songOffset); + FileReader sampleHeaderChunk = file.ReadChunk(63 * sizeof(MMD0Sample)); + + MMDSong songHeader; + file.ReadStruct(songHeader); + + if(songHeader.numSamples > 63 || songHeader.numBlocks > 0x7FFF) + return false; + + MMD0Exp expData{}; + if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset)) { - 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; + file.ReadStruct(expData); } - 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; - dwSmplArr = pmmh.smplarr; - dwExpData = pmmh.expdata; - if ((dwExpData) && (dwExpData < dwMemLength - sizeof(MMD0EXP))) - pmex = (const MMD0EXP *)(lpStream+dwExpData); - else - pmex = NULL; - pmsh = (const MMD0SONGHEADER *)(lpStream + dwSong); - pmsh2 = (const MMD2SONGHEADER *)pmsh; + m_nChannels = MEDScanNumChannels(file, version); + if(m_nChannels < 1 || m_nChannels > MAX_BASECHANNELS) + return false; - uint16 wNumBlocks = pmsh->numblocks; - m_nChannels = 4; - m_nSamples = pmsh->numsamples; - if (m_nSamples > 63) m_nSamples = 63; - // Tempo - m_nDefaultTempo.Set(125); - deftempo = pmsh->deftempo; - if (!deftempo) deftempo = 125; - if (pmsh->flags2 & MMD_FLAG2_BPM) - { - uint32 tempo_tpl = (pmsh->flags2 & MMD_FLAG2_BMASK) + 1; - if (!tempo_tpl) tempo_tpl = 4; - deftempo *= tempo_tpl; - deftempo /= 4; - #ifdef MED_LOG - Log("newtempo: %3d bpm (bpm=%3d lpb=%2d)\n", deftempo, pmsh->deftempo, (pmsh->flags2 & MMD_FLAG2_BMASK)+1); - #endif - } else - { - if((pmsh->flags & MMD_FLAG_8CHANNEL) && deftempo > 0 && deftempo <= 9) - deftempo = bpmvals[deftempo-1]; - else - deftempo = Util::muldiv(deftempo, 5 * 715909, 2 * 474326); - #ifdef MED_LOG - Log("oldtempo: %3d bpm (bpm=%3d)\n", deftempo, pmsh->deftempo); - #endif - } - // Speed - m_nDefaultSpeed = pmsh->tempo2; - if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; - if (deftempo < 0x21) deftempo = 0x21; - m_nDefaultTempo.Set(deftempo); - // Reading Samples - for (uint32 iSHdr=0; iSHdrsample[iSHdr].rep * 2u; - sample.nLoopEnd = sample.nLoopStart + (pmsh->sample[iSHdr].replen * 2u); - sample.nVolume = (pmsh->sample[iSHdr].svol << 2); - sample.nGlobalVol = 64; - if (sample.nVolume > 256) sample.nVolume = 256; - // Was: sample.RelativeTone = -12 * pmsh->sample[iSHdr].strans; - // But that breaks MMD1 modules (e.g. "94' summer.mmd1" from Modland) - "automatic terminated to.mmd0" still sounds broken, probably "play transpose" is broken there. - sample.RelativeTone = pmsh->sample[iSHdr].strans; - sample.nPan = 128; - if (sample.nLoopEnd <= 2) sample.nLoopEnd = 0; - if (sample.nLoopEnd) sample.uFlags.set(CHN_LOOP); - } - // Common Flags - m_SongFlags.set(SONG_FASTVOLSLIDES, !(pmsh->flags & 0x20)); + // Start with the instruments, as those are shared between songs - // Reading play sequence - if (version < '2') + std::vector instrOffsets; + file.Seek(fileHeader.sampleArrOffset); + file.ReadVector(instrOffsets, songHeader.numSamples); + m_nInstruments = m_nSamples = songHeader.numSamples; + + // In MMD0 / MMD1, octave wrapping is only done in 4-channel modules (hardware mixing!), and not for synth instruments + // - It's required e.g. for automatic terminated to.mmd0 + // - dissociate.mmd0 (8 channels) and starkelsesirap.mmd0 (synth) on the other hand don't need it + // In MMD2 / MMD3, the mix flag is used instead. + const bool hardwareMixSamples = (version < 2 && m_nChannels == 4) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX)); + + bool needInstruments = false; + bool anySynthInstrs = false; +#ifndef NO_VST + PLUGINDEX numPlugins = 0; +#endif + for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++) { - uint32 nbo = pmsh->songlen; - if (!nbo) nbo = 1; - ReadOrderFromArray(Order(), pmsh->playseq, nbo); - playtransp = pmsh->playtransp; - } else - { - uint32 nSections; - ORDERINDEX nOrders = 0; - uint16 nTrks = pmsh2->numtracks; - if ((nTrks >= 4) && (nTrks <= 32)) m_nChannels = nTrks; - uint32 playseqtable = pmsh2->playseqtable; - uint32 numplayseqs = pmsh2->numpseqs; - if (!numplayseqs) numplayseqs = 1; - nSections = pmsh2->numsections; - uint32 sectiontable = pmsh2->sectiontable; - if ((!nSections) || (!sectiontable) || (sectiontable >= dwMemLength-2)) nSections = 1; - nOrders = 0; - Order().clear(); - for (uint32 iSection=0; iSection= dwMemLength - sectiontable) - { - nplayseq = *const_unaligned_ptr_be(lpStream + sectiontable); - sectiontable += 2; - } else - { - nSections = 0; - } - uint32 pseq = 0; + file.ReadStruct(instrHeader); + sampleChunk = file.ReadChunk(instrHeader.length); + } + const bool isSynth = instrHeader.type < 0; + const size_t maskedType = static_cast(instrHeader.type & MMDInstrHeader::TYPEMASK); - if ((playseqtable) && (playseqtable < dwMemLength) && (nplayseq * 4 <= dwMemLength - playseqtable)) +#ifndef NO_VST + if(instrHeader.type == MMDInstrHeader::VSTI) + { + needInstruments = true; + sampleChunk.Skip(6); // 00 00 + const std::wstring type = ReadMEDStringUTF16BE(sampleChunk); + const std::wstring name = ReadMEDStringUTF16BE(sampleChunk); + if(type == L"VST") { - pseq = (const_unaligned_ptr_be(lpStream+playseqtable))[nplayseq]; + auto &mixPlug = m_MixPlugins[numPlugins]; + mixPlug = {}; + mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; + mixPlug.Info.gain = 10; + mixPlug.Info.szName = mpt::ToCharset(mpt::CharsetLocaleOrUTF8, name); + mixPlug.Info.szLibraryName = mpt::ToCharset(mpt::Charset::UTF8, name); + instr.nMixPlug = numPlugins + 1; + instr.nMidiChannel = MidiFirstChannel; + instr.Transpose(-24); + instr.AssignSample(0); + // TODO: Figure out patch and routing data + + numPlugins++; } - if (pseq && pseq < dwMemLength && sizeof(MMD2PLAYSEQ) <= dwMemLength - pseq) + } else +#endif // NO_VST + if(isSynth) + { + // TODO: Figure out synth instruments + anySynthInstrs = true; + instr.AssignSample(0); + } + + uint8 numSamples = 1; + static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7}; + if(!isSynth && maskedType < std::size(SamplesPerType)) + numSamples = SamplesPerType[maskedType]; + if(numSamples > 1) + { + static_assert(MAX_SAMPLES > 63 * 9, "Check IFFOCT multisample code"); + m_nSamples += numSamples - 1; + needInstruments = true; + static constexpr uint8 OctSampleMap[][8] = { - const MMD2PLAYSEQ *pmps = (const MMD2PLAYSEQ *)(lpStream + pseq); - if(m_songName.empty()) mpt::String::Read(m_songName, pmps->name); - ORDERINDEX n = std::min(pmps->length, MAX_ORDERS - nOrders); - if (n <= (dwMemLength - pseq + 42) / 2u && n < MPT_ARRAY_COUNT(pmps->seq)) + {1, 1, 0, 0, 0, 0, 0, 0}, // 2 + {2, 2, 1, 1, 0, 0, 0, 0}, // 3 + {3, 3, 2, 2, 1, 0, 0, 0}, // 4 + {4, 3, 2, 1, 1, 0, 0, 0}, // 5 + {5, 4, 3, 2, 1, 0, 0, 0}, // 6 + {6, 5, 4, 3, 2, 1, 0, 0}, // 7 + }; + + static constexpr int8 OctTransposeMap[][8] = + { + { 0, 0, -12, -12, -24, -36, -48, -60}, // 2 + { 0, 0, -12, -12, -24, -36, -48, -60}, // 3 + { 0, 0, -12, -12, -24, -36, -48, -60}, // 4 + {12, 0, -12, -24, -24, -36, -48, -60}, // 5 + {12, 0, -12, -24, -36, -48, -48, -60}, // 6 + {12, 0, -12, -24, -36, -48, -60, -72}, // 7 + }; + + // TODO: Move octaves so that they align better (C-4 = lowest, we don't have access to the highest four octaves) + for(int octave = 4; octave < 10; octave++) + { + for(int note = 0; note < 12; note++) { - Order().resize(nOrders + n); - for (uint32 i=0; iseq[i]; - if (seqval < wNumBlocks) - { - Order()[nOrders++] = static_cast(seqval); - } - } + instr.Keyboard[12 * octave + note] = smp + OctSampleMap[numSamples - 2][octave - 4]; + instr.NoteMap[12 * octave + note] += OctTransposeMap[numSamples - 2][octave - 4]; + } + } + } else if(maskedType == MMDInstrHeader::EXTSAMPLE) + { + needInstruments = true; + instr.Transpose(-24); + } else if(!isSynth && hardwareMixSamples) + { + for(int octave = 7; octave < 10; octave++) + { + for(int note = 0; note < 12; note++) + { + instr.NoteMap[12 * octave + note] -= static_cast((octave - 6) * 12); } } } - playtransp = pmsh2->playtransp; - } - // Reading Expansion structure - if (pmex) - { - // Channel Split - if ((m_nChannels == 4) && (pmsh->flags & MMD_FLAG_8CHANNEL)) - { - for (uint32 i8ch=0; i8ch<4; i8ch++) - { - if (pmex->channelsplit[i8ch]) m_nChannels++; - } - } - // Song Comments (null-terminated) - uint32 annotxt = pmex->annotxt; - uint32 annolen = pmex->annolen; - 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(mpt::byte_cast(lpStream) + annotxt, annolen - 1, SongMessage::leAutodetect); - } - // Song Name - uint32 songname = pmex->songname; - uint32 songnamelen = pmex->songnamelen; - if ((songname) && (songnamelen) && (songname <= dwMemLength) && (songnamelen <= dwMemLength-songname)) - { - mpt::String::Read(m_songName, lpStream + songname, songnamelen); - } - // Sample Names - uint32 smpinfoex = pmex->iinfo; - if (smpinfoex) - { - uint32 iinfoptr = pmex->iinfo; - uint32 ientries = pmex->i_ext_entries; - uint32 ientrysz = pmex->i_ext_entrsz; - if ((iinfoptr) && (ientrysz < 256) && (ientries*ientrysz < dwMemLength) && (iinfoptr < dwMemLength - ientries*ientrysz)) - { - const char *psznames = (const char *)(lpStream + iinfoptr); - for (uint32 i=0; i(m_szNames[i + 1], (psznames + i * ientrysz), ientrysz); - } - } - } - // Track Names - uint32 trackinfo_ofs = pmex->trackinfo_ofs; - if ((trackinfo_ofs) && (trackinfo_ofs < dwMemLength) && (m_nChannels * 4u < dwMemLength - trackinfo_ofs)) + MMD0Sample sampleHeader; + sampleHeaderChunk.ReadStruct(sampleHeader); + + // midiChannel = 0xFF == midi instrument but with invalid channel, midiChannel = 0x00 == sample-based instrument? + if(sampleHeader.midiChannel > 0 && sampleHeader.midiChannel <= 16) { - const_unaligned_ptr_be ptrktags = const_unaligned_ptr_be(lpStream + trackinfo_ofs); - for (uint32 i=0; i(lpStream + trktagofs); - if (ntag == MMDTAG_END) break; - uint32 tagdata = *const_unaligned_ptr_be(lpStream + trktagofs + 4); - switch(ntag) - { - case MMDTAG_TRK_NAMELEN: trknamelen = tagdata; break; - case MMDTAG_TRK_NAME: trknameofs = tagdata; break; - } - trktagofs += 8; - } - if ((trknameofs) && (trknameofs < dwMemLength - trknamelen) && trknamelen < dwMemLength) - { - mpt::String::Read(ChnSettings[i].szName, lpStream + trknameofs, trknamelen); - } - } + auto &mixPlug = m_MixPlugins[numPlugins]; + mixPlug = {}; + mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P'); + mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D'); + mixPlug.Info.gain = 10; + mixPlug.Info.szName = "MIDI Input Output"; + mixPlug.Info.szLibraryName = "MIDI Input Output"; + + instr.nMixPlug = numPlugins + 1; + instr.Transpose(-24); + + numPlugins++; } +#endif // NO_VST + } + if(sampleHeader.midiPreset > 0 && sampleHeader.midiPreset <= 128) + { + instr.nMidiProgram = sampleHeader.midiPreset; + } + + for(SAMPLEINDEX i = 0; i < numSamples; i++) + { + ModSample &mptSmp = Samples[smp + i]; + mptSmp.Initialize(MOD_TYPE_MED); + mptSmp.nVolume = 4u * std::min(sampleHeader.sampleVolume, 64u); + mptSmp.RelativeTone = sampleHeader.sampleTranspose; + } + + if(isSynth || !(loadFlags & loadSampleData)) + { + smp += numSamples; + continue; } - } - // Reading samples - if (dwSmplArr > dwMemLength - 4*m_nSamples) return true; - pdwTable = const_unaligned_ptr_be(lpStream + dwSmplArr); - for (uint32 iSmp=0; iSmp= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength) || !(loadFlags & loadSampleData)) continue; - const MMDSAMPLEHEADER *psdh = (const MMDSAMPLEHEADER *)(lpStream + dwPos); - uint32 len = psdh->length; - #ifdef MED_LOG - Log("SampleData %d: stype=0x%02X len=%d\n", iSmp, psdh->type, len); - #endif - if(dwPos + len + 6 > dwMemLength) len = 0; - uint32 stype = psdh->type; - FileReader chunk(mpt::byte_cast(mpt::as_span(lpStream + dwPos + 6, dwMemLength - dwPos - 6))); SampleIO sampleIO( SampleIO::_8bit, @@ -801,144 +855,502 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) SampleIO::bigEndian, SampleIO::signedPCM); - if (stype & 0x80) + const bool hasLoop = sampleHeader.loopLength > 1; + SmpLength loopStart = sampleHeader.loopStart * 2; + SmpLength loopEnd = loopStart + sampleHeader.loopLength * 2; + + SmpLength length = mpt::saturate_cast(sampleChunk.GetLength()); + if(instrHeader.type & MMDInstrHeader::S_16) { - chunk.Skip((stype & 0x20) ? 14 : 6); - } else - { - if(stype & 0x10) - { - sampleIO |= SampleIO::_16bit; - len /= 2; - } - if(stype & 0x20) - { - sampleIO |= SampleIO::stereoSplit; - len /= 2; - } + sampleIO |= SampleIO::_16bit; + length /= 2; } - Samples[iSmp + 1].nLength = len; - sampleIO.ReadSample(Samples[iSmp + 1], chunk); - } - // Reading patterns (blocks) - if(!(loadFlags & loadPatternData)) - { - return true; - } - if (wNumBlocks > MAX_PATTERNS) wNumBlocks = MAX_PATTERNS; - if ((!dwBlockArr) || (dwBlockArr > dwMemLength - 4u*wNumBlocks) || (4u*wNumBlocks > dwMemLength)) return true; - pdwTable = const_unaligned_ptr_be(lpStream + dwBlockArr); - playtransp += (version == '3') ? 24 : 48; - Patterns.ResizeArray(wNumBlocks); - for (PATTERNINDEX iBlk=0; iBlk= dwMemLength) || (dwPos >= dwMemLength - 8)) continue; - uint32 lines = 64, tracks = 4; - if (version == '0') + if (instrHeader.type & MMDInstrHeader::STEREO) { - const MMD0BLOCK *pmb = (const MMD0BLOCK *)(lpStream + dwPos); - lines = pmb->lines + 1; - tracks = pmb->numtracks; - if (!tracks) tracks = m_nChannels; - if(!Patterns.Insert(iBlk, lines)) continue; - const uint8 * s = (const uint8 *)(lpStream + dwPos + 2); - uint32 maxlen = tracks*lines*3; - if (maxlen + dwPos > dwMemLength - 2) break; - for (uint32 y=0; y 1) + length = length / ((1u << numSamples) - 1); + + for(SAMPLEINDEX i = 0; i < numSamples; i++) + { + ModSample &mptSmp = Samples[smp + i]; + + mptSmp.nLength = length; + sampleIO.ReadSample(mptSmp, sampleChunk); + + if(hasLoop) { - ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0); - for (uint32 x=0; x> 4; - if (s[0] & 0x80) instr |= 0x10; - if (s[0] & 0x40) instr |= 0x20; - if ((note) && (note <= 132)) p->note = static_cast(note + playtransp); - p->instr = instr; - p->command = s[1] & 0x0F; - p->param = s[2]; - // if (!iBlk) Log("%02X.%02X.%02X | ", s[0], s[1], s[2]); - MedConvert(*p, pmsh); - p++; - } - //if (!iBlk) Log("\n"); + mptSmp.nLoopStart = loopStart; + mptSmp.nLoopEnd = loopEnd; + mptSmp.uFlags.set(CHN_LOOP); } - } else + + length *= 2; + loopStart *= 2; + loopEnd *= 2; + } + + smp += numSamples; + } + + if(expData.instrExtOffset != 0 && expData.instrExtEntries != 0 && file.Seek(expData.instrExtOffset)) + { + const uint16 entries = std::min(expData.instrExtEntries, songHeader.numSamples); + const uint16 size = expData.instrExtEntrySize; + for(uint16 i = 0; i < entries; i++) { - const MMD1BLOCK *pmb = (const MMD1BLOCK *)(lpStream + dwPos); - #ifdef MED_LOG - Log("MMD1BLOCK: lines=%2d, tracks=%2d, offset=0x%04X\n", - pmb->lines, pmb->numtracks, pmb->info); - #endif - const MMD1BLOCKINFO *pbi = NULL; - const uint8 *pcmdext = NULL; - lines = pmb->lines + 1; - tracks = pmb->numtracks; - if (!tracks) tracks = m_nChannels; - Patterns.Insert(iBlk, lines); - uint32 dwBlockInfo = pmb->info; - if ((dwBlockInfo) && (dwBlockInfo < dwMemLength - sizeof(MMD1BLOCKINFO))) + MMDInstrExt instrExt; + file.ReadStructPartial(instrExt, size); + + ModInstrument &ins = *Instruments[i + 1]; + if(instrExt.hold) { - pbi = (const MMD1BLOCKINFO *)(lpStream + dwBlockInfo); - #ifdef MED_LOG - Log(" BLOCKINFO: blockname=0x%04X namelen=%d pagetable=0x%04X &cmdexttable=0x%04X\n", - pbi->blockname, pbi->blocknamelen, pbi->pagetable, pbi->cmdexttable); - #endif - if ((pbi->blockname) && (pbi->blocknamelen)) + ins.VolEnv.assign({ + EnvelopeNode{0u, ENVELOPE_MAX}, + EnvelopeNode{static_cast(instrExt.hold - 1), ENVELOPE_MAX}, + EnvelopeNode{static_cast(instrExt.hold + (instrExt.decay ? 64u / instrExt.decay : 0u)), ENVELOPE_MIN}, + }); + if(instrExt.hold == 1) + ins.VolEnv.erase(ins.VolEnv.begin()); + ins.nFadeOut = instrExt.decay ? (instrExt.decay * 512) : 32767; + ins.VolEnv.dwFlags.set(ENV_ENABLED); + needInstruments = true; + } + if(size > offsetof(MMDInstrExt, longMidiPreset)) + { + ins.wMidiBank = instrExt.longMidiPreset; + } +#ifndef NO_VST + if(ins.nMixPlug > 0) + { + PLUGINDEX plug = ins.nMixPlug - 1; + auto &mixPlug = m_MixPlugins[plug]; + if(mixPlug.Info.dwPluginId2 == PLUGMAGIC('M', 'M', 'I', 'D')) { - uint32 nameofs = pbi->blockname; - uint32 namelen = pbi->blocknamelen; - if ((nameofs < dwMemLength) && (namelen < dwMemLength - nameofs)) + float dev = (instrExt.outputDevice + 1) / 65536.0f; // Magic code from MidiInOut.h :( + mixPlug.pluginData.resize(3 * sizeof(uint32)); + auto memFile = std::make_pair(mpt::as_span(mixPlug.pluginData), mpt::IO::Offset(0)); + mpt::IO::WriteIntLE(memFile, 0); // Plugin data type + mpt::IO::Write(memFile, IEEE754binary32LE{0}); // Input device + mpt::IO::Write(memFile, IEEE754binary32LE{dev}); // Output device + + // Check if we already have another plugin referencing this output device + for(PLUGINDEX p = 0; p < plug; p++) { - Patterns[iBlk].SetName((const char *)(lpStream + nameofs), namelen); - } - } - if (pbi->cmdexttable) - { - uint32 cmdexttable = pbi->cmdexttable; - if (cmdexttable < dwMemLength - 4) - { - cmdexttable = *const_unaligned_ptr_be(lpStream + cmdexttable); - if ((cmdexttable) && (cmdexttable <= dwMemLength - lines*tracks)) + const auto &otherPlug = m_MixPlugins[p]; + if(otherPlug.Info.dwPluginId1 == mixPlug.Info.dwPluginId1 + && otherPlug.Info.dwPluginId2 == mixPlug.Info.dwPluginId2 + && otherPlug.pluginData == mixPlug.pluginData) { - pcmdext = (const uint8 *)(lpStream + cmdexttable); + ins.nMixPlug = p + 1; + mixPlug = {}; + break; } } } } - const uint8 * s = (const uint8 *)(lpStream + dwPos + 8); - uint32 maxlen = tracks*lines*4; - if (maxlen + dwPos > dwMemLength - 8 || !Patterns.IsValidPat(iBlk)) break; - for (uint32 y=0; y offsetof(MMDInstrExt, loopLength)) { - ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0); - for (uint32 x=0; x NOTE_MAX) rnote = NOTE_MAX; - p->note = (uint8)rnote; - } else if(note == 0x80) - { - p->note = NOTE_NOTECUT; - } - p->instr = s[1]; - p->command = s[2]; - p->param = s[3]; - if (pcmdext) p->vol = pcmdext[x]; - MedConvert(*p, pmsh); - p++; - } - if (pcmdext) pcmdext += tracks; + sample.nLoopStart = instrExt.loopStart; + sample.nLoopEnd = instrExt.loopStart + instrExt.loopLength; + } + if(size > offsetof(MMDInstrExt, instrFlags)) + { + if(instrExt.instrFlags & MMDInstrExt::SSFLG_LOOP) + sample.uFlags.set(CHN_LOOP); + if(instrExt.instrFlags & MMDInstrExt::SSFLG_PINGPONG) + sample.uFlags.set(CHN_LOOP | CHN_PINGPONGLOOP); + if(instrExt.instrFlags & MMDInstrExt::SSFLG_DISABLED) + sample.nGlobalVol = 0; } } } + if(expData.instrInfoOffset != 0 && expData.instrInfoEntries != 0 && file.Seek(expData.instrInfoOffset)) + { + const uint16 entries = std::min(expData.instrInfoEntries, songHeader.numSamples); + const uint16 size = expData.instrInfoEntrySize; + for(uint16 i = 0; i < entries; i++) + { + MMDInstrInfo instrInfo; + file.ReadStructPartial(instrInfo, size); + Instruments[i + 1]->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrInfo.name); + for(auto smp : Instruments[i + 1]->GetSamples()) + { + m_szNames[smp] = Instruments[i + 1]->name; + } + } + } + + const auto numSongs = std::min(MAX_SEQUENCES, fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1); + + file.Rewind(); + PATTERNINDEX basePattern = 0; + for(SEQUENCEINDEX song = 0; song < numSongs; song++) + { + MEDReadNextSong(file, fileHeader, expData, songHeader); + + if(song != 0) + { + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) + return false; + } + + ModSequence &order = Order(song); + + std::map jumpTargets; + order.clear(); + uint32 preamp = 32; + if(version < 2) + { + if(songHeader.songLength > 256 || m_nChannels > 16) + return false; + ReadOrderFromArray(order, songHeader.GetMMD0Song().sequence, songHeader.songLength); + for(auto &ord : order) + { + ord += basePattern; + } + + SetupMODPanning(true); + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + ChnSettings[chn].nVolume = std::min(songHeader.trackVol[chn], 64); + } + } else + { + const MMD2Song header = songHeader.GetMMD2Song(); + if(header.numTracks < 1 || header.numTracks > 64 || m_nChannels > 64) + return false; + + const bool freePan = (header.flags3 & MMD2Song::FLAG3_FREEPAN); + if(header.volAdjust) + preamp = Util::muldivr_unsigned(preamp, std::min(header.volAdjust, 800), 100); + if (freePan) + preamp /= 2; + + if(file.Seek(header.trackVolsOffset)) + { + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + ChnSettings[chn].nVolume = std::min(file.ReadUint8(), 64); + } + } + if(header.trackPanOffset && file.Seek(header.trackPanOffset)) + { + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + ChnSettings[chn].nPan = (Clamp(file.ReadInt8(), -16, 16) + 16) * 8; + } + } else + { + SetupMODPanning(true); + } + + std::vector sections; + if(!file.Seek(header.sectionTableOffset) + || !file.CanRead(songHeader.songLength * 2) + || !file.ReadVector(sections, songHeader.songLength)) + continue; + for(uint16 section : sections) + { + if(section > header.numPlaySeqs) + continue; + + file.Seek(header.playSeqTableOffset + section * 4); + if(file.Seek(file.ReadUint32BE()) || !file.CanRead(sizeof(MMD2PlaySeq))) + { + MMD2PlaySeq playSeq; + file.ReadStruct(playSeq); + + if(!order.empty()) + order.push_back(order.GetIgnoreIndex()); + + size_t readOrders = playSeq.length; + if(!file.CanRead(readOrders)) + LimitMax(readOrders, file.BytesLeft()); + LimitMax(readOrders, ORDERINDEX_MAX); + + size_t orderStart = order.size(); + order.reserve(orderStart + readOrders); + for(size_t ord = 0; ord < readOrders; ord++) + { + PATTERNINDEX pat = file.ReadUint16BE(); + if(pat < 0x8000) + { + order.push_back(basePattern + pat); + } + } + if(playSeq.name[0]) + order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, playSeq.name)); + + // Play commands (jump / stop) + if(playSeq.commandTableOffset > 0 && file.Seek(playSeq.commandTableOffset)) + { + MMDPlaySeqCommand command; + while(file.ReadStruct(command)) + { + FileReader chunk = file.ReadChunk(command.extraSize); + ORDERINDEX ord = mpt::saturate_cast(orderStart + command.offset); + if(command.offset == 0xFFFF || ord >= order.size()) + break; + if(command.command == MMDPlaySeqCommand::kStop) + { + order[ord] = order.GetInvalidPatIndex(); + } else if(command.command == MMDPlaySeqCommand::kJump) + { + jumpTargets[ord] = chunk.ReadUint16BE(); + order[ord] = order.GetIgnoreIndex(); + } + } + } + } + } + } + + const bool volHex = (songHeader.flags & MMDSong::FLAG_VOLHEX) != 0; + const bool is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0; + const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0; + const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK); + m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat); + m_nDefaultSpeed = Clamp(songHeader.tempo2, 1, 32); + if(bpmMode) + { + m_nDefaultRowsPerBeat = rowsPerBeat; + m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u; + } + + if(songHeader.masterVol) + m_nDefaultGlobalVolume = std::min(songHeader.masterVol, 64) * 4; + m_nSamplePreAmp = m_nVSTiVolume = preamp; + + // For MED, this affects both volume and pitch slides + m_SongFlags.set(SONG_FASTVOLSLIDES, !(songHeader.flags & MMDSong::FLAG_STSLIDE)); + + if(expData.songNameOffset && file.Seek(expData.songNameOffset)) + { + file.ReadString(m_songName, expData.songNameLength); + if(numSongs > 1) + order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, m_songName)); + } + if(expData.annoLength > 1 && file.Seek(expData.annoText)) + { + m_songMessage.Read(file, expData.annoLength - 1, SongMessage::leAutodetect); + } + + if(expData.mmdInfoOffset && file.Seek(expData.mmdInfoOffset)) + { + file.Skip(6); // Next info file (unused) + reserved + if(file.ReadUint16BE() == 1) // ASCII text + { + uint32 length = file.ReadUint32BE(); + if(length && file.CanRead(length)) + { + const auto oldMsg = std::move(m_songMessage); + m_songMessage.Read(file, length, SongMessage::leAutodetect); + if(!oldMsg.empty()) + m_songMessage.SetRaw(oldMsg + std::string(2, SongMessage::InternalLineEnding) + m_songMessage); + } + } + } + + // Track Names + if(version >= 2 && expData.trackInfoOffset) + { + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + if(file.Seek(expData.trackInfoOffset + chn * 4) + && file.Seek(file.ReadUint32BE())) + { + uint32 nameOffset = 0, nameLength = 0; + while(file.CanRead(sizeof(MMDTag))) + { + MMDTag tag; + file.ReadStruct(tag); + if(tag.type == MMDTag::MMDTAG_END) + break; + switch(tag.type & MMDTag::MMDTAG_MASK) + { + case MMDTag::MMDTAG_TRK_NAME: nameOffset = tag.data; break; + case MMDTag::MMDTAG_TRK_NAMELEN: nameLength = tag.data; break; + } + } + if(nameOffset > 0 && nameLength > 0 && file.Seek(nameOffset)) + { + file.ReadString(ChnSettings[chn].szName, nameLength); + } + } + } + } + + PATTERNINDEX numPatterns = songHeader.numBlocks; + Patterns.ResizeArray(basePattern + numPatterns); + for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) + { + if(!(loadFlags & loadPatternData) + || !file.Seek(fileHeader.blockArrOffset + pat * 4u) + || !file.Seek(file.ReadUint32BE())) + { + continue; + } + + CHANNELINDEX numTracks; + ROWINDEX numRows; + std::string patName; + int transpose; + FileReader cmdExt; + + if(version < 1) + { + transpose = NOTE_MIN + 47; + MMD0PatternHeader patHeader; + file.ReadStruct(patHeader); + numTracks = patHeader.numTracks; + numRows = patHeader.numRows + 1; + } else + { + transpose = NOTE_MIN + (version <= 2 ? 47 : 23) + songHeader.playTranspose; + MMD1PatternHeader patHeader; + file.ReadStruct(patHeader); + numTracks = patHeader.numTracks; + numRows = patHeader.numRows + 1; + if(patHeader.blockInfoOffset) + { + auto offset = file.GetPosition(); + file.Seek(patHeader.blockInfoOffset); + MMDBlockInfo blockInfo; + file.ReadStruct(blockInfo); + if(file.Seek(blockInfo.nameOffset)) + { + // We have now chased four pointers to get this far... lovely format. + file.ReadString(patName, blockInfo.nameLength); + } + if(blockInfo.cmdExtTableOffset + && file.Seek(blockInfo.cmdExtTableOffset) + && file.Seek(file.ReadUint32BE())) + { + cmdExt = file.ReadChunk(numTracks * numRows); + } + + file.Seek(offset); + } + } + + if(!Patterns.Insert(basePattern + pat, numRows)) + continue; + + CPattern &pattern = Patterns[basePattern + pat]; + pattern.SetName(patName); + LimitMax(numTracks, m_nChannels); + + for(ROWINDEX row = 0; row < numRows; row++) + { + ModCommand *m = pattern.GetpModCommand(row, 0); + for(CHANNELINDEX chn = 0; chn < numTracks; chn++, m++) + { + int note = NOTE_NONE; + if(version < 1) + { + const auto [noteInstr, instrCmd, param] = file.ReadArray(); + + if(noteInstr & 0x3F) + note = (noteInstr & 0x3F) + transpose; + + m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1); + + m->command = instrCmd & 0x0F; + m->param = param; + } else + { + const auto [noteVal, instr, command, param1] = file.ReadArray(); + m->vol = cmdExt.ReadUint8(); + + if(noteVal & 0x7F) + note = (noteVal & 0x7F) + transpose; + else if(noteVal == 0x80) + m->note = NOTE_NOTECUT; + + m->instr = instr & 0x3F; + m->command = command; + m->param = param1; + } + // Octave wrapping for 4-channel modules (TODO: this should not be set because of synth instruments) + if(hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12) + needInstruments = true; + + if(note >= NOTE_MIN && note <= NOTE_MAX) + m->note = static_cast(note); + ConvertMEDEffect(*m, is8Ch, bpmMode, rowsPerBeat, volHex); + } + } + } + + // Fix jump order commands + for(const auto & [from, to] : jumpTargets) + { + PATTERNINDEX pat; + if(from > 0 && order.IsValidPat(from - 1)) + { + pat = order.EnsureUnique(from - 1); + } else + { + if(to == from + 1) // No action required + continue; + pat = Patterns.InsertAny(1); + if(pat == PATTERNINDEX_INVALID) + continue; + order[from] = pat; + } + Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast(to)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow()); + if(pat >= numPatterns) + numPatterns = pat + 1; + } + + basePattern += numPatterns; + + if(!expData.nextModOffset || !file.Seek(expData.nextModOffset)) + break; + } + Order.SetSequence(0); + + if(!needInstruments) + { + for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) + { + delete Instruments[ins]; + Instruments[ins] = nullptr; + } + m_nInstruments = 0; + } + + if(anySynthInstrs) + AddToLog(LogWarning, U_("Synthesized MED instruments are not supported.")); + + const mpt::uchar *madeWithTracker = MPT_ULITERAL(""); + switch(version) + { + case 0: madeWithTracker = m_nChannels > 4 ? MPT_ULITERAL("OctaMED v2.10 (MMD0)") : MPT_ULITERAL("MED v2 (MMD0)"); break; + case 1: madeWithTracker = MPT_ULITERAL("OctaMED v4 (MMD1)"); break; + case 2: madeWithTracker = MPT_ULITERAL("OctaMED v5 (MMD2)"); break; + case 3: madeWithTracker = MPT_ULITERAL("OctaMED Soundstudio (MMD3)"); break; + } + + m_modFormat.formatName = mpt::format(MPT_USTRING("OctaMED (MMD%1)"))(version); + m_modFormat.type = MPT_USTRING("med"); + m_modFormat.madeWithTracker = madeWithTracker; + m_modFormat.charset = mpt::Charset::ISO8859_1; + return true; } - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp index 02b1f1029..7e7deef7c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp @@ -327,13 +327,13 @@ uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChann // Set GM program / drum name if (!isDrum) { - strcpy(pIns->name, szMidiProgramNames[program]); + pIns->name = szMidiProgramNames[program]; } else { if (note >= 24 && note <= 84) - strcpy(pIns->name, szMidiPercussionNames[note - 24]); + pIns->name = szMidiPercussionNames[note - 24]; else - strcpy(pIns->name, "Percussions"); + pIns->name = "Percussions"; } return m_nInstruments; } @@ -350,7 +350,7 @@ struct MThd MPT_BINARY_STRUCT(MThd, 10); -typedef uint32 tick_t; +using tick_t = uint32; struct TrackState { @@ -362,15 +362,15 @@ struct TrackState struct ModChannelState { - enum : uint8 { NOMIDI = 0xFF }; // No MIDI channel assigned. + static constexpr uint8 NOMIDI = 0xFF; // No MIDI channel assigned. - tick_t age = 0; // At which MIDI tick the channel was triggered - int32 porta = 0; // Current portamento position in extra-fine slide units (1/64th of a semitone) - uint8 vol = 100; // MIDI note volume (0...127) - uint8 pan = 128; // MIDI channel panning (0...256) - uint8 midiCh = NOMIDI; // MIDI channel that was last played on this channel - ModCommand::NOTE note = NOTE_NONE; // MIDI note that was last played on this channel - bool sustained = false; // If true, the note was already released by a note-off event, but sustain pedal CC is still active + 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 @@ -389,7 +389,7 @@ struct MidiChannelState bool monoMode = false; // Mono/Poly operation 126/127 n/a Poly bool sustain = false; // Sustain pedal 64 on/off off - std::array noteOn; // Value != CHANNELINDEX_INVALID: Note is active and mapped to mod channel in value + std::array noteOn; // Value != CHANNELINDEX_INVALID: Note is active and mapped to mod channel in value MidiChannelState() { @@ -638,7 +638,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::ISO8859_1; SetMixLevels(mixLevels1_17RC3); m_nTempoMode = tempoModeModern; @@ -669,14 +669,28 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) std::bitset<16> drumChns; drumChns.set(MIDI_DRUMCHANNEL - 1); - for(uint16 t = 0; t < numTracks; t++) + tick_t timeShift = 0; + for(auto &track : tracks) { if(!file.ReadMagic("MTrk")) return false; - tracks[t].track = file.ReadChunk(file.ReadUint32BE()); + track.track = file.ReadChunk(file.ReadUint32BE()); tick_t delta = 0; - tracks[t].track.ReadVarInt(delta); - tracks[t].nextEvent = delta; + track.track.ReadVarInt(delta); + // Work-around for some MID files that assume that negative deltas exist (they don't according to the standard) + if(delta > int32_max) + timeShift = std::max(static_cast(~delta + 1), timeShift); + track.nextEvent = delta; + } + if(timeShift != 0) + { + for(auto &track : tracks) + { + if(track.nextEvent > int32_max) + track.nextEvent = timeShift - static_cast(~track.nextEvent + 1); + else + track.nextEvent += timeShift; + } } uint16 finishedTracks = 0; @@ -687,7 +701,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) int8 masterTranspose = 0; bool isXG = false; bool isEMIDI = false; - bool isType2 = (fileHeader.format == 2); + const bool isType2 = (fileHeader.format == 2); while(finishedTracks < numTracks) { @@ -901,7 +915,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) midiChnStatus[midiCh].pan = data2 * 2u; for(auto chn : midiChnStatus[midiCh].noteOn) { - if(chn != CHANNELINDEX_INVALID) + if(chn != CHANNELINDEX_INVALID && modChnStatus[chn].pan != midiChnStatus[midiCh].pan) { if(Patterns[pat].WriteEffect(EffectWriter(CMD_PANNING8, midiChnStatus[midiCh].pan).Channel(chn).Row(row))) { @@ -1151,7 +1165,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) } m.command = static_cast(diff < 0 ? CMD_PORTAMENTODOWN : CMD_PORTAMENTOUP); - int32 absDiff = mpt::abs(diff); + int32 absDiff = std::abs(diff); int32 realDiff = 0; if(absDiff < 16) { @@ -1187,7 +1201,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) // Add another sub-song for type-2 files if(isType2 && finishedTracks < numTracks) { - if(Order.AddSequence(false) == SEQUENCEINDEX_INVALID) + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; Order().clear(); } @@ -1222,11 +1236,11 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { channels.push_back(i); if(modChnStatus[i].midiCh != ModChannelState::NOMIDI) - mpt::String::WriteAutoBuf(ChnSettings[i].szName) = mpt::format("MIDI Ch %1")(1 + modChnStatus[i].midiCh); + ChnSettings[i].szName = mpt::format("MIDI Ch %1")(1 + modChnStatus[i].midiCh); else if(i == tempoChannel) - mpt::String::WriteAutoBuf(ChnSettings[i].szName) = "Tempo"; + ChnSettings[i].szName = "Tempo"; else if(i == globalVolChannel) - mpt::String::WriteAutoBuf(ChnSettings[i].szName) = "Global Volume"; + ChnSettings[i].szName = "Global Volume"; } } if(channels.empty()) @@ -1236,7 +1250,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) if(GetpModDoc() != nullptr) { // Keep MIDI channels in patterns neatly grouped - std::sort(channels.begin(), channels.end(), [&modChnStatus] (CHANNELINDEX c1, CHANNELINDEX c2) -> bool + std::sort(channels.begin(), channels.end(), [&modChnStatus] (CHANNELINDEX c1, CHANNELINDEX c2) { if(modChnStatus[c1].midiCh == modChnStatus[c2].midiCh) return c1 < c2; @@ -1309,24 +1323,24 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { // Load from Instrument or Sample file InputFile f; - if(f.Open(midiMapName)) + if(f.Open(midiMapName, SettingCacheCompleteFileBeforeLoading())) { FileReader insFile = GetFileReader(f); if(ReadInstrumentFromFile(ins, insFile, false)) { mpt::PathString filename = midiMapName.GetFullFileName(); pIns = Instruments[ins]; - if(!pIns->filename[0]) mpt::String::Copy(pIns->filename, filename.ToLocale().c_str()); + if(!pIns->filename[0]) pIns->filename = filename.ToLocale(); if(!pIns->name[0]) { if(midiCode < 0x80) { - mpt::String::CopyN(pIns->name, szMidiProgramNames[midiCode]); + pIns->name = szMidiProgramNames[midiCode]; } else { uint32 key = midiCode & 0x7F; - if((key >= 24) && (key < 24 + mpt::size(szMidiPercussionNames))) - mpt::String::CopyN(pIns->name, szMidiPercussionNames[key - 24]); + if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames))) + pIns->name = szMidiPercussionNames[key - 24]; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp index d9401542f..27dfbf000 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp @@ -21,16 +21,31 @@ #include "MPEGFrame.h" #include "OggStream.h" + #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) -#include "../common/mptBufferIO.h" +#include #endif #if defined(MPT_WITH_VORBIS) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif #if defined(MPT_WITH_VORBISFILE) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" #endif @@ -39,7 +54,7 @@ #include #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" -#endif // MPT_WITH_STBVORBIS +#endif // MPT_WITH_STBVORBIS OPENMPT_NAMESPACE_BEGIN @@ -63,7 +78,7 @@ struct MO3FileHeader itCompatGxx = 0x0400, itOldFX = 0x0800, modplugMode = 0x10000, - unknown = 0x20000, // Always set + unknown = 0x20000, // Always set (internal BASS flag to designate modules) modVBlank = 0x80000, hasPlugins = 0x100000, extFilterRange = 0x200000, @@ -118,7 +133,7 @@ struct MO3Envelope if(flags & envLoop) mptEnv.dwFlags.set(ENV_LOOP); if(flags & envFilter) mptEnv.dwFlags.set(ENV_FILTER); if(flags & envCarry) mptEnv.dwFlags.set(ENV_CARRY); - mptEnv.resize(std::min(numNodes, 25)); + mptEnv.resize(std::min(numNodes.get(), uint8(25))); mptEnv.nSustainStart = sustainStart; mptEnv.nSustainEnd = sustainEnd; mptEnv.nLoopStart = loopStart; @@ -224,7 +239,7 @@ struct MO3Instrument } if(type == MOD_TYPE_IT) - mptIns.nGlobalVol = std::min(globalVol, 128) / 2u; + mptIns.nGlobalVol = std::min(static_cast(globalVol), uint8(128)) / 2u; if(panning <= 256) { mptIns.nPan = panning; @@ -235,8 +250,8 @@ struct MO3Instrument mptIns.nPPC = ppc; 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.nVolSwing = static_cast(std::min(volSwing.get(), uint16(100))); + mptIns.nPanSwing = static_cast(std::min(panSwing.get(), uint16(256)) / 4u); mptIns.SetCutoff(cutoff & 0x7F, (cutoff & 0x80) != 0); mptIns.SetResonance(resonance & 0x7F, (resonance & 0x80) != 0); } @@ -249,18 +264,19 @@ struct MO3Sample { enum MO3SampleFlags { - smp16Bit = 0x01, - smpLoop = 0x10, - smpPingPongLoop = 0x20, - smpSustain = 0x100, - smpSustainPingPong = 0x200, - smpStereo = 0x400, - smpCompressionMPEG = 0x1000, // MPEG 1.0 / 2.0 / 2.5 sample - smpCompressionOgg = 0x1000 | 0x2000, // Ogg sample - smpSharedOgg = 0x1000 | 0x2000 | 0x4000, // Ogg sample with shared vorbis header - smpDeltaCompression = 0x2000, // Deltas + compression - smpDeltaPrediction = 0x4000, // Delta prediction + compression - smpCompressionMask = 0x1000 | 0x2000 | 0x4000 + smp16Bit = 0x01, + smpLoop = 0x10, + smpPingPongLoop = 0x20, + smpSustain = 0x100, + smpSustainPingPong = 0x200, + smpStereo = 0x400, + smpCompressionMPEG = 0x1000, // MPEG 1.0 / 2.0 / 2.5 sample + smpCompressionOgg = 0x1000 | 0x2000, // Ogg sample + smpSharedOgg = 0x1000 | 0x2000 | 0x4000, // Ogg sample with shared vorbis header + smpDeltaCompression = 0x2000, // Deltas + compression + smpDeltaPrediction = 0x4000, // Delta prediction + compression + smpOPLInstrument = 0x8000, // OPL patch data + smpCompressionMask = 0x1000 | 0x2000 | 0x4000 | 0x8000 }; int32le freqFinetune; // Frequency in S3M and IT, finetune (0...255) in MOD, MTM, XM @@ -294,10 +310,11 @@ struct MO3Sample } else { mptSmp.nFineTune = static_cast(freqFinetune); - if(type != MOD_TYPE_MTM) mptSmp.nFineTune -= 128; + if(type != MOD_TYPE_MTM) + mptSmp.nFineTune -= 128; mptSmp.RelativeTone = transpose; } - mptSmp.nVolume = std::min(defaultVolume, 64) * 4u; + mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4u; if(panning <= 256) { mptSmp.nPan = panning; @@ -306,10 +323,14 @@ struct MO3Sample mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; - if(flags & smpLoop) mptSmp.uFlags.set(CHN_LOOP); - if(flags & smpPingPongLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); - if(flags & smpSustain) mptSmp.uFlags.set(CHN_SUSTAINLOOP); - if(flags & smpSustainPingPong) mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); + if(flags & smpLoop) + mptSmp.uFlags.set(CHN_LOOP); + if(flags & smpPingPongLoop) + mptSmp.uFlags.set(CHN_PINGPONGLOOP); + if(flags & smpSustain) + mptSmp.uFlags.set(CHN_SUSTAINLOOP); + if(flags & smpSustainPingPong) + mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); mptSmp.nVibType = static_cast(AutoVibratoIT2XM[vibType & 7]); mptSmp.nVibSweep = vibSweep; @@ -317,7 +338,7 @@ struct MO3Sample mptSmp.nVibRate = vibRate; if(type == MOD_TYPE_IT) - mptSmp.nGlobalVol = std::min(globalVol, 64); + mptSmp.nGlobalVol = std::min(static_cast(globalVol), uint8(64)); mptSmp.nSustainStart = sustainStart; mptSmp.nSustainEnd = sustainEnd; } @@ -335,7 +356,7 @@ struct MO3SampleChunk uint16 headerSize; int16 sharedHeader; MO3SampleChunk(const FileReader &chunk_ = FileReader(), uint16 headerSize_ = 0, int16 sharedHeader_ = 0) - : chunk(chunk_), headerSize(headerSize_), sharedHeader(sharedHeader_) { } + : chunk(chunk_), headerSize(headerSize_), sharedHeader(sharedHeader_) {} }; @@ -353,7 +374,10 @@ struct MO3SampleChunk data &= 0xFF; \ if(data == 0) \ { \ - data = file.ReadUint8(); \ + uint8 nextByte; \ + if(!file.Read(nextByte)) \ + break; \ + data = nextByte; \ data = (data << 1) + 1; \ carry = (data > 0xFF); \ data &= 0xFF; \ @@ -365,14 +389,15 @@ struct MO3SampleChunk // until second bit is 0 (noted n0) #define DECODE_CTRL_BITS \ -{ \ - strLen++; \ - do { \ - READ_CTRL_BIT; \ - strLen = (strLen << 1) + carry; \ - READ_CTRL_BIT; \ - } while(carry); \ -} + { \ + strLen++; \ + do \ + { \ + READ_CTRL_BIT; \ + strLen = (strLen << 1) + carry; \ + READ_CTRL_BIT; \ + } while(carry); \ + } static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) { @@ -382,9 +407,9 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) } uint16 data = 0; - int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another) - int32 strLen = 0; // length of previous string - int32 strOffset; // string offset + int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another) + int32 strLen = 0; // length of previous string + int32 strOffset; // string offset uint8 *initDst = dst; uint32 ebp, previousPtr = 0; uint32 initSize = size; @@ -399,31 +424,36 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) if(!carry) { // a 0 ctrl bit means 'copy', not compressed byte - *dst++ = file.ReadUint8(); + if(!file.Read(*dst)) + break; + dst++; size--; } else { // a 1 ctrl bit means compressed bytes are following - ebp = 0; // length adjustment - DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value - strLen -=3; + ebp = 0; // length adjustment + DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value + strLen -= 3; if(strLen < 0) { // means LZ ptr with same previous relative LZ ptr (saved one) - strOffset = previousPtr; // restore previous Ptr + strOffset = previousPtr; // restore previous Ptr strLen++; } else { // LZ ptr in ctrl stream - strOffset = (strLen << 8) | file.ReadUint8(); // read less significant offset byte from stream + uint8 b; + if(!file.Read(b)) + break; + strOffset = (strLen << 8) | b; // read less significant offset byte from stream strLen = 0; strOffset = ~strOffset; if(strOffset < -1280) ebp++; - ebp++; // length is always at least 1 + ebp++; // length is always at least 1 if(strOffset < -32000) ebp++; - previousPtr = strOffset; // save current Ptr + previousPtr = strOffset; // save current Ptr } // read the next 2 bits as part of strLen @@ -434,10 +464,10 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) if(strLen == 0) { // length does not fit in 2 bits - DECODE_CTRL_BITS; // decode length: 1 is the most significant bit, - strLen += 2; // then first bit of each bits pairs (noted n1), until n0. + DECODE_CTRL_BITS; // decode length: 1 is the most significant bit, + strLen += 2; // then first bit of each bits pairs (noted n1), until n0. } - strLen += ebp; // length adjustment + strLen += ebp; // length adjustment if(size >= static_cast(strLen) && strLen > 0) { // Copy previous string @@ -472,12 +502,12 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) struct MO3Delta8BitParams { - typedef int8 sample_t; - typedef uint8 unsigned_t; - enum : int { shift = 7 }; - enum : uint8 { dhInit = 4 }; + using sample_t = int8; + using unsigned_t = uint8; + static constexpr int shift = 7; + static constexpr uint8 dhInit = 4; - static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 &/*dh*/, unsigned_t &val) + static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 & /*dh*/, unsigned_t &val) { do { @@ -490,10 +520,10 @@ struct MO3Delta8BitParams struct MO3Delta16BitParams { - typedef int16 sample_t; - typedef uint16 unsigned_t; - enum : int { shift = 15 }; - enum : uint8 { dhInit = 8 }; + using sample_t = int16; + using unsigned_t = uint16; + static constexpr int shift = 15; + static constexpr uint8 dhInit = 8; static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 &dh, unsigned_t &val) { @@ -505,7 +535,7 @@ struct MO3Delta16BitParams val = (val << 1) + carry; READ_CTRL_BIT; val = (val << 1) + carry; - READ_CTRL_BIT; \ + READ_CTRL_BIT; } while(carry); } else { @@ -520,7 +550,7 @@ struct MO3Delta16BitParams }; -template +template static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t *dst, uint32 length, uint8 numChannels) { uint8 dh = Properties::dhInit, cl = 0; @@ -532,7 +562,7 @@ static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t for(uint8 chn = 0; chn < numChannels; chn++) { typename Properties::sample_t *p = dst + chn; - const typename Properties::sample_t * const pEnd = p + length * numChannels; + const typename Properties::sample_t *const pEnd = p + length * numChannels; while(p < pEnd) { val = 0; @@ -552,12 +582,12 @@ static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t cl--; } dh = dh + cl; - dh >>= 1; // next length in bits of encoded delta second part - carry = val & 1; // sign of delta 1=+, 0=not + dh >>= 1; // next length in bits of encoded delta second part + carry = val & 1; // sign of delta 1=+, 0=not val >>= 1; if(carry == 0) - val = ~val; // negative delta - val += previous; // previous value + delta + val = ~val; // negative delta + val += previous; // previous value + delta *p = val; p += numChannels; previous = val; @@ -566,7 +596,7 @@ static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t } -template +template static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties::sample_t *dst, uint32 length, uint8 numChannels) { uint8 dh = Properties::dhInit, cl = 0; @@ -579,12 +609,12 @@ static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties for(uint8 chn = 0; chn < numChannels; chn++) { typename Properties::sample_t *p = dst + chn; - const typename Properties::sample_t * const pEnd = p + length * numChannels; + const typename Properties::sample_t *const pEnd = p + length * numChannels; while(p < pEnd) { val = 0; Properties::Decode(file, carry, data, dh, val); - cl = dh; // length in bits of: delta second part (right most bits of delta) and sign bit + cl = dh; // length in bits of: delta second part (right most bits of delta) and sign bit while(cl > 0) { READ_CTRL_BIT; @@ -599,18 +629,18 @@ static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties cl--; } dh = dh + cl; - dh >>= 1; // next length in bits of encoded delta second part - carry = val & 1; // sign of delta 1=+, 0=not + dh >>= 1; // next length in bits of encoded delta second part + carry = val & 1; // sign of delta 1=+, 0=not val >>= 1; if(carry == 0) - val = ~val; // negative delta + val = ~val; // negative delta delta = static_cast(val); - val = val + static_cast(next); // predicted value + delta + val = val + static_cast(next); // predicted value + delta *p = val; p += numChannels; sval = static_cast(val); - next = (sval * (1<<1)) + (delta >> 1) - previous; // corrected next value + next = (sval * (1 << 1)) + (delta >> 1) - previous; // corrected next value Limit(next, std::numeric_limits::min(), std::numeric_limits::max()); @@ -628,60 +658,55 @@ static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) { - FileReader &file = *reinterpret_cast(datasource); - return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; + FileReader &file = *reinterpret_cast(datasource); + return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; } static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) { - FileReader &file = *reinterpret_cast(datasource); + FileReader &file = *reinterpret_cast(datasource); switch(whence) { case SEEK_SET: + if(!Util::TypeCanHoldValue(offset)) { - if(!Util::TypeCanHoldValue(offset)) - { - return -1; - } - return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; + return -1; } - break; + return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; + case SEEK_CUR: + if(offset < 0) { - if(offset < 0) + if(offset == std::numeric_limits::min()) { - if(offset == std::numeric_limits::min()) - { - return -1; - } - if(!Util::TypeCanHoldValue(0-offset)) - { - return -1; - } - return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; - } else - { - if(!Util::TypeCanHoldValue(offset)) - { - return -1; - } - return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; + return -1; } - } - break; - case SEEK_END: + if(!Util::TypeCanHoldValue(0 - offset)) + { + return -1; + } + return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; + } else { if(!Util::TypeCanHoldValue(offset)) { return -1; } - if(!Util::TypeCanHoldValue(file.GetLength() + offset)) - { - return -1; - } - return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; + return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; } break; + + case SEEK_END: + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + if(!Util::TypeCanHoldValue(file.GetLength() + offset)) + { + return -1; + } + return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; + default: return -1; } @@ -689,7 +714,7 @@ static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int wh static long VorbisfileFilereaderTell(void *datasource) { - FileReader &file = *reinterpret_cast(datasource); + FileReader &file = *reinterpret_cast(datasource); FileReader::off_t result = file.GetPosition(); if(!Util::TypeCanHoldValue(result)) { @@ -698,7 +723,7 @@ static long VorbisfileFilereaderTell(void *datasource) return static_cast(result); } -#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE +#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE struct MO3ContainerHeader @@ -776,7 +801,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { return false; } -#endif // !MPT_BUILD_FUZZER +#endif // !MPT_BUILD_FUZZER } std::vector musicData(musicSize); @@ -799,9 +824,9 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) MO3FileHeader fileHeader; if(!musicChunk.ReadStruct(fileHeader) - || fileHeader.numChannels == 0 || fileHeader.numChannels > MAX_BASECHANNELS - || fileHeader.numInstruments >= MAX_INSTRUMENTS - || fileHeader.numSamples >= MAX_SAMPLES) + || fileHeader.numChannels == 0 || fileHeader.numChannels > MAX_BASECHANNELS + || fileHeader.numInstruments >= MAX_INSTRUMENTS + || fileHeader.numSamples >= MAX_SAMPLES) { return false; } @@ -858,9 +883,9 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.set(kMODVBlankTiming); if(m_nType == MOD_TYPE_IT) - m_nDefaultGlobalVolume = std::min(fileHeader.globalVol, 128) * 2; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(128)) * 2; else if(m_nType == MOD_TYPE_S3M) - m_nDefaultGlobalVolume = std::min(fileHeader.globalVol, 64) * 4; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(64)) * 4; if(fileHeader.sampleVolume < 0) m_nSamplePreAmp = fileHeader.sampleVolume + 52; @@ -872,7 +897,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX i = 0; i < headerChannels; i++) { if(m_nType == MOD_TYPE_IT) - ChnSettings[i].nVolume = std::min(fileHeader.chnVolume[i], 64); + ChnSettings[i].nVolume = std::min(fileHeader.chnVolume[i].get(), uint8(64)); if(m_nType != MOD_TYPE_XM) { if(fileHeader.chnPan[i] == 127) @@ -990,7 +1015,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) L= 06 00 22 x */ - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_NONE, CMD_NONE, CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, @@ -1051,11 +1076,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) case 0x01: // Note m.note = cmd[1]; - if(m.note < 120) m.note += noteOffset; - else if(m.note == 0xFF) m.note = NOTE_KEYOFF; - else if(m.note == 0xFE) m.note = NOTE_NOTECUT; - else m.note = NOTE_FADE; - if(!m.IsAmigaNote()) onlyAmigaNotes = false; + if(m.note < 120) + m.note += noteOffset; + else if(m.note == 0xFF) + m.note = NOTE_KEYOFF; + else if(m.note == 0xFE) + m.note = NOTE_NOTECUT; + else + m.note = NOTE_FADE; + if(!m.IsAmigaNote()) + onlyAmigaNotes = false; break; case 0x02: // Instrument @@ -1108,7 +1138,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) break; } if((m_nType == MOD_TYPE_IT && !(cmd[1] & 0x03)) - || (m_nType == MOD_TYPE_XM && !(cmd[1] & 0x0F))) + || (m_nType == MOD_TYPE_XM && !(cmd[1] & 0x0F))) { m.volcmd = VOLCMD_PANNING; m.vol = cmd[1] / 4; @@ -1259,10 +1289,12 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) if(isSampleMode || (pIns = AllocateInstrument(ins)) == nullptr) { // Even in IT sample mode, instrument headers are still stored.... - while(musicChunk.ReadUint8() != 0); + while(musicChunk.ReadUint8() != 0) + ; if(version >= 5) { - while(musicChunk.ReadUint8() != 0); + while(musicChunk.ReadUint8() != 0) + ; } musicChunk.Skip(sizeof(MO3Instrument)); continue; @@ -1270,11 +1302,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) std::string name; musicChunk.ReadNullString(name); - mpt::String::Copy(pIns->name, name); + pIns->name = name; if(version >= 5) { musicChunk.ReadNullString(name); - mpt::String::Copy(pIns->filename, name); + pIns->filename = name; } MO3Instrument insHeader; @@ -1297,11 +1329,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[smp]; std::string name; musicChunk.ReadNullString(name); - mpt::String::Copy(m_szNames[smp], name); + m_szNames[smp] = name; if(version >= 5) { musicChunk.ReadNullString(name); - mpt::String::Copy(sample.filename, name); + sample.filename = name; } MO3Sample smpHeader; @@ -1323,11 +1355,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { // Uncompressed sample SampleIO( - (smpHeader.flags & MO3Sample::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, - (smpHeader.flags & MO3Sample::smpStereo) ? SampleIO::stereoSplit : SampleIO::mono, - SampleIO::littleEndian, - SampleIO::signedPCM) - .ReadSample(Samples[smp], file); + (smpHeader.flags & MO3Sample::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, + (smpHeader.flags & MO3Sample::smpStereo) ? SampleIO::stereoSplit : SampleIO::mono, + SampleIO::littleEndian, + SampleIO::signedPCM) + .ReadSample(Samples[smp], file); } else if(smpHeader.compressedSize < 0 && (smp + smpHeader.compressedSize) > 0) { // Duplicate sample @@ -1341,8 +1373,10 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) } } else if(smpHeader.compressedSize > 0) { - if(smpHeader.flags & MO3Sample::smp16Bit) sample.uFlags.set(CHN_16BIT); - if(smpHeader.flags & MO3Sample::smpStereo) sample.uFlags.set(CHN_STEREO); + if(smpHeader.flags & MO3Sample::smp16Bit) + sample.uFlags.set(CHN_16BIT); + if(smpHeader.flags & MO3Sample::smpStereo) + sample.uFlags.set(CHN_STEREO); FileReader sampleData = file.ReadChunk(smpHeader.compressedSize); const uint8 numChannels = sample.GetNumChannels(); @@ -1351,7 +1385,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { // 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; + auto maxLength = sampleData.GetLength(); uint8 maxSamplesPerByte = 4 / numChannels; if(Util::MaxValueOfType(maxLength) / maxSamplesPerByte >= maxLength) maxLength *= maxSamplesPerByte; @@ -1414,6 +1448,13 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { unsupportedSamples = true; } + } else if(compression == MO3Sample::smpOPLInstrument) + { + OPLPatch patch; + if(sampleData.ReadArray(patch)) + { + sample.SetAdlib(true, patch); + } } else { unsupportedSamples = true; @@ -1460,7 +1501,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) // We could in theory only adjust the header and pass 2 chunks to libvorbisfile. // Another option would be to demux both chunks on our own (or using libogg) and pass the raw packet data to libvorbis directly. - mpt::ostringstream mergedStream(std::ios::binary); + std::ostringstream mergedStream(std::ios::binary); mergedStream.imbue(std::locale::classic()); sampleChunks[sharedOggHeader - 1].chunk.Rewind(); @@ -1509,7 +1550,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) // We assume same ordering of streams in both header and data if // multiple streams are present. - mpt::ostringstream mergedStream(std::ios::binary); + std::ostringstream mergedStream(std::ios::binary); mergedStream.imbue(std::locale::classic()); sampleChunks[sharedOggHeader - 1].chunk.Rewind(); @@ -1585,7 +1626,8 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) sampleChunk.chunk.Rewind(); FileReader::PinnedRawDataView sampleChunkView = sampleChunk.chunk.GetPinnedRawDataView(); - mergedData.insert(mergedData.end(), mpt::byte_cast(sampleChunkView.begin()), mpt::byte_cast(sampleChunkView.end())); + mpt::span sampleChunkViewSpan = mpt::byte_cast>(sampleChunkView.span()); + mergedData.insert(mergedData.end(), sampleChunkViewSpan.begin(), sampleChunkViewSpan.end()); #endif } @@ -1594,15 +1636,15 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) FileReader &sampleData = sharedHeader ? mergedDataChunk : sampleChunk.chunk; FileReader &headerChunk = sampleData; -#else // !(MPT_WITH_VORBIS && MPT_WITH_VORBISFILE) +#else // !(MPT_WITH_VORBIS && MPT_WITH_VORBISFILE) FileReader &sampleData = sampleChunk.chunk; FileReader &headerChunk = sharedHeader ? sampleChunks[sharedOggHeader - 1].chunk : sampleData; #if defined(MPT_WITH_STBVORBIS) std::size_t initialRead = sharedHeader ? sampleChunk.headerSize : headerChunk.GetLength(); -#endif // MPT_WITH_STBVORBIS +#endif // MPT_WITH_STBVORBIS -#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE +#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE headerChunk.Rewind(); if(sharedHeader && !headerChunk.CanRead(sampleChunk.headerSize)) @@ -1611,17 +1653,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) ov_callbacks callbacks = { - &VorbisfileFilereaderRead, - &VorbisfileFilereaderSeek, - NULL, - &VorbisfileFilereaderTell - }; + &VorbisfileFilereaderRead, + &VorbisfileFilereaderSeek, + nullptr, + &VorbisfileFilereaderTell}; OggVorbis_File vf; MemsetZero(vf); if(ov_open_callbacks(&sampleData, &vf, nullptr, 0, callbacks) == 0) { if(ov_streams(&vf) == 1) - { // we do not support chained vorbis samples + { // we do not support chained vorbis samples vorbis_info *vi = ov_info(&vf, -1); if(vi && vi->rate > 0 && vi->channels > 0) { @@ -1696,7 +1737,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) headerChunk.Skip(consumed); } FileReader::PinnedRawDataView sampleDataView = sampleData.GetPinnedRawDataView(); - const mpt::byte* data = sampleDataView.data(); + const std::byte *data = sampleDataView.data(); std::size_t dataLeft = sampleDataView.size(); if(!sharedHeader) { @@ -1712,7 +1753,7 @@ 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.HasSampleData()) + && offset < sample.nLength && sample.HasSampleData()) { int channels = 0, decodedSamples = 0; float **output; @@ -1740,11 +1781,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) unsupportedSamples = true; } -#else // !VORBIS +#else // !VORBIS unsupportedSamples = true; -#endif // VORBIS +#endif // VORBIS } } @@ -1780,7 +1821,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { ReadMixPluginChunk(pluginChunk, m_MixPlugins[plug - 1]); } -#endif // NO_PLUGINS +#endif // NO_PLUGINS } } @@ -1811,14 +1852,14 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) cwtv = chunk.ReadUint16LE(); break; case MOD_TYPE_XM: - chunk.ReadString(madeWithTracker, mpt::CharsetCP437, std::min(FileReader::off_t(32), chunk.GetLength())); + chunk.ReadString(madeWithTracker, mpt::Charset::CP437, std::min(FileReader::off_t(32), chunk.GetLength())); break; case MOD_TYPE_MTM: - { - uint8 mtmVersion = chunk.ReadUint8(); - madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(mtmVersion >> 4, mtmVersion & 0x0F); - } - break; + { + uint8 mtmVersion = chunk.ReadUint8(); + madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(mtmVersion >> 4, mtmVersion & 0x0F); + } + break; default: break; } @@ -1875,8 +1916,8 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) } if((GetType() == MOD_TYPE_IT && cwtv >= 0x0100 && cwtv < 0x0214) - || (GetType() == MOD_TYPE_S3M && cwtv >= 0x3100 && cwtv < 0x3214) - || (GetType() == MOD_TYPE_S3M && cwtv >= 0x1300 && cwtv < 0x1320)) + || (GetType() == MOD_TYPE_S3M && cwtv >= 0x3100 && cwtv < 0x3214) + || (GetType() == MOD_TYPE_S3M && cwtv >= 0x1300 && cwtv < 0x1320)) { // Ignore MIDI data in files made with IT older than version 2.14 and old ST3 versions. m_MidiCfg.ClearZxxMacros(); @@ -1890,21 +1931,21 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) if(ModInstrument *ins = Instruments[i]) { // Fix pitch / filter envelope being shortened by one tick - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("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)) + if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) ins->nPanSwing = (ins->nPanSwing + 3) / 4u; } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { m_playBehaviour.reset(kITOffset); - m_playBehaviour.reset(kFT2OffsetOutOfRange); + m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 23, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.23.00.00")) m_playBehaviour.reset(kFT2Periods); - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) m_playBehaviour.reset(kITInstrWithNoteOff); } @@ -1919,11 +1960,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.originalFormatName = std::move(originalFormatName); m_modFormat.madeWithTracker = std::move(madeWithTracker); if(m_dwLastSavedWithVersion) - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.charset = mpt::Charset::Windows1252; else if(GetType() == MOD_TYPE_MOD) - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; else - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; if(unsupportedSamples) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp index 43d5976a9..37ae97172 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp @@ -106,7 +106,7 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co { if(param <= 0x80) { - param = MIN(param << 1, 0xFF); + param = mpt::saturate_cast(param * 2); } else if(param == 0xA4) // surround { @@ -128,8 +128,8 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co case CMD_VOLUME: command = 0x0C; break; case CMD_PATTERNBREAK: command = 0x0D; param = ((param / 10) << 4) | (param % 10); break; case CMD_MODCMDEX: command = 0x0E; break; - case CMD_SPEED: command = 0x0F; param = std::min(param, 0x1F); break; - case CMD_TEMPO: command = 0x0F; param = std::max(param, 0x20); break; + case CMD_SPEED: command = 0x0F; param = std::min(param, uint8(0x1F)); break; + case CMD_TEMPO: command = 0x0F; param = std::max(param, uint8(0x20)); break; case CMD_GLOBALVOLUME: command = 'G' - 55; break; case CMD_GLOBALVOLSLIDE: command = 'H' - 55; break; case CMD_KEYOFF: command = 'K' - 55; break; @@ -227,13 +227,13 @@ struct MODSampleHeader mptSmp.Initialize(MOD_TYPE_MOD); mptSmp.nLength = length * 2; mptSmp.nFineTune = MOD2XMFineTune(finetune & 0x0F); - mptSmp.nVolume = 4u * std::min(volume, 64); + mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); SmpLength lStart = loopStart * 2; SmpLength lLength = loopLength * 2; // See if loop start is incorrect as words, but correct as bytes (like in Soundtracker modules) if(lLength > 2 && (lStart + lLength > mptSmp.nLength) - && (lStart / 2 + lLength <= mptSmp.nLength)) + && (lStart / 2 + lLength <= mptSmp.nLength)) { lStart /= 2; } @@ -315,19 +315,19 @@ struct MODSampleHeader uint32 GetInvalidByteScore() const { return ((volume > 64) ? 1 : 0) - + ((finetune > 15) ? 1 : 0) - + ((loopStart > length * 2) ? 1 : 0); + + ((finetune > 15) ? 1 : 0) + + ((loopStart > length * 2) ? 1 : 0); } // Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore - enum : uint32 { INVALID_BYTE_THRESHOLD = 40 }; - + static constexpr uint32 INVALID_BYTE_THRESHOLD = 40; + // This threshold is used for files where the file magic only gives a // fragile result which alone would lead to too many false positives. // In particular, the files from Inconexia demo by Iguana // (https://www.pouet.net/prod.php?which=830) which have 3 \0 bytes in // the file magic tend to cause misdetection of random files. - enum : uint32 { INVALID_BYTE_FRAGILE_THRESHOLD = 1 }; + static constexpr uint32 INVALID_BYTE_FRAGILE_THRESHOLD = 1; // Retrieve the internal sample format flags for this sample. static SampleIO GetSampleFormat() @@ -398,7 +398,7 @@ struct AMInstrument const struct { uint16 level, speed; - } points[] = { { startLevel, 0 }, { attack1Level, attack1Speed }, { attack2Level, attack2Speed }, { sustainLevel, decaySpeed }, { sustainLevel, sustainTime }, { 0, releaseSpeed } }; + } points[] = {{startLevel, 0}, {attack1Level, attack1Speed}, {attack2Level, attack2Speed}, {sustainLevel, decaySpeed}, {sustainLevel, sustainTime}, {0, releaseSpeed}}; for(uint8 i = 1; i < CountOf(points); i++) { @@ -499,14 +499,14 @@ static bool IsMagic(const char *magic1, const char (&magic2)[5]) } -static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSample &sample, char (&sampleName)[MAX_SAMPLENAME], bool is4Chn) +static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSample &sample, mpt::charbuf &sampleName, bool is4Chn) { file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(sample, is4Chn); - mpt::String::Read(sampleName, sampleHeader.name); + sampleName = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); // Get rid of weird characters in sample names. - for(auto &c : sampleName) + for(auto &c : sampleName.buf) { if(c > 0 && c < ' ') { @@ -571,8 +571,7 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN int illegalBytes = 0; for(int i = 0; i < 256; i++) { - uint8 data[4]; - file.ReadArray(data); + const auto data = file.ReadArray(); if(data[0] & 0xE0) { illegalBytes++; @@ -620,21 +619,19 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN void CSoundFile::ReadMODPatternEntry(FileReader &file, ModCommand &m) { - uint8 data[4]; - file.ReadArray(data); - ReadMODPatternEntry(data, m); + ReadMODPatternEntry(file.ReadArray(), m); } -void CSoundFile::ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m) +void CSoundFile::ReadMODPatternEntry(const std::array data, ModCommand &m) { // Read Period uint16 period = (((static_cast(data[0]) & 0x0F) << 8) | data[1]); size_t note = NOTE_NONE; if(period > 0 && period != 0xFFF) { - note = mpt::size(ProTrackerPeriodTable) + 23 + NOTE_MIN; - for(size_t i = 0; i < mpt::size(ProTrackerPeriodTable); i++) + note = std::size(ProTrackerPeriodTable) + 23 + NOTE_MIN; + for(size_t i = 0; i < std::size(ProTrackerPeriodTable); i++) { if(period >= ProTrackerPeriodTable[i]) { @@ -664,7 +661,7 @@ void CSoundFile::ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m) struct MODMagicResult { - const MPT_UCHAR_TYPE *madeWithTracker = nullptr; + const mpt::uchar *madeWithTracker = nullptr; uint32 invalidByteThreshold = MODSampleHeader::INVALID_BYTE_THRESHOLD; CHANNELINDEX numChannels = 0; bool isNoiseTracker = false; @@ -792,8 +789,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) MODMagicResult modMagicResult; if(!CheckMODMagic(magic, modMagicResult) - || modMagicResult.numChannels < 1 - || modMagicResult.numChannels > MAX_BASECHANNELS) + || modMagicResult.numChannels < 1 + || modMagicResult.numChannels > MAX_BASECHANNELS) { return false; } @@ -858,7 +855,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) // Read order information MODFileHeader fileHeader; file.ReadStruct(fileHeader); - file.Skip(4); // Magic bytes (we already parsed these) + file.Skip(4); // Magic bytes (we already parsed these) ReadOrderFromArray(Order(), fileHeader.orderList); @@ -963,7 +960,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) extendedPanning = true; } else if(m.command == 0x0E && (m.param & 0xF0) == 0x80) { - maxPanning = std::max(maxPanning, (m.param & 0x0F) << 4); + maxPanning = std::max(maxPanning, static_cast((m.param & 0x0F) << 4)); } } } @@ -1174,12 +1171,12 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) if(!filename.empty()) { // Find instrument definition file - const mpt::PathString exts[] = { P_(".nt"), P_(".NT"), P_(".as"), P_(".AS") }; + const mpt::PathString exts[] = {P_(".nt"), P_(".NT"), P_(".as"), P_(".AS")}; for(const auto &ext : exts) { mpt::PathString infoName = filename + ext; char stMagic[16]; - if(infoName.IsFile() && amFile.Open(infoName) && (amData = GetFileReader(amFile)).IsValid() && amData.ReadArray(stMagic)) + if(infoName.IsFile() && amFile.Open(infoName, SettingCacheCompleteFileBeforeLoading()) && (amData = GetFileReader(amFile)).IsValid() && amData.ReadArray(stMagic)) { if(!memcmp(stMagic, "ST1.2 ModuleINFO", 16)) modMagicResult.madeWithTracker = UL_("Startrekker 1.2"); @@ -1213,7 +1210,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) { break; } - mpt::String::Copy(ins->name, m_szNames[smp]); + ins->name = m_szNames[smp]; AMInstrument am; // Allow partial reads for fa.worse face.mod @@ -1226,7 +1223,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) amData.Skip(120 - sizeof(AMInstrument)); } } -#endif // MPT_EXTERNAL_SAMPLES || MPT_BUILD_FUZZER +#endif // MPT_EXTERNAL_SAMPLES || MPT_BUILD_FUZZER // Fix VBlank MODs. Arbitrary threshold: 10 minutes. // Basically, this just converts all tempo commands into speed commands @@ -1257,10 +1254,11 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) } 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.formatName = mpt::format(U_("ProTracker MOD (%1)"))(mpt::ToUnicode(mpt::Charset::ASCII, std::string(std::begin(magic), std::end(magic)))); m_modFormat.type = U_("mod"); - if(modMagicResult.madeWithTracker) m_modFormat.madeWithTracker = modMagicResult.madeWithTracker; - m_modFormat.charset = mpt::CharsetISO8859_1; + if(modMagicResult.madeWithTracker) + m_modFormat.madeWithTracker = modMagicResult.madeWithTracker; + m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } @@ -1306,7 +1304,7 @@ struct M15FileHeaders MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130) -typedef uint8 M15PatternData[64][4][4]; +typedef std::array M15PatternData[64][4]; static bool ValidateHeader(const M15FileHeaders &fileHeaders) @@ -1333,9 +1331,9 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders) // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) if(invalidChars > 48 - || sampleHeader.volume > 64 - || sampleHeader.finetune != 0 - || sampleHeader.length > 32768) + || sampleHeader.volume > 64 + || sampleHeader.finetune != 0 + || sampleHeader.length > 32768) { return false; } @@ -1475,7 +1473,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) totalSampleLen += Samples[smp].nLength; - if(m_szNames[smp][0] && ((memcmp(m_szNames[smp], "st-", 3) && memcmp(m_szNames[smp], "ST-", 3)) || m_szNames[smp][5] != ':')) + if(m_szNames[smp][0] && ((memcmp(m_szNames[smp].buf, "st-", 3) && memcmp(m_szNames[smp].buf, "ST-", 3)) || m_szNames[smp][5] != ':')) { // Ultimate Soundtracker 1.8 and D.O.C. SoundTracker IX always have sample names containing disk names. hasDiskNames = false; @@ -1521,7 +1519,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) if(!memcmp(songname, "jjk55", 6)) fileHeader.restartPos = 0x78; // Sample 7 in echoing.mod won't "loop" correctly if we don't convert the VBlank tempo. - m_nDefaultTempo.Set(fileHeader.restartPos * 25 / 24); + m_nDefaultTempo.Set(125); if(fileHeader.restartPos != 0x78) { // Convert to CIA timing @@ -1540,7 +1538,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) m_nMaxPeriod = 856 * 4; m_nSamplePreAmp = 64; m_SongFlags.set(SONG_PT_MODE); - mpt::String::Read(m_songName, songname); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, songname); // Setup channel pan positions and volume SetupMODPanning(); @@ -1548,7 +1546,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) FileReader::off_t patOffset = file.GetPosition(); // Scan patterns to identify Ultimate Soundtracker modules. - uint32 illegalBytes = 0; + uint32 illegalBytes = 0, totalNumDxx = 0; for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { bool patternInUse = std::find(Order().cbegin(), Order().cend(), pat) != Order().cend(); @@ -1577,10 +1575,10 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) { for(CHANNELINDEX chn = 0; chn < 4; chn++) { - const uint8 (&data)[4] = patternData[row][chn]; + const auto &data = patternData[row][chn]; const uint8 eff = data[2] & 0x0F, param = data[3]; // Check for empty space between the last Dxx command and the beginning of another pattern - if(emptyCmds != 0 && !memcmp(data, "\0\0\0\0", 4)) + if(emptyCmds != 0 && !memcmp(data.data(), "\0\0\0\0", 4)) { emptyCmds++; if(emptyCmds > 32) @@ -1643,8 +1641,13 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) // Not many Dxx commands in one pattern means they were probably pattern breaks minVersion = ST2_00; } + totalNumDxx += numDxx; } + // If there is a huge number of Dxx commands, this is extremely unlikely to be a SoundTracker 2.0 module + if(totalNumDxx > numPatterns + 32u && minVersion == ST2_00) + minVersion = MST1_00; + file.Seek(patOffset); // Reading patterns @@ -1660,7 +1663,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) continue; } - uint8 autoSlide[4] = { 0, 0, 0, 0 }; + uint8 autoSlide[4] = {0, 0, 0, 0}; for(ROWINDEX row = 0; row < 64; row++) { PatternRow rowBase = Patterns[pat].GetpModCommand(row, 0); @@ -1757,7 +1760,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) } } - const MPT_UCHAR_TYPE *madeWithTracker = UL_(""); + const mpt::uchar *madeWithTracker = UL_(""); switch(minVersion) { case UST1_00: @@ -1786,7 +1789,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Soundtracker"); m_modFormat.type = U_("stk"); m_modFormat.madeWithTracker = madeWithTracker; - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; // Reading samples if(loadFlags & loadSampleData) @@ -1835,8 +1838,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, co { return ProbeFailure; } - const uint8 numOrders = file.ReadUint8(); - const uint8 numTracks = file.ReadUint8(); + const auto [numOrders, numTracks] = file.ReadArray(); if(numOrders > 128) { return ProbeFailure; @@ -1876,13 +1878,13 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("MnemoTroN SoundTracker"); m_modFormat.type = U_("st26"); m_modFormat.madeWithTracker = U_("SoundTracker 2.6"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; } else if(IsMagic(magic, "IT10")) { m_modFormat.formatName = U_("Ice Tracker"); m_modFormat.type = U_("ice"); m_modFormat.madeWithTracker = U_("Ice Tracker 1.0 / 1.1"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; } else { return false; @@ -1905,8 +1907,7 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) return false; } - const uint8 numOrders = file.ReadUint8(); - const uint8 numTracks = file.ReadUint8(); + const auto [numOrders, numTracks] = file.ReadArray(); if(numOrders > 128) { return false; @@ -1942,7 +1943,7 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) // Reading patterns Order().resize(numOrders); - uint8 speed[2] = { 0, 0 }, speedPos = 0; + uint8 speed[2] = {0, 0}, speedPos = 0; Patterns.ResizeArray(numOrders); for(PATTERNINDEX pat = 0; pat < numOrders; pat++) { @@ -1960,8 +1961,8 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) ReadMODPatternEntry(file, *m); if((m->command || m->param) - && !(m->command == 0x0E && m->param >= 0x10) // Exx only sets filter - && !(m->command >= 0x05 && m->command <= 0x09)) // These don't exist in ST2.6 + && !(m->command == 0x0E && m->param >= 0x10) // Exx only sets filter + && !(m->command >= 0x05 && m->command <= 0x09)) // These don't exist in ST2.6 { ConvertModCommand(*m); } else @@ -1998,7 +1999,8 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) { Patterns[pat].WriteEffect(EffectWriter(CMD_SPEED, speed[speedPos - 1]).Row(row)); speedPos++; - if(speedPos == 3) speedPos = 1; + if(speedPos == 3) + speedPos = 1; } } } @@ -2116,7 +2118,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) chunk.Skip(4); if(chunk.ReadMagic("PT") && iffHead.chunksize > 6) { - chunk.ReadString(version, mpt::CharsetISO8859_1, iffHead.chunksize - 6); + chunk.ReadString(version, mpt::Charset::ISO8859_1, iffHead.chunksize - 6); } break; @@ -2145,15 +2147,15 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) bool vblank = (info.flags & 0x100) == 0; m_playBehaviour.set(kMODVBlankTiming, vblank); if(info.volume != 0) - m_nSamplePreAmp = std::min(64, info.volume); + m_nSamplePreAmp = std::min(uint16(64), static_cast(info.volume)); if(info.tempo != 0 && !vblank) m_nDefaultTempo.Set(info.tempo); if(info.name[0]) - mpt::String::Read(m_songName, info.name); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, info.name); if(IsInRange(info.dateMonth, 1, 12) && IsInRange(info.dateDay, 1, 31) && IsInRange(info.dateHour, 0, 23) - && IsInRange(info.dateMinute, 0, 59) && IsInRange(info.dateSecond, 0, 59)) + && IsInRange(info.dateMinute, 0, 59) && IsInRange(info.dateSecond, 0, 59)) { FileHistory mptHistory; mptHistory.loadDate.tm_year = info.dateYear; @@ -2172,7 +2174,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) std::string author; commentChunk.ReadString(author, 32); if(author != "UNNAMED AUTHOR") - m_songArtist = mpt::ToUnicode(mpt::CharsetISO8859_1, author); + m_songArtist = mpt::ToUnicode(mpt::Charset::ISO8859_1, author); if(!commentChunk.NoBytesLeft()) { m_songMessage.ReadFixedLineLength(commentChunk, commentChunk.BytesLeft(), 40, 0); @@ -2202,7 +2204,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const // Write song title { char name[20]; - mpt::String::Write(name, m_songName); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = m_songName; mpt::IO::Write(f, name); } @@ -2215,11 +2217,11 @@ bool CSoundFile::SaveMod(std::ostream &f) const for(INSTRUMENTINDEX ins = 1; ins <= lastIns; ins++) if (Instruments[ins]) { // Find some valid sample associated with this instrument. - for(size_t i = 0; i < CountOf(Instruments[ins]->Keyboard); i++) + for(auto smp : Instruments[ins]->Keyboard) { - if(Instruments[ins]->Keyboard[i] > 0 && Instruments[ins]->Keyboard[i] <= GetNumSamples()) + if(smp > 0 && smp <= GetNumSamples()) { - sampleSource[ins] = Instruments[ins]->Keyboard[i]; + sampleSource[ins] = smp; break; } } @@ -2236,7 +2238,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader; - mpt::String::Write(sampleHeader.name, m_szNames[sampleSource[smp]]); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.name) = m_szNames[sampleSource[smp]]; sampleLength[smp] = sampleHeader.ConvertToMOD(sampleSource[smp] <= GetNumSamples() ? GetSample(sampleSource[smp]) : ModSample(MOD_TYPE_MOD)); mpt::IO::Write(f, sampleHeader); } @@ -2316,7 +2318,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const size_t eventByte = 0; for(CHANNELINDEX chn = 0; chn < writeChannels; chn++, eventByte += 4) { - ModCommand &m = rowBase[chn]; + const ModCommand &m = rowBase[chn]; uint8 command = m.command, param = m.param; ModSaveCommand(command, param, false, true); @@ -2324,12 +2326,12 @@ bool CSoundFile::SaveMod(std::ostream &f) const { // Maybe we can save some volume commands... command = 0x0C; - param = MIN(m.vol, 64); + param = std::min(m.vol, uint8(64)); } uint16 period = 0; // Convert note to period - if(m.note >= 24 + NOTE_MIN && m.note < mpt::size(ProTrackerPeriodTable) + 24 + NOTE_MIN) + if(m.note >= 24 + NOTE_MIN && m.note < std::size(ProTrackerPeriodTable) + 24 + NOTE_MIN) { period = ProTrackerPeriodTable[m.note - 24 - NOTE_MIN]; } @@ -2396,7 +2398,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const return true; } -#endif // MODPLUG_NO_FILESAVE +#endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp index bdd6b73d8..b28a76305 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp @@ -457,10 +457,10 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.madeWithTracker = mpt::ToUnicode(mpt::Charset::Windows1252, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.trackerName)); + m_modFormat.charset = mpt::Charset::Windows1252; - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); m_nChannels = fileHeader.numChannels; m_nDefaultSpeed = Clamp(fileHeader.ticksPerLine, 1, 31); m_nDefaultTempo.Set(125); @@ -482,7 +482,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) const CHANNELINDEX channelsWithoutDrums = m_nChannels; const bool hasDrumChannels = drumData.CanRead(sizeof(MT2DrumsData)); - STATIC_ASSERT(MAX_BASECHANNELS >= 64 + 8); + static_assert(MAX_BASECHANNELS >= 64 + 8); if(hasDrumChannels) { m_nChannels += 8; @@ -549,12 +549,12 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) { for(ROWINDEX row = 0; row < numRows; row++) { - ModCommand *m = Patterns[pat].GetRow(row); - for(CHANNELINDEX chn = 0; chn < channelsWithoutDrums; chn++, m++) + auto rowData = Patterns[pat].GetRow(row); + for(CHANNELINDEX chn = 0; chn < channelsWithoutDrums; chn++) { MT2Command cmd; chunk.ReadStruct(cmd); - hasLegacyTempo |= ConvertMT2Command(this, *m, cmd); + hasLegacyTempo |= ConvertMT2Command(this, rowData[chn], cmd); } } } @@ -609,9 +609,8 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) MT2TrackSettings trackSettings; if(chunk.ReadStruct(trackSettings)) { - ChnSettings[c].nVolume = trackSettings.volume >> 10; // 32768 is 0dB + ChnSettings[c].nVolume = static_cast(trackSettings.volume >> 10); // 32768 is 0dB trackRouting[c] = trackSettings.output; - LimitMax(ChnSettings[c].nVolume, uint16(64)); } } break; @@ -621,7 +620,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) { std::string name; chunk.ReadNullString(name); - mpt::String::Read(ChnSettings[i].szName, name.c_str(), name.length()); + ChnSettings[i].szName = mpt::String::ReadBuf(mpt::String::spacePadded, name.c_str(), name.length()); } break; @@ -650,7 +649,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) chunk.ReadNullString(artist); if(artist != "Unregistered") { - m_songArtist = mpt::ToUnicode(mpt::CharsetWindows1252, artist); + m_songArtist = mpt::ToUnicode(mpt::Charset::Windows1252, artist); } } break; @@ -679,14 +678,14 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) SNDMIXPLUGIN &mixPlug = m_MixPlugins[i]; mixPlug.Destroy(); - mpt::String::Read(mixPlug.Info.szLibraryName, vstHeader.dll); - mpt::String::Read(mixPlug.Info.szName, vstHeader.programName); - const size_t len = strlen(mixPlug.Info.szLibraryName); - if(len > 4 && mixPlug.Info.szLibraryName[len - 4] == '.') + std::string libraryName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, vstHeader.dll); + mixPlug.Info.szName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, vstHeader.programName); + if(libraryName.length() > 4 && libraryName[libraryName.length() - 4] == '.') { // Remove ".dll" from library name - mixPlug.Info.szLibraryName[len - 4] = '\0'; + libraryName.resize(libraryName.length() - 4 ); } + mixPlug.Info.szLibraryName = libraryName; mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; mixPlug.Info.dwPluginId2 = vstHeader.fxID; if(vstHeader.track >= m_nChannels) @@ -797,8 +796,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) ModInstrument *mptIns = AllocateInstrument(drumMap[i], drumHeader.DrumSamples[i] + 1); if(mptIns != nullptr) { - strcpy(mptIns->name, "Drum #x"); - mptIns->name[6] = '1' + char(i); + mptIns->name = mpt::format("Drum #%1")(i+1); } } else { @@ -910,7 +908,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) if(mptIns == nullptr) continue; - mpt::String::Read(mptIns->name, instrName); + mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrName); if(!dataLength) continue; @@ -945,7 +943,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) mptEnv.dwFlags.set(ENV_ENABLED, (mt2Env.flags & 1) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (mt2Env.flags & 2) != 0); mptEnv.dwFlags.set(ENV_LOOP, (mt2Env.flags & 4) != 0); - mptEnv.resize(std::min(mt2Env.numPoints, 16)); + mptEnv.resize(std::min(mt2Env.numPoints.get(), uint8(16))); mptEnv.nSustainStart = mptEnv.nSustainEnd = mt2Env.sustainPos; mptEnv.nLoopStart = mt2Env.loopStart; mptEnv.nLoopEnd = mt2Env.loopEnd; @@ -976,7 +974,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) mptIns->SetCutoff(FrequencyToCutOff(synthData.cutoff), true); mptIns->SetResonance(synthData.resonance, true); } - mptIns->nFilterMode = synthData.effectID == 1 ? FLTMODE_HIGHPASS : FLTMODE_LOWPASS; + mptIns->filterMode = synthData.effectID == 1 ? FilterMode::HighPass : FilterMode::LowPass; if(flags & 4) { // VSTi / MIDI synth enabled @@ -989,7 +987,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } if(synthData.transpose) { - for(uint32 n = 0; n < CountOf(mptIns->NoteMap); n++) + for(uint32 n = 0; n < std::size(mptIns->NoteMap); n++) { int note = NOTE_MIN + n + synthData.transpose; Limit(note, NOTE_MIN, NOTE_MAX); @@ -1015,7 +1013,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) if(i < fileHeader.numSamples) { - mpt::String::Read(m_szNames[i + 1], sampleName); + m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleName); } if(dataLength && i < fileHeader.numSamples) @@ -1144,7 +1142,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) file.Skip(12); // Reserved std::string filename; file.ReadString(filename, filenameSize); - mpt::String::Copy(mptSmp.filename, filename); + mptSmp.filename = filename; #if defined(MPT_EXTERNAL_SAMPLES) if(filename.length() >= 2 diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp index 0c2231a0e..ca24f4e6a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp @@ -134,14 +134,14 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_MTM); - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); m_nSamples = fileHeader.numSamples; m_nChannels = fileHeader.numChannels; m_modFormat.formatName = U_("MultiTracker"); m_modFormat.type = U_("mtm"); m_modFormat.madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(fileHeader.version >> 4, fileHeader.version & 0x0F); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; // Reading instruments for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) @@ -149,7 +149,7 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) MTMSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.samplename); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.samplename); } // Setting Channel Pan Position @@ -190,13 +190,13 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) ModCommand *m = Patterns[pat].GetpModCommand(0, chn); for(ROWINDEX row = 0; row < rowsPerPat; row++, m += GetNumChannels()) { - uint8 data[3]; - tracks.ReadArray(data); + const auto [noteInstr, instrCmd, par] = tracks.ReadArray(); - if(data[0] & 0xFC) m->note = (data[0] >> 2) + 36 + NOTE_MIN; - m->instr = ((data[0] & 0x03) << 4) | (data[1] >> 4); - uint8 cmd = data[1] & 0x0F; - uint8 param = data[2]; + if(noteInstr & 0xFC) + m->note = (noteInstr >> 2) + 36 + NOTE_MIN; + m->instr = ((noteInstr & 0x03) << 4) | (instrCmd >> 4); + uint8 cmd = instrCmd & 0x0F; + uint8 param = par; if(cmd == 0x0A) { if(param & 0xF0) param &= 0xF0; else param &= 0x0F; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp index e2be1a04a..3544a89bb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp @@ -51,7 +51,7 @@ MPT_BINARY_STRUCT(OktSample, 32) // Parse the sample header block static void ReadOKTSamples(FileReader &chunk, std::vector &sample7bit, CSoundFile &sndFile) { - sndFile.m_nSamples = std::min(static_cast(chunk.BytesLeft() / sizeof(OktSample)), MAX_SAMPLES - 1u); + sndFile.m_nSamples = std::min(static_cast(chunk.BytesLeft() / sizeof(OktSample)), static_cast(MAX_SAMPLES - 1)); sample7bit.resize(sndFile.GetNumSamples()); for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++) @@ -61,11 +61,11 @@ static void ReadOKTSamples(FileReader &chunk, std::vector &sample7bit, CSo chunk.ReadStruct(oktSmp); mptSmp.Initialize(); - mpt::String::Read(sndFile.m_szNames[smp], oktSmp.name); + sndFile.m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, oktSmp.name); mptSmp.nC5Speed = 8287; mptSmp.nGlobalVol = 64; - mptSmp.nVolume = std::min(oktSmp.volume, 64u) * 4u; + mptSmp.nVolume = std::min(oktSmp.volume.get(), uint16(64)) * 4u; mptSmp.nLength = oktSmp.length & ~1; // round down // Parse loops const SmpLength loopStart = oktSmp.loopStart * 2; @@ -106,10 +106,8 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF 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(); + const auto [note, instr, effect, param] = chunk.ReadArray(); + m.param = param; if(note > 0 && note <= 36) { @@ -154,14 +152,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF if (m.param) { m.command = CMD_NOTESLIDEDOWN; - m.param = 0x10 | MIN(0x0F, m.param); + m.param = 0x10 | std::min(uint8(0x0F), m.param); } break; case 30: // U Slide Up (Notes) if (m.param) { m.command = CMD_NOTESLIDEUP; - m.param = 0x10 | MIN(0x0F, m.param); + m.param = 0x10 | std::min(uint8(0x0F), m.param); } break; // We don't have fine note slide, but this is supposed to happen once @@ -171,14 +169,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF if (m.param) { m.command = CMD_NOTESLIDEDOWN; - m.param = 0x50 | MIN(0x0F, m.param); + m.param = 0x50 | std::min(uint8(0x0F), m.param); } break; case 17: // H Slide Up Once (Notes) if (m.param) { m.command = CMD_NOTESLIDEUP; - m.param = 0x50 | MIN(0x0F, m.param); + m.param = 0x50 | std::min(uint8(0x0F), m.param); } break; @@ -211,7 +209,7 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF break; } // 0x40 is set volume -- fall through - MPT_FALLTHROUGH; + [[fallthrough]]; case 0: case 1: case 2: case 3: m.volcmd = VOLCMD_VOLUME; m.vol = m.param; @@ -222,10 +220,10 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF m.param = (m.param & 0x0F) << 4; // Dx0 break; case 6: - m.param = 0xF0 | MIN(m.param & 0x0F, 0x0E); // DFx + m.param = 0xF0 | std::min(static_cast(m.param & 0x0F), uint8(0x0E)); // DFx break; case 7: - m.param = (MIN(m.param & 0x0F, 0x0E) << 4) | 0x0F; // DxF + m.param = (std::min(static_cast(m.param & uint8(0x0F)), uint8(0x0E)) << 4) | 0x0F; // DxF break; default: // Junk. @@ -243,7 +241,8 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF #endif default: - m.command = m.param = 0; + m.command = CMD_NONE; + m.param = 0; break; } } @@ -297,7 +296,7 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Oktalyzer"); m_modFormat.type = U_("okt"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; // Go through IFF chunks... while(file.CanRead(sizeof(OktIffChunk))) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp index 0eef50654..3bf698ea2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp @@ -151,10 +151,10 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Disorder Tracker 2"); m_modFormat.type = U_("plm"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; // Some PLMs use ASCIIZ, some space-padding strings...weird. Oh, and the file browser stops at 0 bytes in the name, the main GUI doesn't. - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); m_nChannels = fileHeader.numChannels + 1; // Additional channel for writing pattern breaks m_nSamplePreAmp = fileHeader.amplify; m_nDefaultTempo.Set(fileHeader.tempo); @@ -183,14 +183,14 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) || !file.ReadStruct(sampleHeader)) continue; - mpt::String::Read(m_szNames[smp + 1], sampleHeader.name); - mpt::String::Read(sample.filename, sampleHeader.filename); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); + sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); if(sampleHeader.panning <= 15) { sample.uFlags.set(CHN_PANNING); sample.nPan = sampleHeader.panning * 0x11; } - sample.nGlobalVol = std::min(sampleHeader.volume, 64); + sample.nGlobalVol = std::min(sampleHeader.volume.get(), uint8(64)); sample.nC5Speed = sampleHeader.sampleRate; sample.nLoopStart = sampleHeader.loopStart; sample.nLoopEnd = sampleHeader.loopEnd; @@ -229,7 +229,7 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) const ROWINDEX rowsPerPat = 64; uint32 maxPos = 0; - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_PORTAMENTOUP, @@ -267,10 +267,10 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) file.ReadStruct(patHeader); if(!patHeader.numRows) continue; - STATIC_ASSERT(ORDERINDEX_MAX >= ((mpt::limits::max)() + 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); + const CHANNELINDEX numChannels = std::min(patHeader.numChannels.get(), static_cast(fileHeader.numChannels - ord.y)); const uint32 patternEnd = ord.x + patHeader.numRows; maxPos = std::max(maxPos, patternEnd); @@ -293,25 +293,24 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) ModCommand *m = Patterns[pat].GetpModCommand(curRow, ord.y); for(CHANNELINDEX c = 0; c < numChannels; c++, m++) { - uint8 data[5]; - file.ReadArray(data); - if(data[0]) - lastNote[c] = m->note = (data[0] >> 4) * 12 + (data[0] & 0x0F) + 12 + NOTE_MIN; + const auto [note, instr, volume, command, param] = file.ReadArray(); + if(note > 0 && note < 0x90) + lastNote[c] = m->note = (note >> 4) * 12 + (note & 0x0F) + 12 + NOTE_MIN; else m->note = NOTE_NONE; - m->instr = data[1]; + m->instr = instr; m->volcmd = VOLCMD_VOLUME; - if(data[2] != 0xFF) - m->vol = data[2]; + if(volume != 0xFF) + m->vol = volume; else m->volcmd = VOLCMD_NONE; - if(data[3] < CountOf(effTrans)) + if(command < CountOf(effTrans)) { - m->command = effTrans[data[3]]; - m->param = data[4]; + m->command = effTrans[command]; + m->param = param; // Fix some commands - switch(data[3]) + switch(command) { case 0x07: // Tremolo waveform m->param = 0x40 | (m->param & 0x03); @@ -341,13 +340,13 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) m->param = 0x80 | (m->param & 0x0F); break; case 0x10: // Delay Note - m->param = 0xD0 | std::min(m->param, 0x0F); + m->param = 0xD0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x11: // Cut Note - m->param = 0xC0 | std::min(m->param, 0x0F); + m->param = 0xC0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x12: // Pattern Delay - m->param = 0xE0 | std::min(m->param, 0x0F); + m->param = 0xE0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x04: // Volume Slide case 0x14: // Vibrato + Volume Slide diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp index d03f09260..9f0f41c73 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp @@ -27,9 +27,9 @@ OPENMPT_NAMESPACE_BEGIN // PSM File Header struct PSMFileHeader { - char formatID[4]; // "PSM " (new format) - uint32le fileSize; // Filesize - 12 - char fileInfoID[4]; // "FILE" + char formatID[4]; // "PSM " (new format) + uint32le fileSize; // Filesize - 12 + char fileInfoID[4]; // "FILE" }; MPT_BINARY_STRUCT(PSMFileHeader, 12) @@ -71,9 +71,9 @@ MPT_BINARY_STRUCT(PSMChunk, 8) // Song Information struct PSMSongHeader { - char songType[9]; // Mostly "MAINSONG " (But not in Extreme Pinball!) - uint8 compression; // 1 - uncompressed - uint8 numChannels; // Number of channels + char songType[9]; // Mostly "MAINSONG " (But not in Extreme Pinball!) + uint8 compression; // 1 - uncompressed + uint8 numChannels; // Number of channels }; @@ -83,26 +83,26 @@ MPT_BINARY_STRUCT(PSMSongHeader, 11) struct PSMSampleHeader { uint8le flags; - char fileName[8]; // Filename of the original module (without extension) - char sampleID[4]; // INS0...INS9 (only last digit of sample ID, i.e. sample 1 and sample 11 are equal) + char fileName[8]; // Filename of the original module (without extension) + char sampleID[4]; // Identifier like "INS0" (only last digit of sample ID, i.e. sample 1 and sample 11 are equal) or "I0 " char sampleName[33]; - uint8le unknown1[6]; // 00 00 00 00 00 FF + uint8le unknown1[6]; // 00 00 00 00 00 FF uint16le sampleNumber; uint32le sampleLength; uint32le loopStart; - uint32le loopEnd; // FF FF FF FF = end of sample + uint32le loopEnd; // FF FF FF FF = end of sample uint8le unknown3; - uint8le finetune; // unused? always 0 + uint8le finetune; // unused? always 0 uint8le defaultVolume; uint32le unknown4; - uint32le c5Freq; // MASI ignores the high 16 bits - char padding[19]; // 00 ... 00 + uint32le c5Freq; // MASI ignores the high 16 bits + char padding[19]; // Convert header data to OpenMPT's internal format void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, fileName); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileName); mptSmp.nC5Speed = c5Freq; mptSmp.nLength = sampleLength; @@ -126,26 +126,26 @@ MPT_BINARY_STRUCT(PSMSampleHeader, 96) struct PSMSinariaSampleHeader { uint8le flags; - char fileName[8]; // Filename of the original module (without extension) - char sampleID[8]; // INS0...INS99999 + char fileName[8]; // Filename of the original module (without extension) + char sampleID[8]; // INS0...INS99999 char sampleName[33]; - uint8le unknown1[6]; // 00 00 00 00 00 FF + uint8le unknown1[6]; // 00 00 00 00 00 FF uint16le sampleNumber; uint32le sampleLength; uint32le loopStart; uint32le loopEnd; uint16le unknown3; - uint8le finetune; // Possibly finetune like in PSM16, but sounds even worse than just ignoring it + uint8le finetune; // Appears to be unused uint8le defaultVolume; uint32le unknown4; uint16le c5Freq; - char padding[16]; // 00 ... 00 + char padding[16]; // Convert header data to OpenMPT's internal format void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, fileName); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileName); mptSmp.nC5Speed = c5Freq; mptSmp.nLength = sampleLength; @@ -167,15 +167,13 @@ struct PSMSubSong // For internal use (pattern conversion) std::vector channelSurround; ORDERINDEX startOrder = ORDERINDEX_INVALID, endOrder = ORDERINDEX_INVALID, restartPos = 0; uint8 defaultTempo = 125, defaultSpeed = 6; - char songName[10]; + char songName[10] = {}; PSMSubSong() - { - channelPanning.assign(MAX_BASECHANNELS, 128); - channelVolume.assign(MAX_BASECHANNELS, 64); - channelSurround.assign(MAX_BASECHANNELS, false); - MemsetZero(songName); - } + : channelPanning(MAX_BASECHANNELS, 128) + , channelVolume(MAX_BASECHANNELS, 64) + , channelSurround(MAX_BASECHANNELS, false) + { } }; @@ -245,7 +243,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, co { return ProbeFailure; } - if((chunkHeader.id & 0x7f7f7f7fu) != chunkHeader.id) // ASCII? + if((chunkHeader.id & 0x7F7F7F7Fu) != chunkHeader.id) // ASCII? { return ProbeFailure; } @@ -265,7 +263,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) #ifdef MPT_PSM_DECRYPT // CONVERT.EXE /K - I don't think any game ever used this. - std::vector decrypted; + std::vector decrypted; if(!memcmp(fileHeader.formatID, "QUP$", 4) && !memcmp(fileHeader.fileInfoID, "OSWQ", 4)) { @@ -331,16 +329,16 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) m_nChannels = Clamp(static_cast(songHeader.numChannels), m_nChannels, MAX_BASECHANNELS); PSMSubSong subsong; - mpt::String::Read(subsong.songName, songHeader.songType); + mpt::String::WriteAutoBuf(subsong.songName) = mpt::String::ReadBuf(mpt::String::nullTerminated, songHeader.songType); #ifdef MPT_PSM_USE_REAL_SUBSONGS if(!Order().empty()) { // Add a new sequence for this subsong - if(Order.AddSequence(false) == SEQUENCEINDEX_INVALID) + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; } - Order().SetName(subsong.songName); + Order().SetName(mpt::ToUnicode(mpt::Charset::CP437, subsong.songName)); #endif // MPT_PSM_USE_REAL_SUBSONGS // Read "Sub chunks" @@ -454,9 +452,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) case 0x0D: // Channel panning table - can be set using CONVERT.EXE /E { - uint8 chn = subChunk.ReadUint8(); - uint8 pan = subChunk.ReadUint8(); - uint8 type = subChunk.ReadUint8(); + const auto [chn, pan, type] = subChunk.ReadArray(); if(chn < subsong.channelPanning.size()) { switch(type) @@ -489,8 +485,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) case 0x0E: // Channel volume table (0...255) - can be set using CONVERT.EXE /E, is 255 in all "official" PSMs except for some OMF 2097 tracks { - uint8 chn = subChunk.ReadUint8(); - uint8 vol = subChunk.ReadUint8(); + const auto [chn, vol] = subChunk.ReadArray(); if(chn < subsong.channelVolume.size()) { subsong.channelVolume[chn] = (vol / 4u) + 1; @@ -516,8 +511,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) if(!subChunk.CanRead(2)) break; - uint8 type = subChunk.ReadUint8(); - uint8 pan = subChunk.ReadUint8(); + const auto [type, pan] = subChunk.ReadArray(); switch(type) { case 0: // use panning @@ -583,34 +577,28 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) // Original header PSMSampleHeader sampleHeader; if(!chunk.ReadStruct(sampleHeader)) - { continue; - } smp = static_cast(sampleHeader.sampleNumber + 1); if(smp > 0 && smp < MAX_SAMPLES) { m_nSamples = std::max(m_nSamples, smp); - mpt::String::Read(m_szNames[smp], sampleHeader.sampleName); - sampleHeader.ConvertToMPT(Samples[smp]); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.sampleName); } } else { // Sinaria uses a slightly different sample header PSMSinariaSampleHeader sampleHeader; if(!chunk.ReadStruct(sampleHeader)) - { continue; - } smp = static_cast(sampleHeader.sampleNumber + 1); if(smp > 0 && smp < MAX_SAMPLES) { m_nSamples = std::max(m_nSamples, smp); - mpt::String::Read(m_szNames[smp], sampleHeader.sampleName); - sampleHeader.ConvertToMPT(Samples[smp]); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.sampleName); } } if(smp > 0 && smp < MAX_SAMPLES) @@ -638,7 +626,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = mpt::Charset::CP437; if(!(loadFlags & loadPatternData) || m_nChannels == 0) { @@ -687,10 +675,9 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) while(rowChunk.CanRead(3)) { - uint8 flags = rowChunk.ReadUint8(); - uint8 channel = rowChunk.ReadUint8(); + const auto [flags, channel] = rowChunk.ReadArray(); // Point to the correct channel - ModCommand &m = rowBase[std::min(m_nChannels - 1, channel)]; + ModCommand &m = rowBase[std::min(static_cast(m_nChannels - 1), static_cast(channel))]; if(flags & noteFlag) { @@ -720,17 +707,17 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) // Volume present uint8 vol = rowChunk.ReadUint8(); m.volcmd = VOLCMD_VOLUME; - m.vol = (MIN(vol, 127) + 1) / 2; + m.vol = (std::min(vol, uint8(127)) + 1) / 2; } if(flags & effectFlag) { // Effect present - convert - m.command = rowChunk.ReadUint8(); - m.param = rowChunk.ReadUint8(); + const auto [command, param] = rowChunk.ReadArray(); + m.param = param; // This list is annoyingly similar to PSM16, but not quite identical. - switch(m.command) + switch(command) { // Volslides case 0x01: // fine volslide up @@ -1014,7 +1001,7 @@ struct PSM16SampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; @@ -1024,7 +1011,7 @@ struct PSM16SampleHeader mptSmp.nC5Speed = c2freq; mptSmp.Transpose(((finetune ^ 0x08) - 0x78) / (12.0 * 16.0)); - mptSmp.nVolume = std::min(volume, 64u) * 4u; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; mptSmp.uFlags.reset(); if(flags & PSM16SampleHeader::smp16Bit) @@ -1132,7 +1119,7 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Epic MegaGames MASI (Old Version)"); m_modFormat.type = U_("psm"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nChannels = Clamp(CHANNELINDEX(fileHeader.numChannelsPlay), CHANNELINDEX(fileHeader.numChannelsReal), MAX_BASECHANNELS); m_nSamplePreAmp = fileHeader.masterVolume; @@ -1144,7 +1131,7 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultSpeed = fileHeader.songSpeed; m_nDefaultTempo.Set(fileHeader.songTempo); - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); // Read orders if(fileHeader.orderOffset > 4 && file.Seek(fileHeader.orderOffset - 4) && file.ReadMagic("PORD")) @@ -1176,13 +1163,13 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) break; } - SAMPLEINDEX smp = sampleHeader.sampleNumber; - if(smp > 0 && smp < MAX_SAMPLES) + const SAMPLEINDEX smp = sampleHeader.sampleNumber; + if(smp > 0 && smp < MAX_SAMPLES && !Samples[smp].HasSampleData()) { m_nSamples = std::max(m_nSamples, smp); sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(loadFlags & loadSampleData) { @@ -1241,13 +1228,14 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) continue; } - ModCommand &m = *Patterns[pat].GetpModCommand(curRow, std::min(chnFlag & channelMask, m_nChannels - 1)); + ModCommand &m = *Patterns[pat].GetpModCommand(curRow, std::min(static_cast(chnFlag & channelMask), static_cast(m_nChannels - 1))); if(chnFlag & noteFlag) { // note + instr present - m.note = patternChunk.ReadUint8() + 36; - m.instr = patternChunk.ReadUint8(); + const auto [note, instr] = patternChunk.ReadArray(); + m.note = note + 36; + m.instr = instr; } if(chnFlag & volFlag) { @@ -1258,10 +1246,10 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) if(chnFlag & effectFlag) { // effect present - convert - m.command = patternChunk.ReadUint8(); - m.param = patternChunk.ReadUint8(); + const auto [command, param] = patternChunk.ReadArray(); + m.param = param; - switch(m.command) + switch(command) { // Volslides case 0x01: // fine volslide up diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp index 3e2d77602..22c3bfddd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp @@ -65,10 +65,10 @@ struct PTMSampleHeader SampleIO ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(MOD_TYPE_S3M); - mptSmp.nVolume = std::min(volume, 64) * 4; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; mptSmp.nC5Speed = c4speed * 2; - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); SampleIO sampleIO( SampleIO::_8bit, @@ -168,16 +168,16 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_PTM); - mpt::String::Read(m_songName, fileHeader.songname); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname); 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_modFormat.charset = mpt::Charset::CP437; m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = fileHeader.numChannels; - m_nSamples = std::min(fileHeader.numSamples, MAX_SAMPLES - 1); + m_nSamples = std::min(static_cast(fileHeader.numSamples), static_cast(MAX_SAMPLES - 1)); ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.numOrders, 0xFF, 0xFE); // Reading channel panning @@ -195,7 +195,7 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) sampleHeaderChunk.ReadStruct(sampleHeader); ModSample &sample = Samples[smp + 1]; - mpt::String::Read(m_szNames[smp + 1], sampleHeader.samplename); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.samplename); SampleIO sampleIO = sampleHeader.ConvertToMPT(sample); if((loadFlags & loadSampleData) && sample.nLength && file.Seek(sampleHeader.dataOffset)) @@ -238,8 +238,9 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) if(b & 0x20) { - m.note = file.ReadUint8(); - m.instr = file.ReadUint8(); + const auto [note, instr] = file.ReadArray(); + m.note = note; + m.instr = instr; if(m.note == 254) m.note = NOTE_NOTECUT; else if(!m.note || m.note > 120) @@ -247,10 +248,11 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) } if(b & 0x40) { - m.command = file.ReadUint8(); - m.param = file.ReadUint8(); + const auto [command, param] = file.ReadArray(); + m.command = command; + m.param = param; - static const EffectCommand effTrans[] = { CMD_GLOBALVOLUME, CMD_RETRIG, CMD_FINEVIBRATO, CMD_NOTESLIDEUP, CMD_NOTESLIDEDOWN, CMD_NOTESLIDEUPRETRIG, CMD_NOTESLIDEDOWNRETRIG, CMD_REVERSEOFFSET }; + static constexpr EffectCommand effTrans[] = { CMD_GLOBALVOLUME, CMD_RETRIG, CMD_FINEVIBRATO, CMD_NOTESLIDEUP, CMD_NOTESLIDEDOWN, CMD_NOTESLIDEUPRETRIG, CMD_NOTESLIDEDOWNRETRIG, CMD_REVERSEOFFSET }; if(m.command < 0x10) { // Beware: Effect letters are as in MOD, but portamento and volume slides behave like in S3M (i.e. fine slides share the same effect letters) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp index 5171166b0..c0f3a5151 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp @@ -28,37 +28,38 @@ void CSoundFile::S3MConvert(ModCommand &m, bool fromIT) { switch(m.command | 0x40) { - case 'A': m.command = CMD_SPEED; break; - case 'B': m.command = CMD_POSITIONJUMP; break; - case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break; - case 'D': m.command = CMD_VOLUMESLIDE; break; - case 'E': m.command = CMD_PORTAMENTODOWN; break; - case 'F': m.command = CMD_PORTAMENTOUP; break; - case 'G': m.command = CMD_TONEPORTAMENTO; break; - case 'H': m.command = CMD_VIBRATO; break; - case 'I': m.command = CMD_TREMOR; break; - case 'J': m.command = CMD_ARPEGGIO; break; - case 'K': m.command = CMD_VIBRATOVOL; break; - case 'L': m.command = CMD_TONEPORTAVOL; break; - case 'M': m.command = CMD_CHANNELVOLUME; break; - case 'N': m.command = CMD_CHANNELVOLSLIDE; break; - case 'O': m.command = CMD_OFFSET; break; - case 'P': m.command = CMD_PANNINGSLIDE; break; - case 'Q': m.command = CMD_RETRIG; break; - case 'R': m.command = CMD_TREMOLO; break; - case 'S': m.command = CMD_S3MCMDEX; break; - case 'T': m.command = CMD_TEMPO; break; - case 'U': m.command = CMD_FINEVIBRATO; break; - case 'V': m.command = CMD_GLOBALVOLUME; break; - case 'W': m.command = CMD_GLOBALVOLSLIDE; break; - case 'X': m.command = CMD_PANNING8; break; - case 'Y': m.command = CMD_PANBRELLO; break; - case 'Z': m.command = CMD_MIDI; break; - case '\\': m.command = static_cast(fromIT ? CMD_SMOOTHMIDI : CMD_MIDI); break; + case '@': m.command = (m.param ? CMD_DUMMY : CMD_NONE); break; + case 'A': m.command = CMD_SPEED; break; + case 'B': m.command = CMD_POSITIONJUMP; break; + case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break; + case 'D': m.command = CMD_VOLUMESLIDE; break; + case 'E': m.command = CMD_PORTAMENTODOWN; break; + case 'F': m.command = CMD_PORTAMENTOUP; break; + case 'G': m.command = CMD_TONEPORTAMENTO; break; + case 'H': m.command = CMD_VIBRATO; break; + case 'I': m.command = CMD_TREMOR; break; + case 'J': m.command = CMD_ARPEGGIO; break; + case 'K': m.command = CMD_VIBRATOVOL; break; + case 'L': m.command = CMD_TONEPORTAVOL; break; + case 'M': m.command = CMD_CHANNELVOLUME; break; + case 'N': m.command = CMD_CHANNELVOLSLIDE; break; + case 'O': m.command = CMD_OFFSET; break; + case 'P': m.command = CMD_PANNINGSLIDE; break; + case 'Q': m.command = CMD_RETRIG; break; + case 'R': m.command = CMD_TREMOLO; break; + case 'S': m.command = CMD_S3MCMDEX; break; + case 'T': m.command = CMD_TEMPO; break; + case 'U': m.command = CMD_FINEVIBRATO; break; + case 'V': m.command = CMD_GLOBALVOLUME; break; + case 'W': m.command = CMD_GLOBALVOLSLIDE; break; + case 'X': m.command = CMD_PANNING8; break; + case 'Y': m.command = CMD_PANBRELLO; break; + case 'Z': m.command = CMD_MIDI; break; + case '\\': m.command = static_cast(fromIT ? CMD_SMOOTHMIDI : CMD_MIDI); break; // Chars under 0x40 don't save properly, so map : to ] and # to [. - case ']': m.command = CMD_DELAYCUT; break; - case '[': m.command = CMD_XPARAM; break; - default: m.command = CMD_NONE; + case ']': m.command = CMD_DELAYCUT; break; + case '[': m.command = CMD_XPARAM; break; + default: m.command = CMD_NONE; } } @@ -68,29 +69,30 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co { switch(command) { - case CMD_SPEED: command = 'A'; break; - case CMD_POSITIONJUMP: command = 'B'; break; - case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break; - case CMD_VOLUMESLIDE: command = 'D'; break; - case CMD_PORTAMENTODOWN: command = 'E'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; - case CMD_PORTAMENTOUP: command = 'F'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; - case CMD_TONEPORTAMENTO: command = 'G'; break; - case CMD_VIBRATO: command = 'H'; break; - case CMD_TREMOR: command = 'I'; break; - case CMD_ARPEGGIO: command = 'J'; break; - case CMD_VIBRATOVOL: command = 'K'; break; - case CMD_TONEPORTAVOL: command = 'L'; break; - case CMD_CHANNELVOLUME: command = 'M'; break; - case CMD_CHANNELVOLSLIDE: command = 'N'; break; - case CMD_OFFSET: command = 'O'; break; - case CMD_PANNINGSLIDE: command = 'P'; break; - case CMD_RETRIG: command = 'Q'; break; - case CMD_TREMOLO: command = 'R'; break; - case CMD_S3MCMDEX: command = 'S'; break; - case CMD_TEMPO: command = 'T'; break; - case CMD_FINEVIBRATO: command = 'U'; break; - case CMD_GLOBALVOLUME: command = 'V'; break; - case CMD_GLOBALVOLSLIDE: command = 'W'; break; + case CMD_DUMMY: command = (param ? '@' : 0); break; + case CMD_SPEED: command = 'A'; break; + case CMD_POSITIONJUMP: command = 'B'; break; + case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break; + case CMD_VOLUMESLIDE: command = 'D'; break; + case CMD_PORTAMENTODOWN: command = 'E'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; + case CMD_PORTAMENTOUP: command = 'F'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; + case CMD_TONEPORTAMENTO: command = 'G'; break; + case CMD_VIBRATO: command = 'H'; break; + case CMD_TREMOR: command = 'I'; break; + case CMD_ARPEGGIO: command = 'J'; break; + case CMD_VIBRATOVOL: command = 'K'; break; + case CMD_TONEPORTAVOL: command = 'L'; break; + case CMD_CHANNELVOLUME: command = 'M'; break; + case CMD_CHANNELVOLSLIDE: command = 'N'; break; + case CMD_OFFSET: command = 'O'; break; + case CMD_PANNINGSLIDE: command = 'P'; break; + case CMD_RETRIG: command = 'Q'; break; + case CMD_TREMOLO: command = 'R'; break; + case CMD_S3MCMDEX: command = 'S'; break; + case CMD_TEMPO: command = 'T'; break; + case CMD_FINEVIBRATO: command = 'U'; break; + case CMD_GLOBALVOLUME: command = 'V'; break; + case CMD_GLOBALVOLSLIDE: command = 'W'; break; case CMD_PANNING8: command = 'X'; if(toIT && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD))) @@ -104,8 +106,8 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co param >>= 1; } break; - case CMD_PANBRELLO: command = 'Y'; break; - case CMD_MIDI: command = 'Z'; break; + case CMD_PANBRELLO: command = 'Y'; break; + case CMD_MIDI: command = 'Z'; break; case CMD_SMOOTHMIDI: if(compatibilityExport || !toIT) command = 'Z'; @@ -115,10 +117,10 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co case CMD_XFINEPORTAUPDOWN: switch(param & 0xF0) { - case 0x10: command = 'F'; param = (param & 0x0F) | 0xE0; break; - case 0x20: command = 'E'; param = (param & 0x0F) | 0xE0; break; - case 0x90: command = 'S'; break; - default: command = 0; + case 0x10: command = 'F'; param = (param & 0x0F) | 0xE0; break; + case 0x20: command = 'E'; param = (param & 0x0F) | 0xE0; break; + case 0x90: command = 'S'; break; + default: command = 0; } break; case CMD_MODCMDEX: @@ -134,16 +136,10 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co return; // Chars under 0x40 don't save properly, so map : to ] and # to [. case CMD_DELAYCUT: - if(compatibilityExport || !toIT) - command = 0; - else - command = ']'; + command = (compatibilityExport || !toIT) ? 0 : ']'; break; case CMD_XPARAM: - if(compatibilityExport || !toIT) - command = 0; - else - command = '['; + command = (compatibilityExport || !toIT) ? 0 : '['; break; default: command = 0; @@ -248,7 +244,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0) { // MPT 1.16 and older versions of OpenMPT - Simply keep default (filter) MIDI macros - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker / OpenMPT"); keepMidiMacros = true; nonCompatTracker = true; @@ -287,6 +283,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_FileHistory.push_back(hist); } nonCompatTracker = true; + m_playBehaviour.set(kST3SampleSwap); // Not exactly like ST3, but close enough m_nMinPeriod = 1; break; case S3MFileHeader::trkSchismTracker: @@ -327,7 +324,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) 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; + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; if(nonCompatTracker) { @@ -355,12 +352,12 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_MidiCfg.ClearZxxMacros(); } - mpt::String::Read(m_songName, fileHeader.name); + m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.name); if(fileHeader.flags & S3MFileHeader::amigaLimits) m_SongFlags.set(SONG_AMIGALIMITS); if(fileHeader.flags & S3MFileHeader::st2Vibrato) m_SongFlags.set(SONG_S3MOLDVIBRATO); - if(fileHeader.cwtv < S3MFileHeader::trkST3_20 || (fileHeader.flags & S3MFileHeader::fastVolumeSlides) != 0) + if(fileHeader.cwtv == S3MFileHeader::trkST3_00 || (fileHeader.flags & S3MFileHeader::fastVolumeSlides) != 0) { m_SongFlags.set(SONG_FASTVOLSLIDES); } @@ -382,7 +379,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } // Global Volume - m_nDefaultGlobalVolume = std::min(fileHeader.globalVol, 64) * 4u; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(64)) * 4u; // The following check is probably not very reliable, but it fixes a few tunes, e.g. // DARKNESS.S3M by Purple Motion (ST 3.00) and "Image of Variance" by C.C.Catch (ST 3.01): if(m_nDefaultGlobalVolume == 0 && fileHeader.cwtv < S3MFileHeader::trkST3_20) @@ -390,8 +387,15 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; } - // Bit 7 = Stereo (we always use stereo) - m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); + if(fileHeader.formatVersion == S3MFileHeader::oldVersion && fileHeader.masterVolume < 8) + m_nSamplePreAmp = std::min((fileHeader.masterVolume + 1) * 0x10, 0x7F); + // These changes were probably only supposed to be done for older format revisions, where supposedly 0x10 was the stereo flag. + // However, this version check is missing in ST3, so any mono file with a master volume of 18 will be converted to a stereo file with master volume 32. + else if(fileHeader.masterVolume == 2 || fileHeader.masterVolume == (2 | 0x10)) + m_nSamplePreAmp = 0x20; + else + m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); // Bit 7 = Stereo (we always use stereo) + // 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)) @@ -451,7 +455,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } // Reading sample headers - m_nSamples = std::min(fileHeader.smpNum, MAX_SAMPLES - 1); + m_nSamples = std::min(static_cast(fileHeader.smpNum), static_cast(MAX_SAMPLES - 1)); for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++) { S3MSampleHeader sampleHeader; @@ -462,7 +466,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } sampleHeader.ConvertToMPT(Samples[smp + 1]); - mpt::String::Read(m_szNames[smp + 1], sampleHeader.name); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) { @@ -489,7 +493,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) return true; } // Order list cannot contain pattern indices > 255, so do not even try to load higher patterns - const PATTERNINDEX readPatterns = std::min(fileHeader.patNum, uint8_max); + const PATTERNINDEX readPatterns = std::min(static_cast(fileHeader.patNum), static_cast(uint8_max)); Patterns.ResizeArray(readPatterns); for(PATTERNINDEX pat = 0; pat < readPatterns; pat++) { @@ -528,23 +532,13 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) if(info & s3mNotePresent) { - uint8 note = file.ReadUint8(), instr = file.ReadUint8(); - + const auto [note, instr] = file.ReadArray(); if(note < 0xF0) - { - // Note - note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN; - } else if(note == s3mNoteOff) - { - // ^^ - note = NOTE_NOTECUT; - } else if(note == s3mNoteNone) - { - // .. - note = NOTE_NONE; - } - - m.note = note; + m.note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN; + else if(note == s3mNoteOff) + m.note = NOTE_NOTECUT; + else if(note == s3mNoteNone) + m.note = NOTE_NONE; m.instr = instr; } @@ -558,20 +552,16 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } else { m.volcmd = VOLCMD_VOLUME; - m.vol = MIN(volume, 64); + m.vol = std::min(volume, uint8(64)); } } if(info & s3mEffectPresent) { - uint8 command = file.ReadUint8(), param = file.ReadUint8(); - - if(command != 0) - { - m.command = command; - m.param = param; - S3MConvert(m, false); - } + const auto [command, param] = file.ReadArray(); + m.command = command; + m.param = param; + S3MConvert(m, false); if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xA0 && fileHeader.cwtv < S3MFileHeader::trkST3_20) { @@ -620,7 +610,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) bool CSoundFile::SaveS3M(std::ostream &f) const { - static const uint8 filler[16] = + static constexpr uint8 filler[16] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, @@ -641,7 +631,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const S3MFileHeader fileHeader; MemsetZero(fileHeader); - mpt::String::Write(fileHeader.name, m_songName); + mpt::String::WriteBuf(mpt::String::nullTerminated, fileHeader.name) = m_songName; fileHeader.dosEof = S3MFileHeader::idEOF; fileHeader.fileType = S3MFileHeader::idS3MType; @@ -668,7 +658,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const fileHeader.smpNum = static_cast(writeSamples); // Patterns - PATTERNINDEX writePatterns = MIN(Patterns.GetNumPatterns(), 100u); + PATTERNINDEX writePatterns = std::min(Patterns.GetNumPatterns(), PATTERNINDEX(100)); fileHeader.patNum = static_cast(writePatterns); // Flags @@ -693,7 +683,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const 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, uint32(64))); fileHeader.speed = static_cast(Clamp(m_nDefaultSpeed, 1u, 254u)); fileHeader.tempo = static_cast(Clamp(m_nDefaultTempo.GetInt(), 33u, 255u)); fileHeader.masterVolume = static_cast(Clamp(m_nSamplePreAmp, 16u, 127u) | 0x80); @@ -719,7 +709,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const std::vector sampleOffsets(writeSamples); for(SAMPLEINDEX smp = 0; smp < writeSamples; smp++) { - STATIC_ASSERT((sizeof(S3MSampleHeader) % 16) == 0); + static_assert((sizeof(S3MSampleHeader) % 16) == 0); sampleOffsets[smp] = static_cast((sampleHeaderOffset + smp * sizeof(S3MSampleHeader)) / 16); } mpt::IO::Write(f, sampleOffsets); @@ -840,7 +830,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const { command = CMD_NONE; volcmd = VOLCMD_VOLUME; - vol = MIN(param, 64); + vol = std::min(param, uint8(64)); } if(volcmd == VOLCMD_VOLUME) @@ -855,7 +845,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const if(command != CMD_NONE) { S3MSaveConvert(command, param, false, true); - if(command) + if(command || param) { info |= s3mEffectPresent; if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE] && m.IsGlobalCommand()) @@ -907,7 +897,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const } if(globalCmdOnMutedChn) { - //AddToLog(LogWarning, U_("Global commands on muted channels are interpreted by only some S3M players.")); + //AddToLog(LogWarning, U_("Global commands on muted channels are interpreted only by some S3M players.")); } mpt::IO::Offset sampleDataOffset = mpt::IO::TellWrite(f); @@ -937,7 +927,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const } const SmpLength smpLength = sampleHeader[smp].ConvertToS3M(Samples[realSmp]); - mpt::String::Write(sampleHeader[smp].name, m_szNames[realSmp]); + mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader[smp].name) = m_szNames[realSmp]; if(smpLength != 0) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp index 7a4134dc0..21674d620 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp @@ -40,7 +40,7 @@ struct SFXSampleHeader mptSmp.Initialize(MOD_TYPE_MOD); mptSmp.nLength = length; mptSmp.nFineTune = MOD2XMFineTune(finetune); - mptSmp.nVolume = 4u * std::min(volume, 64); + mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); SmpLength lStart = loopStart; SmpLength lLength = loopLength * 2u; @@ -89,7 +89,7 @@ static uint8 ClampSlideParam(uint8 value, uint8 lowNote, uint8 highNote) // with a fixed speed of 6 ticks/row, and excluding the first row, // 1xx/2xx param has a max value of (low-high)/5 to avoid sliding too far - return std::min(value, static_cast((lowPeriod - highPeriod) / 5)); + return std::min(value, static_cast((lowPeriod - highPeriod) / 5)); } return 0; @@ -236,13 +236,13 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) } if(invalidChars >= 128) return false; - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); } // Broken conversions of the "Operation Stealth" soundtrack (BOND23 / BOND32) // There is a converter that shifts all note values except FFFD (empty note) to the left by 1 bit, // but it should not do that for FFFE (STP) notes - as a consequence, they turn into pattern breaks (FFFC). - const bool fixPatternBreaks = !strcmp(m_szNames[1], "BASSE2.AMI") || !strcmp(m_szNames[1], "PRA1.AMI"); + const bool fixPatternBreaks = (m_szNames[1] == "BASSE2.AMI") || (m_szNames[1] == "PRA1.AMI"); SFXFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) @@ -261,7 +261,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) PATTERNINDEX numPatterns = 0; for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) { - numPatterns = std::max(numPatterns, fileHeader.orderList[ord] + 1u); + numPatterns = std::max(numPatterns, static_cast(fileHeader.orderList[ord] + 1)); } if(fileHeader.restartPos < fileHeader.numOrders) @@ -297,8 +297,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 4; chn++) { ModCommand &m = rowBase[chn]; - uint8 data[4]; - file.ReadArray(data); + auto data = file.ReadArray(); if(data[0] == 0xFF) { @@ -438,7 +437,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) case 0x9: // 9xy: Auto slide version = std::max(version, uint8(8)); - MPT_FALLTHROUGH; + [[fallthrough]]; default: m.command = CMD_NONE; break; @@ -476,7 +475,7 @@ 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; + m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp index f5c5557ad..9f8147900 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp @@ -17,14 +17,14 @@ OPENMPT_NAMESPACE_BEGIN // STM sample header struct struct STMSampleHeader { - char filename[12]; // Can't have long comments - just filename comments :) + char filename[12]; // Can't have long comments - just filename comments :) uint8le zero; - uint8le disk; // A blast from the past - uint16le offset; // 20-bit offset in file (lower 4 bits are zero) - uint16le length; // Sample length - uint16le loopStart; // Loop start point - uint16le loopEnd; // Loop end point - uint8le volume; // Volume + uint8le disk; // A blast from the past + uint16le offset; // 20-bit offset in file (lower 4 bits are zero) + uint16le length; // Sample length + uint16le loopStart; // Loop start point + uint16le loopEnd; // Loop end point + uint8le volume; // Volume uint8le reserved2; uint16le sampleRate; uint8le reserved3[6]; @@ -33,10 +33,10 @@ struct STMSampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); mptSmp.nC5Speed = sampleRate; - mptSmp.nVolume = std::min(volume, 64) * 4; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; @@ -60,13 +60,13 @@ MPT_BINARY_STRUCT(STMSampleHeader, 32) struct STMFileHeader { char songname[20]; - char trackername[8]; // !Scream! for ST 2.xx - uint8 dosEof; // 0x1A - uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :) + char trackername[8]; // !Scream! for ST 2.xx + uint8 dosEof; // 0x1A + uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :) uint8 verMajor; uint8 verMinor; - uint8 initTempo; // Ticks per row. - uint8 numPatterns; // number of patterns + uint8 initTempo; + uint8 numPatterns; uint8 globalVolume; uint8 reserved[13]; }; @@ -144,12 +144,12 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_STM); - mpt::String::Read(m_songName, fileHeader.songname); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname); m_modFormat.formatName = U_("Scream Tracker 2"); m_modFormat.type = U_("stm"); m_modFormat.madeWithTracker = mpt::format(U_("Scream Tracker %1.%2"))(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor)); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nSamples = 31; m_nChannels = 4; @@ -165,7 +165,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultTempo = ConvertST2Tempo(initTempo); m_nDefaultSpeed = initTempo >> 4; if(fileHeader.verMinor > 10) - m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, 64) * 4u; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u; // Setting up channels for(CHANNELINDEX chn = 0; chn < 4; chn++) @@ -183,7 +183,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) if(sampleHeader.zero != 0 && sampleHeader.zero != 46) // putup10.stm has zero = 46 return false; sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.filename); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); sampleOffsets[smp - 1] = sampleHeader.offset; } @@ -214,126 +214,137 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) auto m = Patterns[pat].begin(); ORDERINDEX breakPos = ORDERINDEX_INVALID; - ROWINDEX breakRow = 63; // Candidate row for inserting pattern break - - for(unsigned int i = 0; i < 64 * 4; i++, m++) + ROWINDEX breakRow = 63; // Candidate row for inserting pattern break + + for(ROWINDEX row = 0; row < 64; row++) { - uint8 note = file.ReadUint8(), insvol, volcmd, cmdinf; - switch(note) + uint8 newTempo = 0; + for(CHANNELINDEX chn = 0; chn < 4; chn++, m++) { - case 0xFB: - note = insvol = volcmd = cmdinf = 0x00; - break; - case 0xFC: - continue; - case 0xFD: - m->note = NOTE_NOTECUT; - continue; - default: + uint8 note = file.ReadUint8(), insVol, volCmd, cmdInf; + switch(note) { - uint8 patData[3]; - file.ReadArray(patData); - insvol = patData[0]; - volcmd = patData[1]; - cmdinf = patData[2]; + case 0xFB: + note = insVol = volCmd = cmdInf = 0x00; + break; + case 0xFC: + continue; + case 0xFD: + m->note = NOTE_NOTECUT; + continue; + default: + { + const auto patData = file.ReadArray(); + insVol = patData[0]; + volCmd = patData[1]; + cmdInf = patData[2]; + } + break; } - break; - } - if(note == 0xFE) - m->note = NOTE_NOTECUT; - else if(note < 0x60) - m->note = (note >> 4) * 12 + (note & 0x0F) + 36 + NOTE_MIN; + if(note == 0xFE) + m->note = NOTE_NOTECUT; + else if(note < 0x60) + m->note = (note >> 4) * 12 + (note & 0x0F) + 36 + NOTE_MIN; - m->instr = insvol >> 3; - if(m->instr > 31) - { - m->instr = 0; - } + m->instr = insVol >> 3; + if(m->instr > 31) + { + m->instr = 0; + } - uint8 vol = (insvol & 0x07) | ((volcmd & 0xF0) >> 1); - if(vol <= 64) - { - m->volcmd = VOLCMD_VOLUME; - m->vol = vol; - } - - static const EffectCommand stmEffects[] = - { - CMD_NONE, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, // .ABC - CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TONEPORTAMENTO, // DEFG - CMD_VIBRATO, CMD_TREMOR, CMD_ARPEGGIO, CMD_NONE, // HIJK - CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, // LMNO - // KLMNO can be entered in the editor but don't do anything - }; - - m->command = stmEffects[volcmd & 0x0F]; - m->param = cmdinf; - - switch(m->command) - { - case CMD_VOLUMESLIDE: - // Lower nibble always has precedence, and there are no fine slides. - if(m->param & 0x0F) - m->param &= 0x0F; - else - m->param &= 0xF0; - break; - - case CMD_PATTERNBREAK: - m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); - if(breakPos != ORDERINDEX_INVALID && m->param == 0) + uint8 vol = (insVol & 0x07) | ((volCmd & 0xF0) >> 1); + if(vol <= 64) { - // Merge Bxx + C00 into just Bxx - m->command = CMD_POSITIONJUMP; - m->param = static_cast(breakPos); - breakPos = ORDERINDEX_INVALID; + m->volcmd = VOLCMD_VOLUME; + m->vol = vol; } - LimitMax(breakRow, i / 4u); - break; - case CMD_POSITIONJUMP: - // This effect is also very weird. - // Bxx doesn't appear to cause an immediate break -- it merely - // sets the next order for when the pattern ends (either by - // playing it all the way through, or via Cxx effect) - breakPos = m->param; - breakRow = 63; - m->command = CMD_NONE; - break; - - case CMD_TREMOR: - // this actually does something with zero values, and has no - // effect memory. which makes SENSE for old-effects tremor, - // but ST3 went and screwed it all up by adding an effect - // memory and IT followed that, and those are much more popular - // than STM so we kind of have to live with this effect being - // broken... oh well. not a big loss. - break; - - case CMD_SPEED: - if(fileHeader.verMinor < 21) + static constexpr EffectCommand stmEffects[] = { - m->param = ((m->param / 10u) << 4u) + m->param % 10u; - } + CMD_NONE, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, // .ABC + CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TONEPORTAMENTO, // DEFG + CMD_VIBRATO, CMD_TREMOR, CMD_ARPEGGIO, CMD_NONE, // HIJK + CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, // LMNO + // KLMNO can be entered in the editor but don't do anything + }; + + m->command = stmEffects[volCmd & 0x0F]; + m->param = cmdInf; + + switch(m->command) + { + case CMD_VOLUMESLIDE: + // Lower nibble always has precedence, and there are no fine slides. + if(m->param & 0x0F) + m->param &= 0x0F; + else + m->param &= 0xF0; + break; + + case CMD_PATTERNBREAK: + m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); + if(breakPos != ORDERINDEX_INVALID && m->param == 0) + { + // Merge Bxx + C00 into just Bxx + m->command = CMD_POSITIONJUMP; + m->param = static_cast(breakPos); + breakPos = ORDERINDEX_INVALID; + } + LimitMax(breakRow, row); + break; + + case CMD_POSITIONJUMP: + // This effect is also very weird. + // Bxx doesn't appear to cause an immediate break -- it merely + // sets the next order for when the pattern ends (either by + // playing it all the way through, or via Cxx effect) + breakPos = m->param; + breakRow = 63; + m->command = CMD_NONE; + break; + + case CMD_TREMOR: + // this actually does something with zero values, and has no + // effect memory. which makes SENSE for old-effects tremor, + // but ST3 went and screwed it all up by adding an effect + // memory and IT followed that, and those are much more popular + // than STM so we kind of have to live with this effect being + // broken... oh well. not a big loss. + break; + + case CMD_SPEED: + if(fileHeader.verMinor < 21) + { + m->param = ((m->param / 10u) << 4u) + m->param % 10u; + } + + if(!m->param) + { + m->command = CMD_NONE; + break; + } #ifdef MODPLUG_TRACKER - // ST2 has a very weird tempo mode where the length of a tick depends both - // on the ticks per row and a scaling factor. This is probably the closest - // we can get with S3M-like semantics. - m->param >>= 4; + // ST2 has a very weird tempo mode where the length of a tick depends both + // on the ticks per row and a scaling factor. Try to write the tempo into a separate command. + newTempo = m->param; + m->param >>= 4; #endif // MODPLUG_TRACKER + break; - MPT_FALLTHROUGH; - - default: - // Anything not listed above is a no-op if there's no value. - // (ST2 doesn't have effect memory) - if(!m->param) - { - m->command = CMD_NONE; + default: + // Anything not listed above is a no-op if there's no value, as ST2 doesn't have effect memory. + if(!m->param) + { + m->command = CMD_NONE; + } + break; } - break; + } + if(newTempo != 0) + { + Patterns[pat].WriteEffect(EffectWriter(CMD_TEMPO, mpt::saturate_round(ConvertST2Tempo(newTempo).ToDouble())).Row(row).RetryPreviousRow()); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp index afbf18c91..0b0a17582 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp @@ -59,7 +59,7 @@ struct STPSampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.nLength = length; - mptSmp.nVolume = 4u * std::min(volume, 64); + mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopStart + loopLength; @@ -188,7 +188,7 @@ static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList) // update loop info based on position in edited sample info.loopStart = start; - if(i > 0 && i <= mpt::size(newSmp.cues)) + if(i > 0 && i <= std::size(newSmp.cues)) { newSmp.cues[i - 1] = start; } @@ -258,7 +258,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) 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_modFormat.charset = mpt::Charset::ISO8859_1; m_nChannels = 4; m_nSamples = 0; @@ -306,12 +306,12 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) std::string str; // Read path chunk.ReadNullString(str, 257); - mpt::String::Copy(mptSmp.filename, str); + mptSmp.filename = str; // Ignore flags, they are all not relevant for us chunk.Skip(1); // Read filename / sample text chunk.ReadNullString(str, 31); - mpt::String::Copy(m_szNames[actualSmp], str); + m_szNames[actualSmp] = str; // Seek to even boundary if(chunk.GetPosition() % 2u) chunk.Skip(1); @@ -433,13 +433,11 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) { ChannelMemory &chnMem = channelMemory[chn]; ModCommand &m = rowBase[chn]; - uint8 data[4]; - file.ReadArray(data); + const auto [instr, note, command, param] = file.ReadArray(); - m.instr = data[0]; - m.note = data[1]; - m.command = data[2]; - m.param = data[3]; + m.instr = instr; + m.note = note; + m.param = param; if(m.note) { @@ -451,10 +449,10 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) // and auto global fine volside uint8 swapped = (m.param >> 4) | (m.param << 4); - if((m.command & 0xF0) == 0xF0) + if((command & 0xF0) == 0xF0) { // 12-bit CIA tempo - uint16 ciaTempo = (static_cast(m.command & 0x0F) << 8) | m.param; + uint16 ciaTempo = (static_cast(command & 0x0F) << 8) | m.param; if(ciaTempo) { m.param = mpt::saturate_round(ConvertTempo(ciaTempo).ToDouble()); @@ -463,11 +461,13 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) { m.command = CMD_NONE; } - } else switch(m.command) + } else switch(command) { case 0x00: // arpeggio if(m.param) m.command = CMD_ARPEGGIO; + else + m.command = CMD_NONE; break; case 0x01: // portamento up @@ -609,7 +609,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; - if(m.param < std::min(mpt::size(ModSample().cues), loopList.size())) + if(m.param < std::min(std::size(ModSample().cues), loopList.size())) { m.volcmd = VOLCMD_OFFSET; m.vol = m.param; @@ -642,7 +642,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; - if(m.param < std::min(mpt::size(ModSample().cues), loopList.size())) + if(m.param < std::min(std::size(ModSample().cues), loopList.size())) { m.volcmd = VOLCMD_OFFSET; m.vol = m.param; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp index f46234955..8a5fa53c1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp @@ -90,7 +90,7 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(); m_modFormat.formatName = mpt::format(U_("Unreal Package v%1"))(fileHeader.packageVersion); m_modFormat.type = U_("uax"); - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.charset = mpt::Charset::Windows1252; for(uint32 i = 0; i < fileHeader.exportCount && file.CanRead(4); i++) { @@ -175,7 +175,7 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags) { if(static_cast(objName) < names.size()) { - mpt::String::Copy(m_szNames[GetNumSamples()], names[objName]); + m_szNames[GetNumSamples()] = names[objName]; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp index c960cf3ce..07cdaed37 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp @@ -50,7 +50,7 @@ struct UltSample { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); if(sizeEnd <= sizeStart) { @@ -85,21 +85,8 @@ struct UltSample MPT_BINARY_STRUCT(UltSample, 66) -struct UltPatternCommand -{ - uint8 instr; - uint8 cmd; - uint8 param1; - uint8 param2; -}; - -MPT_BINARY_STRUCT(UltPatternCommand, 4) - - /* Unhandled effects: 5x1 - do not loop sample (x is unused) -9xx - set sample offset to xx * 1024 - with 9yy: set sample offset to xxyy * 4 E0x - set vibrato strength (2 is normal) The logarithmic volume scale used in older format versions here, or pretty @@ -110,7 +97,7 @@ convert them. */ static void TranslateULTCommands(uint8 &effect, uint8 ¶m, uint8 version) { - static const uint8 ultEffTrans[] = + static constexpr uint8 ultEffTrans[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, @@ -215,25 +202,23 @@ static void TranslateULTCommands(uint8 &effect, uint8 ¶m, uint8 version) static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version) { - uint8 b, repeat = 1; - uint8 cmd1, cmd2; // 1 = vol col, 2 = fx col in the original schismtracker code - uint8 param1, param2; - - b = file.ReadUint8(); - if (b == 0xFC) // repeat event + uint8 repeat = 1; + uint8 b = file.ReadUint8(); + if(b == 0xFC) // repeat event { repeat = file.ReadUint8(); b = file.ReadUint8(); } m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE; - UltPatternCommand patCmd; - file.ReadStruct(patCmd); - m.instr = patCmd.instr; - cmd1 = patCmd.cmd & 0x0F; - cmd2 = patCmd.cmd >> 4; - param1 = patCmd.param1; - param2 = patCmd.param2; + + const auto [instr, cmd, para1, para2] = file.ReadArray(); + + m.instr = instr; + uint8 cmd1 = cmd & 0x0F; + uint8 cmd2 = cmd >> 4; + uint8 param1 = para1; + uint8 param2 = para2; TranslateULTCommands(cmd1, param1, version); TranslateULTCommands(cmd2, param2, version); @@ -287,7 +272,7 @@ struct PostFixUltCommands isPortaActive.resize(numChannels, false); } - void operator()(ModCommand& m) + void operator()(ModCommand &m) { // Attempt to fix portamentos. // UltraTracker will slide until the destination note is reached or 300 is encountered. @@ -359,7 +344,7 @@ static bool ValidateHeader(const UltFileHeader &fileHeader) static uint64 GetHeaderMinimumAdditionalSize(const UltFileHeader &fileHeader) { - return fileHeader.messageLength * 32u; + return fileHeader.messageLength * 32u + 3u + 256u; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) @@ -400,13 +385,13 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_ULT); - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); - const MPT_UCHAR_TYPE *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; + const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; m_modFormat.formatName = U_("UltraTracker"); m_modFormat.type = U_("ult"); m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1']; - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT. @@ -433,7 +418,7 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) } sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } ReadOrderFromFile(Order(), file, 256, 0xFF, 0xFE); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp index dfeac4f8c..a71f0bdf8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp @@ -36,7 +36,7 @@ static bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t cha return false; } - const mpt::byte *inBuf = file.GetRawData(); + const std::byte *inBuf = file.GetRawData(); CopySample(reinterpret_cast(sample.samplev()), sample.nLength, 1, inBuf + offset, file.BytesLeft() - offset, numChannels, conv); return true; } @@ -87,7 +87,7 @@ bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("RIFF WAVE"); m_modFormat.type = U_("wav"); - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.charset = mpt::Charset::Windows1252; const SmpLength sampleLength = wavFile.GetSampleLength(); @@ -95,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, uint32(1)); Order().assign(1, 0); ORDERINDEX numOrders = 1; @@ -136,7 +136,7 @@ bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags) sample.uFlags = CHN_PANNING; sample.nLength = sampleLength; sample.nC5Speed = wavFile.GetSampleRate(); - strcpy(m_szNames[channel + 1], ""); + 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 918969070..1c0b10846 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp @@ -13,18 +13,132 @@ #include "Loaders.h" #include "../common/version.h" #include "XMTools.h" +#include "mod_specifications.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif +#include "OggStream.h" #include #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" // For super smooth ramping option #endif // MODPLUG_TRACKER +#if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) +#include +#endif + +#if defined(MPT_WITH_VORBIS) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG +#include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#endif + +#if defined(MPT_WITH_VORBISFILE) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG +#include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#include "../soundbase/SampleFormatConverters.h" +#include "../soundbase/SampleFormatCopy.h" +#endif + +#ifdef MPT_WITH_STBVORBIS +#include +#include "../soundbase/SampleFormatConverters.h" +#include "../soundbase/SampleFormatCopy.h" +#endif // MPT_WITH_STBVORBIS + OPENMPT_NAMESPACE_BEGIN + +#if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) + +static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + FileReader &file = *reinterpret_cast(datasource); + return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; +} + +static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) +{ + FileReader &file = *reinterpret_cast(datasource); + switch(whence) + { + case SEEK_SET: + { + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; + } + break; + case SEEK_CUR: + { + if(offset < 0) + { + if(offset == std::numeric_limits::min()) + { + return -1; + } + if(!Util::TypeCanHoldValue(0-offset)) + { + return -1; + } + return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; + } else + { + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; + } + } + break; + case SEEK_END: + { + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + if(!Util::TypeCanHoldValue(file.GetLength() + offset)) + { + return -1; + } + return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; + } + break; + default: + return -1; + } +} + +static long VorbisfileFilereaderTell(void *datasource) +{ + FileReader &file = *reinterpret_cast(datasource); + FileReader::off_t result = file.GetPosition(); + if(!Util::TypeCanHoldValue(result)) + { + return -1; + } + return static_cast(result); +} + +#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE + + // Allocate samples for an instrument static std::vector AllocateXMSamples(CSoundFile &sndFile, SAMPLEINDEX numSamples) { @@ -144,10 +258,10 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo // A packed size of 0 indicates a completely empty pattern. const uint16 packedSize = file.ReadUint16LE(); - if(numRows == 0 || numRows > MAX_PATTERN_ROWS) - { + if(numRows == 0) numRows = 64; - } + else if(numRows > MAX_PATTERN_ROWS) + numRows = MAX_PATTERN_ROWS; file.Seek(curPos + headerSize); FileReader patternChunk = file.ReadChunk(packedSize); @@ -221,7 +335,7 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo } else if (vol >= 0x60) { // Volume commands 6-F translation. - static const ModCommand::VOLCMD volEffTrans[] = + static constexpr ModCommand::VOLCMD volEffTrans[] = { VOLCMD_VOLSLIDEDOWN, VOLCMD_VOLSLIDEUP, VOLCMD_FINEVOLDOWN, VOLCMD_FINEVOLUP, VOLCMD_VIBRATOSPEED, VOLCMD_VIBRATODEPTH, VOLCMD_PANNING, VOLCMD_PANSLIDELEFT, @@ -294,6 +408,174 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXM(MemoryFileReader file, con } +static bool ReadSampleData(ModSample &sample, SampleIO sampleFlags, FileReader &sampleChunk, bool &isOXM) +{ + bool unsupportedSample = false; + + bool isOGG = false; + if(sampleChunk.CanRead(8)) + { + isOGG = true; + sampleChunk.Skip(4); + // In order to avoid false-detecting PCM as OggVorbis as much as possible, + // we parse and verify the complete sample data and only assume OggVorbis, + // if all Ogg checksums are correct a no single byte of non-Ogg data exists. + // The fast-path for regular PCM will only check "OggS" magic and do no other work after failing that check. + while(!sampleChunk.EndOfFile()) + { + if(!Ogg::ReadPage(sampleChunk)) + { + isOGG = false; + break; + } + } + } + isOXM = isOXM || isOGG; + sampleChunk.Rewind(); + if(isOGG) + { + uint32 originalSize = sampleChunk.ReadInt32LE(); + FileReader sampleData = sampleChunk.ReadChunk(sampleChunk.BytesLeft()); + + sample.uFlags.set(CHN_16BIT, sampleFlags.GetBitDepth() >= 16); + sample.uFlags.set(CHN_STEREO, sampleFlags.GetChannelFormat() != SampleIO::mono); + sample.nLength = originalSize / (sample.uFlags[CHN_16BIT] ? 2 : 1) / (sample.uFlags[CHN_STEREO] ? 2 : 1); + +#if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) + + ov_callbacks callbacks = { + &VorbisfileFilereaderRead, + &VorbisfileFilereaderSeek, + NULL, + &VorbisfileFilereaderTell + }; + OggVorbis_File vf; + MemsetZero(vf); + if(ov_open_callbacks(&sampleData, &vf, nullptr, 0, callbacks) == 0) + { + if(ov_streams(&vf) == 1) + { // we do not support chained vorbis samples + vorbis_info *vi = ov_info(&vf, -1); + if(vi && vi->rate > 0 && vi->channels > 0) + { + sample.AllocateSample(); + SmpLength offset = 0; + int channels = vi->channels; + int current_section = 0; + long decodedSamples = 0; + bool eof = false; + while(!eof && offset < sample.nLength && sample.HasSampleData()) + { + float **output = nullptr; + long ret = ov_read_float(&vf, &output, 1024, ¤t_section); + if(ret == 0) + { + eof = true; + } else if(ret < 0) + { + // stream error, just try to continue + } else + { + decodedSamples = ret; + LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); + if(decodedSamples > 0 && channels == sample.GetNumChannels()) + { + for(int chn = 0; chn < channels; chn++) + { + if(sample.uFlags[CHN_16BIT]) + { + CopyChannelToInterleaved >(sample.sample16() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + } else + { + CopyChannelToInterleaved >(sample.sample8() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + } + } + } + offset += decodedSamples; + } + } + } else + { + unsupportedSample = true; + } + } else + { + unsupportedSample = true; + } + ov_clear(&vf); + } else + { + unsupportedSample = true; + } + +#elif defined(MPT_WITH_STBVORBIS) + + // NOTE/TODO: stb_vorbis does not handle inferred negative PCM sample + // position at stream start. (See + // ). + // This means that, for remuxed and re-aligned/cutted (at stream start) + // Vorbis files, stb_vorbis will include superfluous samples at the + // beginning. OXM files with this property are yet to be spotted in the + // wild, thus, this behaviour is currently not problematic. + + int consumed = 0, error = 0; + stb_vorbis *vorb = nullptr; + FileReader::PinnedRawDataView sampleDataView = sampleData.GetPinnedRawDataView(); + const std::byte* data = sampleDataView.data(); + std::size_t dataLeft = sampleDataView.size(); + vorb = stb_vorbis_open_pushdata(mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); + sampleData.Skip(consumed); + data += consumed; + dataLeft -= consumed; + if(vorb) + { + // Header has been read, proceed to reading the sample data + sample.AllocateSample(); + SmpLength offset = 0; + while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0)) + && offset < sample.nLength && sample.HasSampleData()) + { + int channels = 0, decodedSamples = 0; + float **output; + consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &channels, &output, &decodedSamples); + sampleData.Skip(consumed); + data += consumed; + dataLeft -= consumed; + LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); + if(decodedSamples > 0 && channels == sample.GetNumChannels()) + { + for(int chn = 0; chn < channels; chn++) + { + if(sample.uFlags[CHN_16BIT]) + CopyChannelToInterleaved >(sample.sample16() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + else + CopyChannelToInterleaved >(sample.sample8() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + } + } + offset += decodedSamples; + error = stb_vorbis_get_error(vorb); + } + stb_vorbis_close(vorb); + } else + { + unsupportedSample = true; + } + +#else // !VORBIS + + unsupportedSample = true; + +#endif // VORBIS + + } else + { + sampleFlags.ReadSample(sample, sampleChunk); + } + + return !unsupportedSample; +} + + bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); @@ -322,33 +604,25 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) FlagSet madeWith(verUnknown); mpt::ustring madeWithTracker; - if(!memcmp(fileHeader.trackerName, "FastTracker ", 12)) + if(!memcmp(fileHeader.trackerName, "FastTracker v2.00 ", 20) && fileHeader.size == 276) { - if(fileHeader.size == 276 && !memcmp(fileHeader.trackerName + 12, "v2.00 ", 8)) - { - if(fileHeader.version < 0x0104) - madeWith = verFT2Generic | verConfirmed; - else if(memchr(fileHeader.songName, '\0', 20) != nullptr) - // FT2 pads the song title with spaces, some other trackers use null chars - madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; - else - madeWith = verFT2Generic | verNewModPlug; - } else if(!memcmp(fileHeader.trackerName + 12, "v 2.00 ", 8)) - { - // MPT 1.0 (exact version to be determined later) - madeWith = verOldModPlug; - } else - { - // ??? - madeWith.set(verConfirmed); - madeWithTracker = U_("FastTracker Clone"); - } + if(fileHeader.version < 0x0104) + madeWith = verFT2Generic | verConfirmed; + else if(memchr(fileHeader.songName, '\0', 20) != nullptr) + // FT2 pads the song title with spaces, some other trackers use null chars + madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; + else + madeWith = verFT2Generic | verNewModPlug; + } else if(!memcmp(fileHeader.trackerName, "FastTracker v 2.00 ", 20)) + { + // MPT 1.0 (exact version to be determined later) + madeWith = verOldModPlug; } else { // Something else! madeWith = verUnknown | verConfirmed; - mpt::String::Read(madeWithTracker, mpt::CharsetCP437, fileHeader.trackerName); + madeWithTracker = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.trackerName)); if(!memcmp(fileHeader.trackerName, "OpenMPT ", 8)) { @@ -361,6 +635,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { m_nMixLevels = mixLevelsCompatibleFT2; } + } else if(!memcmp(fileHeader.trackerName, "Fasttracker II clone", 20)) + { + // 8bitbubsy's FT2 clone should be treated exactly like FT2 + madeWith = verFT2Generic | verConfirmed; } else if(!memcmp(fileHeader.trackerName, "MadTracker 2.0\0", 15)) { // Fix channel 2 in m3_cha.xm @@ -369,25 +647,25 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kFT2Arpeggio); } else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14)) { - m_playBehaviour.reset(kFT2OffsetOutOfRange); + m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); } else if(!memcmp(fileHeader.trackerName, "*Converted ", 11)) { madeWith = verDigiTrakker; } } - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); m_nMinPeriod = 1; m_nMaxPeriod = 31999; Order().SetRestartPos(fileHeader.restartPos); m_nChannels = fileHeader.channels; - m_nInstruments = std::min(fileHeader.instruments, MAX_INSTRUMENTS - 1u); + m_nInstruments = std::min(static_cast(fileHeader.instruments), static_cast(MAX_INSTRUMENTS - 1)); if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; if(fileHeader.tempo) - m_nDefaultTempo.Set(Clamp(fileHeader.tempo, 32, 512)); + m_nDefaultTempo = Clamp(TEMPO(fileHeader.tempo, 0), ModSpecs::xmEx.GetTempoMin(), ModSpecs::xmEx.GetTempoMax()); m_SongFlags.reset(); m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & XMFileHeader::linearSlides) != 0); @@ -410,10 +688,13 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) ReadXMPatterns(file, fileHeader, *this); } + bool isOXM = false; + // In case of XM versions < 1.04, we need to memorize the sample flags for all samples, as they are not stored immediately after the sample headers. std::vector sampleFlags; uint8 sampleReserved = 0; int instrType = -1; + bool unsupportedSamples = false; // Reading instruments for(INSTRUMENTINDEX instr = 1; instr <= m_nInstruments; instr++) @@ -437,12 +718,12 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) if(instrHeader.size == 245) { // ModPlug Tracker Alpha - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); + m_dwLastSavedWithVersion = MPT_V("1.00.00.A5"); madeWithTracker = U_("ModPlug Tracker 1.0 alpha"); } else if(instrHeader.size == 263) { // ModPlug Tracker Beta (Beta 1 still behaves like Alpha, but Beta 3.3 does it this way) - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, B3); + m_dwLastSavedWithVersion = MPT_V("1.00.00.B3"); madeWithTracker = U_("ModPlug Tracker 1.0 beta"); } else { @@ -532,7 +813,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) sampleHeader.ConvertToMPT(Samples[mptSample]); instrHeader.instrument.ApplyAutoVibratoToMPT(Samples[mptSample]); - mpt::String::Read(m_szNames[mptSample], sampleHeader.name); + m_szNames[mptSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); if((sampleHeader.flags & 3) == 3 && madeWith[verNewModPlug]) { @@ -552,7 +833,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) FileReader sampleChunk = file.ReadChunk(sampleFlags[sample].GetEncoding() != SampleIO::ADPCM ? sampleSize[sample] : (16 + (sampleSize[sample] + 1) / 2)); if(sample < sampleSlots.size() && (loadFlags & loadSampleData)) { - sampleFlags[sample].ReadSample(Samples[sampleSlots[sample]], sampleChunk); + if(!ReadSampleData(Samples[sampleSlots[sample]], sampleFlags[sample], sampleChunk, isOXM)) + { + unsupportedSamples = true; + } } } } @@ -582,6 +866,11 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) } } + if(unsupportedSamples) + { + AddToLog(LogWarning, U_("Some compressed samples could not be loaded because they use an unsupported codec.")); + } + // Read song comments: "text" if(file.ReadMagic("text")) { @@ -639,11 +928,11 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { if(madeWith[verModPlug1_09]) { - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.09.00.00"); madeWithTracker = U_("ModPlug Tracker 1.09"); } else if(madeWith[verNewModPlug]) { - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker 1.10 - 1.16"); } } @@ -652,10 +941,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { // Hey, I know this tracker! std::string mptVersion(fileHeader.trackerName + 8, 12); - m_dwLastSavedWithVersion = Version::Parse(mpt::ToUnicode(mpt::CharsetASCII, mptVersion)); + m_dwLastSavedWithVersion = Version::Parse(mpt::ToUnicode(mpt::Charset::ASCII, mptVersion)); madeWith = verOpenMPT | verConfirmed; - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 19)) + if(m_dwLastSavedWithVersion < MPT_V("1.22.07.19")) m_nMixLevels = mixLevelsCompatible; else m_nMixLevels = mixLevelsCompatibleFT2; @@ -711,20 +1000,20 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) LoadExtendedSongProperties(file, true, &isOpenMPTMade); - if(isOpenMPTMade && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(isOpenMPTMade && m_dwLastSavedWithVersion < MPT_V("1.17.00.00")) { // Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0 // when saving a file in OpenMPT for the first time. - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00")) { madeWithTracker = U_("OpenMPT ") + m_dwLastSavedWithVersion.ToUString(); } // We no longer allow any --- or +++ items in the order list now. - if(m_dwLastSavedWithVersion && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 02, 02)) + if(m_dwLastSavedWithVersion && m_dwLastSavedWithVersion < MPT_V("1.22.02.02")) { if(!Patterns.IsValidPat(0xFE)) Order().RemovePattern(0xFE); @@ -732,10 +1021,21 @@ 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; + if(isOXM) + { + m_modFormat.formatName = U_("OggMod FastTracker 2"); + m_modFormat.type = U_("oxm"); + m_modFormat.originalFormatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); + m_modFormat.originalType = U_("xm"); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; + } else + { + 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::Charset::Windows1252 : mpt::Charset::CP437; + } return true; } @@ -756,10 +1056,10 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) MemsetZero(fileHeader); memcpy(fileHeader.signature, "Extended Module: ", 17); - mpt::String::Write(fileHeader.songName, m_songName); + mpt::String::WriteBuf(mpt::String::spacePadded, fileHeader.songName) = m_songName; fileHeader.eof = 0x1A; const std::string openMptTrackerName = mpt::ToCharset(GetCharsetFile(), Version::Current().GetOpenMPTVersionString()); - mpt::String::Write(fileHeader.trackerName, openMptTrackerName); + mpt::String::WriteBuf(mpt::String::spacePadded, fileHeader.trackerName) = openMptTrackerName; // Writing song header fileHeader.version = 0x0104; // XM Format v1.04 @@ -826,7 +1126,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) mpt::IO::Write(f, fileHeader); // Write processed order list - mpt::IO::WriteRaw(f, orderList.data(), orderList.size()); + mpt::IO::Write(f, orderList); // Writing patterns @@ -1051,7 +1351,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) } sampleFlags[smp] = xmSample.GetSampleFormat(); - mpt::String::Write(xmSample.name, m_szNames[samples[smp]]); + mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[smp]]; mpt::IO::Write(f, xmSample); } @@ -1092,7 +1392,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) for(PATTERNINDEX pat = 0; pat < numNamedPats; pat++) { char name[MAX_PATTERNNAME]; - mpt::String::Write(name, Patterns[pat].GetName()); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = Patterns[pat].GetName(); mpt::IO::Write(f, name); } } @@ -1111,7 +1411,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) for(CHANNELINDEX chn = 0; chn < numNamedChannels; chn++) { char name[MAX_CHANNELNAME]; - mpt::String::Write(name, ChnSettings[chn].szName); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = ChnSettings[chn].szName; mpt::IO::Write(f, name); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h index ad3724a2c..a436448f9 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h @@ -47,7 +47,7 @@ constexpr uint16 MagicBE(const char(&id)[3]) template bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t howMany = arraySize, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { - STATIC_ASSERT(mpt::is_binary_safe::value); + static_assert(mpt::is_binary_safe::value); LimitMax(howMany, arraySize); LimitMax(howMany, MAX_ORDERS); ORDERINDEX readEntries = static_cast(howMany); @@ -69,7 +69,7 @@ bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t template bool ReadOrderFromFile(ModSequence &order, FileReader &file, size_t howMany, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { - STATIC_ASSERT(mpt::is_binary_safe::value); + static_assert(mpt::is_binary_safe::value); if(!file.CanRead(howMany * sizeof(T))) return false; LimitMax(howMany, MAX_ORDERS); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp index a19eb46ce..a6124231c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp @@ -96,6 +96,14 @@ void MIDIMacroConfig::CreateParameteredMacro(Macro ¶meteredMacro, Parametere } +std::string MIDIMacroConfig::CreateParameteredMacro(ParameteredMacro macroType, int subType) const +{ + Macro parameteredMacro; + CreateParameteredMacro(parameteredMacro, macroType, subType); + return mpt::String::ReadAutoBuf(parameteredMacro); +} + + // Create Zxx (Z80 - ZFF) from preset void MIDIMacroConfig::CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h index 3681bd458..5307fd0f1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h @@ -117,12 +117,7 @@ public: { CreateParameteredMacro(szMidiSFXExt[macroIndex], macroType, subType); } - std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const - { - Macro parameteredMacro; - CreateParameteredMacro(parameteredMacro, macroType, subType); - return std::string(parameteredMacro); - } + std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const; protected: void CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const; @@ -176,7 +171,7 @@ protected: }; -STATIC_ASSERT(sizeof(MIDIMacroConfig) == sizeof(MIDIMacroConfigData)); // this is directly written to files, so the size must be correct! +static_assert(sizeof(MIDIMacroConfig) == sizeof(MIDIMacroConfigData)); // this is directly written to files, so the size must be correct! OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp index 925c2967a..16547539b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp @@ -15,13 +15,13 @@ OPENMPT_NAMESPACE_BEGIN // Samples per frame - for each MPEG version and all three layers -static const uint16 samplesPerFrame[2][3] = +static constexpr uint16 samplesPerFrame[2][3] = { { 384, 1152, 1152 }, // MPEG 1 { 384, 1152, 576 } // MPEG 2 / 2.5 }; // Bit rates for each MPEG version and all three layers -static const uint16 bitRates[2][3][15] = +static constexpr uint16 bitRates[2][3][15] = { // MPEG 1 { @@ -37,7 +37,7 @@ static const uint16 bitRates[2][3][15] = } }; // Sampling rates for each MPEG version and all three layers -static const uint16 samplingRates[4][3] = +static constexpr uint16 samplingRates[4][3] = { { 11025, 12000, 8000 }, // MPEG 2.5 { 0, 0, 0 }, // Invalid @@ -45,13 +45,13 @@ static const uint16 samplingRates[4][3] = { 44100, 48000, 32000 } // MPEG 1 }; // Samples per Frame / 8 -static const uint8 mpegCoefficients[2][3] = +static constexpr uint8 mpegCoefficients[2][3] = { { 12, 144, 144 }, // MPEG 1 { 12, 144, 72 } // MPEG 2 / 2.5 }; // Side info size = Offset in frame where Xing/Info magic starts -static const uint8 sideInfoSize[2][2] = +static constexpr uint8 sideInfoSize[2][2] = { { 17, 32 }, // MPEG 1 { 9, 17 } // MPEG 2 / 2.5 diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp index 7f64676c6..60d826606 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp @@ -24,7 +24,7 @@ OPENMPT_NAMESPACE_BEGIN // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). // [in] lineEnding: line ending formatting of the text in memory. // [out] returns true on success. -bool SongMessage::Read(const mpt::byte *data, size_t length, LineEnding lineEnding) +bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEnding) { const char *str = mpt::byte_cast(data); while(length != 0 && str[length - 1] == '\0') @@ -120,7 +120,7 @@ bool SongMessage::Read(FileReader &file, const size_t length, LineEnding lineEnd // [in] lineLength: The fixed length of a line. // [in] lineEndingLength: The padding space between two fixed lines. (there could for example be a null char after every line) // [out] returns true on success. -bool SongMessage::ReadFixedLineLength(const mpt::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength) +bool SongMessage::ReadFixedLineLength(const std::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength) { if(lineLength == 0) return false; @@ -203,7 +203,7 @@ std::string SongMessage::GetFormatted(const LineEnding lineEnding) const bool SongMessage::SetFormatted(std::string message, LineEnding lineEnding) { MPT_ASSERT(lineEnding == leLF || lineEnding == leCR || lineEnding == leCRLF); - switch (lineEnding) + switch(lineEnding) { case leLF: message = mpt::String::Replace(message, "\n", std::string(1, InternalLineEnding)); @@ -222,7 +222,7 @@ bool SongMessage::SetFormatted(std::string message, LineEnding lineEnding) { return false; } - assign(message); + assign(std::move(message)); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h index cd21343cf..a249eab0c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h @@ -42,7 +42,7 @@ public: // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). // [in] lineEnding: line ending formatting of the text in memory. // [out] returns true on success. - bool Read(const mpt::byte *data, const size_t length, LineEnding lineEnding); + bool Read(const std::byte *data, const size_t length, LineEnding lineEnding); bool Read(FileReader &file, const size_t length, LineEnding lineEnding); // Read comments with fixed line length from a mapped file. @@ -51,7 +51,7 @@ public: // [in] lineLength: The fixed length of a line. // [in] lineEndingLength: The padding space between two fixed lines. (there could for example be a null char after every line) // [out] returns true on success. - bool ReadFixedLineLength(const mpt::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength); + bool ReadFixedLineLength(const std::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength); bool ReadFixedLineLength(FileReader &file, const size_t length, const size_t lineLength, const size_t lineEndingLength); // Retrieve song message. @@ -64,6 +64,8 @@ public: // [out] returns true if the message has been changed. bool SetFormatted(std::string message, LineEnding lineEnding); + // Sets the song message. Expects the provided string to already use the internal line ending character. + void SetRaw(std::string message) noexcept { assign(std::move(message)); } }; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp index 347dc7c48..585f57c79 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp @@ -29,15 +29,15 @@ OPENMPT_NAMESPACE_BEGIN namespace MixFuncTable { #ifdef MPT_INTMIXER - typedef Int8MToIntS I8M; - typedef Int16MToIntS I16M; - typedef Int8SToIntS I8S; - typedef Int16SToIntS I16S; +using I8M = Int8MToIntS; +using I16M = Int16MToIntS; +using I8S = Int8SToIntS; +using I16S = Int16SToIntS; #else - typedef Int8MToFloatS I8M; - typedef Int16MToFloatS I16M; - typedef Int8SToFloatS I8S; - typedef Int16SToFloatS I16S; +using I8M = Int8MToFloatS; +using I16M = Int16MToFloatS; +using I8S = Int8SToFloatS; +using I16S = Int16SToFloatS; #endif // MPT_INTMIXER // Build mix function table for given resampling, filter and ramping settings: One function each for 8-Bit / 16-Bit Mono / Stereo @@ -59,15 +59,14 @@ namespace MixFuncTable const MixFuncInterface Functions[6 * 16] = { - BuildMixFuncTable(NoInterpolation), // No SRC - BuildMixFuncTable(LinearInterpolation), // Linear SRC - BuildMixFuncTable(FastSincInterpolation), // Fast Sinc (Cubic Spline) SRC - BuildMixFuncTable(PolyphaseInterpolation), // Kaiser SRC - BuildMixFuncTable(FIRFilterInterpolation), // FIR SRC - BuildMixFuncTable(AmigaBlepInterpolation), // Amiga emulation + BuildMixFuncTable(NoInterpolation), // No SRC + BuildMixFuncTable(LinearInterpolation), // Linear SRC + BuildMixFuncTable(FastSincInterpolation), // Fast Sinc (Cubic Spline) SRC + BuildMixFuncTable(PolyphaseInterpolation), // Kaiser SRC + BuildMixFuncTable(FIRFilterInterpolation), // FIR SRC + BuildMixFuncTable(AmigaBlepInterpolation), // Amiga emulation }; - #undef BuildMixFuncTableRamp #undef BuildMixFuncTableFilter #undef BuildMixFuncTable @@ -77,13 +76,13 @@ ResamplingIndex ResamplingModeToMixFlags(ResamplingMode resamplingMode) { switch(resamplingMode) { - case SRCMODE_NEAREST: return ndxNoInterpolation; - case SRCMODE_LINEAR: return ndxLinear; - case SRCMODE_CUBIC: return ndxFastSinc; - case SRCMODE_SINC8LP: return ndxKaiser; - case SRCMODE_SINC8: return ndxFIRFilter; - case SRCMODE_AMIGA: return ndxAmigaBlep; - default: MPT_ASSERT_NOTREACHED(); + case SRCMODE_NEAREST: return ndxNoInterpolation; + case SRCMODE_LINEAR: return ndxLinear; + case SRCMODE_CUBIC: return ndxFastSinc; + case SRCMODE_SINC8LP: return ndxKaiser; + case SRCMODE_SINC8: return ndxFIRFilter; + case SRCMODE_AMIGA: return ndxAmigaBlep; + default: MPT_ASSERT_NOTREACHED(); } return ndxNoInterpolation; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h index fd90ec43f..843a3019d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h @@ -18,30 +18,30 @@ OPENMPT_NAMESPACE_BEGIN namespace MixFuncTable { - // Table index: - // [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo) - // [b2] ramp - // [b3] filter - // [b6-b4] src type + // Table index bits: + // [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo) + // [b2] ramp + // [b3] filter + // [b6-b4] src type // Sample type / processing type index enum FunctionIndex { - ndx16Bit = 0x01, - ndxStereo = 0x02, - ndxRamp = 0x04, - ndxFilter = 0x08, + ndx16Bit = 0x01, + ndxStereo = 0x02, + ndxRamp = 0x04, + ndxFilter = 0x08, }; // SRC index enum ResamplingIndex { - ndxNoInterpolation = 0x00, - ndxLinear = 0x10, - ndxFastSinc = 0x20, - ndxKaiser = 0x30, - ndxFIRFilter = 0x40, - ndxAmigaBlep = 0x50, + ndxNoInterpolation = 0x00, + ndxLinear = 0x10, + ndxFastSinc = 0x20, + ndxKaiser = 0x30, + ndxFIRFilter = 0x40, + ndxAmigaBlep = 0x50, }; extern const MixFuncInterface Functions[6 * 16]; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h index 94885abc2..6e2a547fa 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h @@ -12,15 +12,33 @@ #include "BuildSettings.h" +#include "../soundbase/SampleTypes.h" + OPENMPT_NAMESPACE_BEGIN #define MPT_INTMIXER #ifdef MPT_INTMIXER -typedef int32 mixsample_t; -enum { MIXING_FILTER_PRECISION = 24 }; // Fixed point resonant filter bits +using mixsample_t = MixSampleIntTraits::sample_type; +enum { MIXING_FILTER_PRECISION = MixSampleIntTraits::filter_precision_bits() }; // Fixed point resonant filter bits #else -typedef float mixsample_t; +using mixsample_t = AudioSampleFloat; +#endif +enum { MIXING_ATTENUATION = MixSampleIntTraits::mix_headroom_bits() }; +enum { MIXING_FRACTIONAL_BITS = MixSampleIntTraits::mix_fractional_bits() }; + +constexpr float MIXING_SCALEF = MixSampleIntTraits::mix_scale(); + +#ifdef MPT_INTMIXER +static_assert(sizeof(mixsample_t) == 4); +static_assert(MIXING_FILTER_PRECISION == 24); +static_assert(MIXING_ATTENUATION == 4); +static_assert(MIXING_FRACTIONAL_BITS == 27); +static_assert(MixSampleIntTraits::mix_clip_max() == int32(0x7FFFFFF)); +static_assert(MixSampleIntTraits::mix_clip_min() == (0 - int32(0x7FFFFFF))); +static_assert(MIXING_SCALEF == 134217728.0f); +#else +static_assert(sizeof(mixsample_t) == 4); #endif #define MIXBUFFERSIZE 512 @@ -28,17 +46,6 @@ typedef float mixsample_t; #define VOLUMERAMPPRECISION 12 // Fractional bits in volume ramp variables -enum { MIXING_ATTENUATION = 4 }; -enum { MIXING_FRACTIONAL_BITS = 32 - 1 - MIXING_ATTENUATION }; - -enum -{ - MIXING_CLIPMAX = ((1<(1 << MIXING_FRACTIONAL_BITS); - // The absolute maximum number of sampling points any interpolation algorithm is going to look at in any direction from the current sampling point // Currently, the maximum is 4 sampling points forwards and 3 sampling points backwards (Polyphase / FIR algorithms). // Hence, this value must be at least 4. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp index f56df384f..5b4345e4e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp @@ -12,6 +12,7 @@ #include "stdafx.h" #include "MixerLoops.h" +#include "..//soundbase/SampleBuffer.h" #include "Snd_defs.h" #include "ModChannel.h" #ifdef ENABLE_SSE2 @@ -21,137 +22,6 @@ OPENMPT_NAMESPACE_BEGIN -//////////////////////////////////////////////////////////////////////////////////// -// 3DNow! optimizations - -#ifdef ENABLE_X86_AMD - -// Convert integer mix to floating-point -static void AMD_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) -{ - _asm { - movd mm0, _i2fc - mov edx, pSrc - mov edi, pOut1 - mov ebx, pOut2 - mov ecx, nCount - punpckldq mm0, mm0 - inc ecx - shr ecx, 1 -mainloop: - movq mm1, qword ptr [edx] - movq mm2, qword ptr [edx+8] - add edi, 8 - pi2fd mm1, mm1 - pi2fd mm2, mm2 - add ebx, 8 - pfmul mm1, mm0 - pfmul mm2, mm0 - add edx, 16 - movq mm3, mm1 - punpckldq mm3, mm2 - punpckhdq mm1, mm2 - dec ecx - movq qword ptr [edi-8], mm3 - movq qword ptr [ebx-8], mm1 - jnz mainloop - emms - } -} - -static void AMD_FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 nCount, const float _f2ic) -{ - _asm { - movd mm0, _f2ic - mov eax, pIn1 - mov ebx, pIn2 - mov edx, pOut - mov ecx, nCount - punpckldq mm0, mm0 - inc ecx - shr ecx, 1 - sub edx, 16 -mainloop: - movq mm1, [eax] - movq mm2, [ebx] - add edx, 16 - movq mm3, mm1 - punpckldq mm1, mm2 - punpckhdq mm3, mm2 - add eax, 8 - pfmul mm1, mm0 - pfmul mm3, mm0 - add ebx, 8 - pf2id mm1, mm1 - pf2id mm3, mm3 - dec ecx - movq qword ptr [edx], mm1 - movq qword ptr [edx+8], mm3 - jnz mainloop - emms - } -} - - -static void AMD_FloatToMonoMix(const float *pIn, int32 *pOut, uint32 nCount, const float _f2ic) -{ - _asm { - movd mm0, _f2ic - mov eax, pIn - mov edx, pOut - mov ecx, nCount - punpckldq mm0, mm0 - add ecx, 3 - shr ecx, 2 - sub edx, 16 -mainloop: - movq mm1, [eax] - movq mm2, [eax+8] - add edx, 16 - pfmul mm1, mm0 - pfmul mm2, mm0 - add eax, 16 - pf2id mm1, mm1 - pf2id mm2, mm2 - dec ecx - movq qword ptr [edx], mm1 - movq qword ptr [edx+8], mm2 - jnz mainloop - emms - } -} - - -static void AMD_MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _i2fc) -{ - _asm { - movd mm0, _i2fc - mov eax, pSrc - mov edx, pOut - mov ecx, nCount - punpckldq mm0, mm0 - add ecx, 3 - shr ecx, 2 - sub edx, 16 -mainloop: - movq mm1, qword ptr [eax] - movq mm2, qword ptr [eax+8] - add edx, 16 - pi2fd mm1, mm1 - pi2fd mm2, mm2 - add eax, 16 - pfmul mm1, mm0 - pfmul mm2, mm0 - dec ecx - movq qword ptr [edx], mm1 - movq qword ptr [edx+8], mm2 - jnz mainloop - emms - } -} - -#endif // ENABLE_X86_AMD - /////////////////////////////////////////////////////////////////////////////////////// // SSE Optimizations @@ -213,36 +83,6 @@ static void SSE2_FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *p #if defined(ENABLE_X86) && defined(ENABLE_SSE) -static void SSE_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) -{ - _asm { - movss xmm0, _i2fc - mov edx, pSrc - mov eax, pOut1 - mov ebx, pOut2 - mov ecx, nCount - shufps xmm0, xmm0, 0x00 - xorps xmm1, xmm1 - xorps xmm2, xmm2 - inc ecx - shr ecx, 1 -mainloop: - cvtpi2ps xmm1, [edx] - cvtpi2ps xmm2, [edx+8] - add eax, 8 - add ebx, 8 - movlhps xmm1, xmm2 - mulps xmm1, xmm0 - add edx, 16 - shufps xmm1, xmm1, 0xD8 - dec ecx - movlps qword ptr [eax-8], xmm1 - movhps qword ptr [ebx-8], xmm1 - jnz mainloop - } -} - - static void SSE_MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _i2fc) { _asm { @@ -274,60 +114,6 @@ mainloop: #ifdef ENABLE_X86 -// Convert floating-point mix to integer - -static void X86_FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 nCount, const float _f2ic) -{ - _asm { - mov esi, pIn1 - mov ebx, pIn2 - mov edi, pOut - mov ecx, nCount - fld _f2ic -mainloop: - fld dword ptr [ebx] - add edi, 8 - fld dword ptr [esi] - add ebx, 4 - add esi, 4 - fmul st(0), st(2) - fistp dword ptr [edi-8] - fmul st(0), st(1) - fistp dword ptr [edi-4] - dec ecx - jnz mainloop - fstp st(0) - } -} - - -// Convert integer mix to floating-point - -static void X86_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) -{ - _asm { - mov esi, pSrc - mov edi, pOut1 - mov ebx, pOut2 - mov ecx, nCount - fld _i2fc -mainloop: - fild dword ptr [esi] - fild dword ptr [esi+4] - add ebx, 4 - add edi, 4 - fmul st(0), st(2) - add esi, 8 - fstp dword ptr [ebx-4] - fmul st(0), st(1) - fstp dword ptr [edi-4] - dec ecx - jnz mainloop - fstp st(0) - } -} - - static void X86_FloatToMonoMix(const float *pIn, int32 *pOut, uint32 nCount, const float _f2ic) { _asm { @@ -348,27 +134,6 @@ R2I_Loop: } } - -static void X86_MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _i2fc) -{ - _asm { - mov edx, pOut - mov eax, pSrc - mov ecx, nCount - fld _i2fc - sub edx, 4 -I2R_Loop: - fild DWORD PTR [eax] - add edx, 4 - fmul ST(0), ST(1) - dec ecx - lea eax, [eax+4] - fstp DWORD PTR [edx] - jnz I2R_Loop - fstp st(0) - } -} - #endif // ENABLE_X86 @@ -422,26 +187,10 @@ void StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCou return; } #endif // ENABLE_SSE2 - #if defined(ENABLE_X86) && defined(ENABLE_SSE) - if(GetProcSupport() & PROCSUPPORT_SSE) - { - SSE_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - return; - } - #endif // ENABLE_X86 && ENABLE_SSE - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) - { - AMD_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - return; - } - #endif // ENABLE_X86_AMD - #ifdef ENABLE_X86 - X86_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - #else // !ENABLE_X86 + { C_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - #endif // ENABLE_X86 + } } @@ -455,19 +204,10 @@ void FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 return; } #endif // ENABLE_SSE2 - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) - { - AMD_FloatToStereoMix(pIn1, pIn2, pOut, nCount, _f2ic); - return; - } - #endif // ENABLE_X86_AMD - #ifdef ENABLE_X86 - X86_FloatToStereoMix(pIn1, pIn2, pOut, nCount, _f2ic); - #else // !ENABLE_X86 + { C_FloatToStereoMix(pIn1, pIn2, pOut, nCount, _f2ic); - #endif // ENABLE_X86 + } } @@ -482,19 +222,10 @@ void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _ return; } #endif // ENABLE_X86 && ENABLE_SSE - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) - { - AMD_MonoMixToFloat(pSrc, pOut, nCount, _i2fc); - return; - } - #endif // ENABLE_X86_AMD - #ifdef ENABLE_X86 - X86_MonoMixToFloat(pSrc, pOut, nCount, _i2fc); - #else // !ENABLE_X86 + { C_MonoMixToFloat(pSrc, pOut, nCount, _i2fc); - #endif // ENABLE_X86 + } } @@ -502,20 +233,18 @@ void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _ void FloatToMonoMix(const float *pIn, int32 *pOut, uint32 nCount, const float _f2ic) { - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) + #ifdef ENABLE_X86 + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) { - AMD_FloatToMonoMix(pIn, pOut, nCount, _f2ic); + X86_FloatToMonoMix(pIn, pOut, nCount, _f2ic); return; } - #endif // ENABLE_X86_AMD - - #ifdef ENABLE_X86 - X86_FloatToMonoMix(pIn, pOut, nCount, _f2ic); - #else // !ENABLE_X86 - C_FloatToMonoMix(pIn, pOut, nCount, _f2ic); #endif // ENABLE_X86 + { + C_FloatToMonoMix(pIn, pOut, nCount, _f2ic); + } + } @@ -577,10 +306,15 @@ static void C_InterleaveFrontRear(mixsample_t *pFrontBuf, mixsample_t *pRearBuf, void InterleaveFrontRear(mixsample_t *pFrontBuf, mixsample_t *pRearBuf, uint32 nFrames) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); - #else - C_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); + return; + } #endif + { + C_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); + } } @@ -616,10 +350,15 @@ static void C_MonoFromStereo(mixsample_t *pMixBuf, uint32 nSamples) void MonoFromStereo(mixsample_t *pMixBuf, uint32 nSamples) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_MonoFromStereo(pMixBuf, nSamples); - #else - C_MonoFromStereo(pMixBuf, nSamples); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_MonoFromStereo(pMixBuf, nSamples); + return; + } #endif + { + C_MonoFromStereo(pMixBuf, nSamples); + } } @@ -740,10 +479,15 @@ static void C_StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rof void StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rofs, mixsample_t &lofs) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_StereoFill(pBuffer, nSamples, &rofs, &lofs); - #else - C_StereoFill(pBuffer, nSamples, rofs, lofs); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_StereoFill(pBuffer, nSamples, &rofs, &lofs); + return; + } #endif + { + C_StereoFill(pBuffer, nSamples, rofs, lofs); + } } @@ -818,8 +562,8 @@ static void C_EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSampl pBuffer[i*2+1] += lofs; } #ifndef MPT_INTMIXER - if(mpt::abs(rofs) < OFSTHRESHOLD) rofs = 0; - if(mpt::abs(lofs) < OFSTHRESHOLD) lofs = 0; + if(std::abs(rofs) < OFSTHRESHOLD) rofs = 0; + if(std::abs(lofs) < OFSTHRESHOLD) lofs = 0; #endif chn.nROfs = rofs; @@ -829,10 +573,15 @@ static void C_EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSampl void EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSamples) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_EndChannelOfs(&chn, pBuffer, nSamples); - #else - C_EndChannelOfs(chn, pBuffer, nSamples); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_EndChannelOfs(&chn, pBuffer, nSamples); + return; + } #endif + { + C_EndChannelOfs(chn, pBuffer, nSamples); + } } @@ -858,7 +607,7 @@ void DeinterleaveStereo(const mixsample_t * MPT_RESTRICT input, mixsample_t * MP #ifndef MODPLUG_TRACKER -void ApplyGain(int32 *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16) +void ApplyGain(MixSampleInt *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16) { if(gainFactor16_16 == (1<<16)) { @@ -866,7 +615,7 @@ void ApplyGain(int32 *soundBuffer, std::size_t channels, std::size_t countChunk, return; } // no clipping prevention is done here - int32 * buf = soundBuffer; + MixSampleInt * buf = soundBuffer; for(std::size_t i=0; i outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + if(gainFactor == 1.0f) + { + // nothing to do, gain == +/- 0dB + return; + } + for(std::size_t i = 0; i < countChunk; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { - ApplyGain( - outputBuffers[channel] + offset, - outputBuffers[channel] + offset + countChunk, - gainFactor); + outputBuffer(channel, offset + i) *= gainFactor; } } } +void ApplyGain(audio_buffer_planar outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + if(gainFactor == 1.0f) + { + // nothing to do, gain == +/- 0dB + return; + } + for(std::size_t i = 0; i < countChunk; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outputBuffer(channel, offset + i) *= gainFactor; + } + } +} + + #endif // !MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h index 27be04a99..c19e34643 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h @@ -17,6 +17,8 @@ OPENMPT_NAMESPACE_BEGIN struct ModChannel; +template struct audio_buffer_interleaved; +template struct audio_buffer_planar; 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); @@ -24,8 +26,10 @@ void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 uint32, const float _ void FloatToMonoMix(const float *pIn, int32 *pOut, uint32 uint32, const float _f2ic); #ifndef MODPLUG_TRACKER -void ApplyGain(int32 *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16); -void ApplyGain(float *outputBuffer, float * const *outputBuffers, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor); +void ApplyGain(MixSampleInt *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16); +void ApplyGain(MixSampleFloat *soundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor); +void ApplyGain(audio_buffer_interleaved outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor); +void ApplyGain(audio_buffer_planar outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor); #endif // !MODPLUG_TRACKER void InitMixBuffer(mixsample_t *pBuffer, uint32 nSamples); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp index f9615de4b..7a1057256 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp @@ -26,7 +26,7 @@ MixerSettings::MixerSettings() // Mixing Configuration gnChannels = 2; - gdwMixingFreq = 44100; + gdwMixingFreq = 48000; m_nPreAmp = 128; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp index 66ae13d44..6e9b45ea3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "Sndfile.h" #include "ModChannel.h" +#include "tuning.h" OPENMPT_NAMESPACE_BEGIN @@ -46,6 +47,7 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI if(resetMask & resetSetPosAdvanced) { + increment = SamplePosition(0); nPeriod = 0; position.Set(0); nLength = 0; @@ -56,11 +58,11 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI pModInstrument = nullptr; nCutOff = 0x7F; nResonance = 0; - nFilterMode = 0; + nFilterMode = FilterMode::LowPass; rightVol = leftVol = 0; newRightVol = newLeftVol = 0; rightRamp = leftRamp = 0; - nVolume = 0; // Needs to be 0 for SMP_NODEFAULTVOLUME flag + nVolume = 0; // Needs to be 0 for SMP_NODEFAULTVOLUME flag nVibratoPos = nTremoloPos = nPanbrelloPos = 0; nOldHiOffset = 0; nLeftVU = nRightVU = 0; @@ -70,7 +72,6 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI m_CalculateFreq = false; m_PortamentoFineSteps = 0; m_PortamentoTickSlide = 0; - m_Freq = 0; } if(resetMask & resetChannelSettings) @@ -89,7 +90,6 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI nRestorePanOnNewNote = 0; nRestoreCutoffOnNewNote = 0; nRestoreResonanceOnNewNote = 0; - } } @@ -124,7 +124,7 @@ ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping) const } ModCommand::NOTE plugNote = mpt::saturate_cast(nNote - nTranspose); // Caution: When in compatible mode, ModChannel::nNote stores the "real" note, not the mapped note! - if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (mpt::size(pModInstrument->NoteMap) + NOTE_MIN)) + if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN)) { plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN]; } @@ -132,4 +132,32 @@ ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping) const } +void ModChannel::SetInstrumentPan(int32 pan, const CSoundFile &sndFile) +{ + // IT compatibility: Instrument and sample panning does not override channel panning + // Test case: PanResetInstr.it + if(sndFile.m_playBehaviour[kITDoNotOverrideChannelPan]) + { + nRestorePanOnNewNote = static_cast(nPan + 1); + if(dwFlags[CHN_SURROUND]) + nRestorePanOnNewNote |= 0x8000; + } + nPan = pan; +} + + +void ModChannel::RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile) +{ + if(!HasCustomTuning()) + return; + + ModCommand::NOTE note = ModCommand::IsNote(nNote) ? nNote : nLastNote; + + if(sndFile.m_playBehaviour[kITRealNoteMapping] && note >= NOTE_MIN && note <= NOTE_MAX) + note = pModInstrument->NoteMap[note - NOTE_MIN]; + + nPeriod = mpt::saturate_round((nC5Speed << FREQ_FRACBITS) * vibratoFactor * pModInstrument->pTuning->GetRatio(note - NOTE_MIDDLEC + arpeggioSteps, nFineTune + m_PortamentoFineSteps)); +} + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h index 4cc1f7fa2..8f870a4a9 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h @@ -16,6 +16,7 @@ #include "ModInstrument.h" #include "modcommand.h" #include "Paula.h" +#include "tuningbase.h" OPENMPT_NAMESPACE_BEGIN @@ -39,17 +40,17 @@ struct ModChannel }; // Information used in the mixer (should be kept tight for better caching) - SamplePosition position; // Current play position (fixed point) - SamplePosition increment; // Sample speed relative to mixing frequency (fixed point) - const void *pCurrentSample; // Currently playing sample (nullptr if no sample is playing) - int32 leftVol; // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION) - int32 rightVol; // Ditto - int32 leftRamp; // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION) - int32 rightRamp; // Ditto - int32 rampLeftVol; // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION) - int32 rampRightVol; // Ditto - mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel - mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs + SamplePosition position; // Current play position (fixed point) + SamplePosition increment; // Sample speed relative to mixing frequency (fixed point) + const void *pCurrentSample; // Currently playing sample (nullptr if no sample is playing) + int32 leftVol; // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION) + int32 rightVol; // Ditto + int32 leftRamp; // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION) + int32 rightRamp; // Ditto + int32 rampLeftVol; // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION) + int32 rampRightVol; // Ditto + mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel + mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs mixsample_t nFilter_HP; SmpLength nLength; @@ -59,41 +60,42 @@ struct ModChannel mixsample_t nROfs, nLOfs; uint32 nRampLength; - const ModSample *pModSample; // Currently assigned sample slot (may already be stopped) + const ModSample *pModSample; // Currently assigned sample slot (may already be stopped) Paula::State paulaState; // Information not used in the mixer - const ModInstrument *pModInstrument; // Currently assigned instrument slot - SmpLength prevNoteOffset; // Offset for instrument-less notes for ProTracker/ScreamTracker + const ModInstrument *pModInstrument; // Currently assigned instrument slot + SmpLength prevNoteOffset; // Offset for instrument-less notes for ProTracker/ScreamTracker SmpLength oldOffset; - FlagSet dwOldFlags; // Flags from previous tick + FlagSet dwOldFlags; // Flags from previous tick int32 newLeftVol, newRightVol; int32 nRealVolume, nRealPan; int32 nVolume, nPan, nFadeOutVol; - int32 nPeriod, nC5Speed, nPortamentoDest; + int32 nPeriod; // Frequency in Hz if !CSoundFile::PeriodsAreFrequencies() or using custom tuning, 4x Amiga periods otherwise + int32 nC5Speed, nPortamentoDest; int32 cachedPeriod, glissandoPeriod; - int32 nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros - EnvInfo VolEnv, PanEnv, PitchEnv; // Envelope playback info - int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) 0...64 - int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64 + int32 nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros + EnvInfo VolEnv, PanEnv, PitchEnv; // Envelope playback info + int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) 0...64 + int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64 int32 nFineTune, nTranspose; int32 nPortamentoSlide, nAutoVibDepth; - uint32 nEFxOffset; // offset memory for Invert Loop (EFx, .MOD only) + uint32 nEFxOffset; // Offset memory for Invert Loop (EFx, .MOD only) int16 nVolSwing, nPanSwing; int16 nCutSwing, nResSwing; - int16 nRestorePanOnNewNote; //If > 0, nPan should be set to nRestorePanOnNewNote - 1 on new note. Used to recover from panswing. + uint16 nRestorePanOnNewNote; //If > 0, nPan should be set to nRestorePanOnNewNote - 1 on new note. Used to recover from pan swing and IT sample / instrument panning. High bit set = surround int16 nRetrigCount, nRetrigParam; ROWINDEX nPatternLoop; CHANNELINDEX nMasterChn; ModCommand rowCommand; // 8-bit members ResamplingMode resamplingMode; - uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote - uint8 nRestoreCutoffOnNewNote; // ditto + uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote + uint8 nRestoreCutoffOnNewNote; // ditto uint8 nNote; NewNoteAction nNNA; - uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros - uint8 nArpeggioLastNote, nArpeggioBaseNote; // For plugin arpeggio + uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros + uint8 nArpeggioLastNote, nArpeggioBaseNote; // For plugin arpeggio uint8 nNewNote, nNewIns, nOldIns, nCommand, nArpeggio; uint8 nOldVolumeSlide, nOldFineVolUpDown; uint8 nOldPortaUp, nOldPortaDown, nOldFinePortaUpDown, nOldExtraFinePortaUpDown; @@ -110,16 +112,17 @@ struct ModChannel uint8 nTremorCount, nTremorParam; uint8 nPatternLoopCount; uint8 nLeftVU, nRightVU; - uint8 nActiveMacro, nFilterMode; - uint8 nEFxSpeed, nEFxDelay; // memory for Invert Loop (EFx, .MOD only) - uint8 nNoteSlideCounter, nNoteSlideSpeed, nNoteSlideStep; // IMF / PTM Note Slide - uint8 lastZxxParam; // Memory for \xx slides + uint8 nActiveMacro; + FilterMode nFilterMode; + uint8 nEFxSpeed, nEFxDelay; // memory for Invert Loop (EFx, .MOD only) + 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. - //Currently used only for vibrato things - using in other context might be + //Currently used only for vibrato things - using in other context might be //problematic. bool m_ReCalculateFreqOnFirstTick : 1; @@ -128,8 +131,6 @@ struct ModChannel int32 m_PortamentoFineSteps, m_PortamentoTickSlide; - uint32 m_Freq; - //NOTE_PCs memory. float m_plugParamValueStep, m_plugParamTargetValue; uint16 m_RowPlugParam; @@ -166,40 +167,46 @@ struct ModChannel enum ResetFlags { - resetChannelSettings = 1, // Reload initial channel settings - resetSetPosBasic = 2, // Reset basic runtime channel attributes - resetSetPosAdvanced = 4, // Reset more runtime channel attributes - resetSetPosFull = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings, // Reset all runtime channel attributes - resetTotal = resetSetPosFull, + resetChannelSettings = 1, // Reload initial channel settings + resetSetPosBasic = 2, // Reset basic runtime channel attributes + resetSetPosAdvanced = 4, // Reset more runtime channel attributes + resetSetPosFull = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings, // Reset all runtime channel attributes + resetTotal = resetSetPosFull, }; void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel); void Stop(); - bool IsSamplePlaying() const { return !increment.IsZero(); } + bool IsSamplePlaying() const noexcept { return !increment.IsZero(); } - uint32 GetVSTVolume() { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; } + uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; } ModCommand::NOTE GetPluginNote(bool realNoteMapping) const; - // Check if the channel has a valid MIDI output. This function guarantees that pModInstrument != nullptr. - bool HasMIDIOutput() const { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); } + // Check if the channel has a valid MIDI output. A return value of true implies that pModInstrument != nullptr. + bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); } + // Check if the channel uses custom tuning. A return value of true implies that pModInstrument != nullptr. + bool HasCustomTuning() const noexcept { return pModInstrument != nullptr && pModInstrument->pTuning != nullptr; } // Check if currently processed loop is a sustain loop. pModSample is not checked for validity! - bool InSustainLoop() const { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; } + bool InSustainLoop() const noexcept { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; } void UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins); + + void SetInstrumentPan(int32 pan, const CSoundFile &sndFile); + + void RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile); }; // Default pattern channel settings struct ModChannelSettings { - FlagSet dwFlags; // Channel flags - uint16 nPan; // Initial pan (0...256) - uint16 nVolume; // Initial channel volume (0...64) - PLUGINDEX nMixPlugin; // Assigned plugin - char szName[MAX_CHANNELNAME]; // Channel name + FlagSet dwFlags; // Channel flags + uint16 nPan; // Initial pan (0...256) + uint16 nVolume; // Initial channel volume (0...64) + PLUGINDEX nMixPlugin; // Assigned plugin + mpt::charbuf szName; // Channel name ModChannelSettings() { @@ -212,7 +219,7 @@ struct ModChannelSettings nPan = 128; nVolume = 64; nMixPlugin = 0; - szName[0] = '\0'; + szName = ""; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp index 604e4eaa0..887b79993 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp @@ -138,46 +138,15 @@ void InstrumentEnvelope::Sanitize(uint8 maxValue) ModInstrument::ModInstrument(SAMPLEINDEX sample) { - nFadeOut = 256; - dwFlags.reset(); - nGlobalVol = 64; - nPan = 32 * 4; - - nNNA = NNA_NOTECUT; - nDCT = DCT_NONE; - nDNA = DNA_NOTECUT; - - nPanSwing = 0; - nVolSwing = 0; SetCutoff(0, false); SetResonance(0, false); - wMidiBank = 0; - nMidiProgram = 0; - nMidiChannel = 0; - nMidiDrumKey = 0; - midiPWD = 2; - - nPPC = NOTE_MIDDLEC - 1; - nPPS = 0; - - nMixPlug = 0; - nVolRampUp = 0; - resampling = SRCMODE_DEFAULT; - nCutSwing = 0; - nResSwing = 0; - nFilterMode = FLTMODE_UNCHANGED; pitchToTempoLock.Set(0); - pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; - pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; pTuning = CSoundFile::GetDefaultTuning(); AssignSample(sample); ResetNoteMap(); - - MemsetZero(name); - MemsetZero(filename); } @@ -195,7 +164,7 @@ void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) dwFlags.reset(INS_SETPANNING); SetCutoff(GetCutoff(), false); SetResonance(GetResonance(), false); - nFilterMode = FLTMODE_UNCHANGED; + filterMode = FilterMode::Unchanged; nCutSwing = nPanSwing = nResSwing = nVolSwing = 0; @@ -208,11 +177,11 @@ void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) if(nMidiChannel == MidiMappedChannel) { - nMidiChannel = 1; + nMidiChannel = MidiFirstChannel; } // FT2 only has unsigned Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load). - midiPWD = static_cast(mpt::abs(midiPWD)); + midiPWD = static_cast(std::abs(midiPWD)); Limit(midiPWD, int8(0), int8(36)); nGlobalVol = 64; @@ -253,7 +222,7 @@ void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) SetTuning(nullptr); pitchToTempoLock.Set(0); nCutSwing = nResSwing = 0; - nFilterMode = FLTMODE_UNCHANGED; + filterMode = FilterMode::Unchanged; nVolRampUp = 0; } } @@ -264,12 +233,11 @@ std::set ModInstrument::GetSamples() const { std::set referencedSamples; - for(size_t i = 0; i < CountOf(Keyboard); i++) + for(const auto sample : Keyboard) { - // 0 isn't a sample. - if(Keyboard[i] != 0) + if(sample) { - referencedSamples.insert(Keyboard[i]); + referencedSamples.insert(sample); } } @@ -281,12 +249,11 @@ std::set ModInstrument::GetSamples() const // The caller has to initialize the vector. void ModInstrument::GetSamples(std::vector &referencedSamples) const { - for(size_t i = 0; i < CountOf(Keyboard); i++) + for(const auto sample : Keyboard) { - // 0 isn't a sample. - if(Keyboard[i] != 0 && Keyboard[i] < referencedSamples.size()) + if(sample != 0 && sample < referencedSamples.size()) { - referencedSamples[Keyboard[i]] = true; + referencedSamples[sample] = true; } } } @@ -324,11 +291,23 @@ void ModInstrument::Sanitize(MODTYPE modType) PanEnv.Sanitize(); PitchEnv.Sanitize(range); - for(size_t i = 0; i < CountOf(NoteMap); i++) + for(size_t i = 0; i < std::size(NoteMap); i++) { if(NoteMap[i] < NOTE_MIN || NoteMap[i] > NOTE_MAX) NoteMap[i] = static_cast(i + NOTE_MIN); } + + if(!Resampling::IsKnownMode(resampling)) + resampling = SRCMODE_DEFAULT; +} + + +void ModInstrument::Transpose(int8 amount) +{ + for(auto ¬e : NoteMap) + { + note = static_cast(Clamp(note + amount, NOTE_MIN, NOTE_MAX)); + } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h index 41c4b5121..b5f5a2a9d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h @@ -12,6 +12,7 @@ #include "BuildSettings.h" +#include "modcommand.h" #include "tuningbase.h" #include "Snd_defs.h" #include "../common/FlagSet.h" @@ -64,88 +65,94 @@ struct InstrumentEnvelope : public std::vector // Instrument Struct struct ModInstrument { - 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. + uint32 nFadeOut = 256; // Instrument fadeout speed + uint32 nGlobalVol = 64; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) + uint32 nPan = 32 * 4; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. - uint16 nVolRampUp; // Default sample ramping up, 0 = use global default + uint16 nVolRampUp = 0; // Default sample ramping up, 0 = use global default - uint16 wMidiBank; // MIDI Bank (1...16384). 0 = Don't send. - uint8 nMidiProgram; // MIDI Program (1...128). 0 = Don't send. - uint8 nMidiChannel; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). - uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader) - int8 midiPWD; // MIDI Pitch Wheel Depth in semitones + ResamplingMode resampling = SRCMODE_DEFAULT; // Resampling mode - 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 - uint8 nIFR; // Default filter resonance (0...127). Used if the high bit is set + FlagSet dwFlags; // Instrument flags + NewNoteAction nNNA = NNA_NOTECUT; // New note action + DuplicateCheckType nDCT = DCT_NONE; // Duplicate check type (i.e. which condition will trigger the duplicate note action) + DuplicateNoteAction nDNA = DNA_NOTECUT; // Duplicate note action + uint8 nPanSwing = 0; // Random panning factor (0...64) + uint8 nVolSwing = 0; // Random volume factor (0...100) - int8 nPPS; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) - uint8 nPPC; // Pitch/Pan centre (zero-based, default is NOTE_MIDDLE_C - 1) + uint8 nIFC = 0; // Default filter cutoff (0...127). Used if the high bit is set + uint8 nIFR = 0; // Default filter resonance (0...127). Used if the high bit is set + uint8 nCutSwing = 0; // Random cutoff factor (0...64) + uint8 nResSwing = 0; // Random resonance factor (0...64) + FilterMode filterMode = FilterMode::Unchanged; // Default filter mode - 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) - InstrFilterMode nFilterMode; // Default filter mode - PlugVelocityHandling pluginVelocityHandling; // How to deal with plugin velocity - PlugVolumeHandling pluginVolumeHandling; // How to deal with plugin volume - ResamplingMode resampling; // Resampling mode - TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) - CTuning *pTuning; // sample tuning assigned to this instrument + int8 nPPS = 0; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) + uint8 nPPC = NOTE_MIDDLEC - NOTE_MIN; // Pitch/Pan centre (zero-based) - InstrumentEnvelope VolEnv; // Volume envelope data - InstrumentEnvelope PanEnv; // Panning envelope data - InstrumentEnvelope PitchEnv; // Pitch / filter envelope data + uint16 wMidiBank = 0; // MIDI Bank (1...16384). 0 = Don't send. + uint8 nMidiProgram = 0; // MIDI Program (1...128). 0 = Don't send. + uint8 nMidiChannel = 0; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). + uint8 nMidiDrumKey = 0; // Drum set note mapping (currently only used by the .MID loader) + int8 midiPWD = 2; // MIDI Pitch Wheel Depth in semitones + PLUGINDEX nMixPlug = 0; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) - uint8 NoteMap[128]; // Note mapping, e.g. C-5 => D-5. - SAMPLEINDEX Keyboard[128]; // Sample mapping, e.g. C-5 => Sample 1 + PlugVelocityHandling pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; // How to deal with plugin velocity + PlugVolumeHandling pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; // How to deal with plugin volume - char name[MAX_INSTRUMENTNAME]; - char filename[MAX_INSTRUMENTFILENAME]; + TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) + CTuning *pTuning = nullptr; // sample tuning assigned to this instrument + + InstrumentEnvelope VolEnv; // Volume envelope data + InstrumentEnvelope PanEnv; // Panning envelope data + InstrumentEnvelope PitchEnv; // Pitch / filter envelope data + + std::array NoteMap; // Note mapping, e.g. C-5 => D-5 + std::array Keyboard; // Sample mapping, e.g. C-5 => Sample 1 + + mpt::charbuf name; + mpt::charbuf filename; + + std::string GetName() const { return name; } + std::string GetFilename() const { return filename; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // WHEN adding new members here, ALSO update InstrumentExtensions.cpp // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - void SetTuning(CTuning* pT) - { - pTuning = pT; - } - ModInstrument(SAMPLEINDEX sample = 0); // Assign all notes to a given sample. void AssignSample(SAMPLEINDEX sample) { - for(size_t n = 0; n < CountOf(Keyboard); n++) - { - Keyboard[n] = sample; - } + Keyboard.fill(sample); } // Reset note mapping (i.e. every note is mapped to itself) void ResetNoteMap() { - for(size_t n = 0; n < CountOf(NoteMap); n++) + for(size_t n = 0; n < std::size(NoteMap); n++) { NoteMap[n] = static_cast(n + 1); } } + // Transpose entire note mapping by given number of semitones + void Transpose(int8 amount); + bool IsCutoffEnabled() const { return (nIFC & 0x80) != 0; } bool IsResonanceEnabled() const { return (nIFR & 0x80) != 0; } uint8 GetCutoff() const { return (nIFC & 0x7F); } uint8 GetResonance() const { return (nIFR & 0x7F); } - void SetCutoff(uint8 cutoff, bool enable) { nIFC = std::min(cutoff, 0x7F) | (enable ? 0x80 : 0x00); } - void SetResonance(uint8 resonance, bool enable) { nIFR = std::min(resonance, 0x7F) | (enable ? 0x80 : 0x00); } + void SetCutoff(uint8 cutoff, bool enable) { nIFC = std::min(cutoff, uint8(0x7F)) | (enable ? 0x80 : 0x00); } + void SetResonance(uint8 resonance, bool enable) { nIFR = std::min(resonance, uint8(0x7F)) | (enable ? 0x80 : 0x00); } bool HasValidMIDIChannel() const { return (nMidiChannel >= 1 && nMidiChannel <= 17); } + void SetTuning(CTuning *pT) + { + pTuning = pT; + } + // Get a reference to a specific envelope of this instrument const InstrumentEnvelope &GetEnvelope(EnvelopeType envType) const { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp index 05a01179a..380b9d325 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp @@ -66,7 +66,7 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType) uFlags.set(CHN_PINGPONGLOOP, uFlags[CHN_PINGPONGSUSTAIN]); } nSustainStart = nSustainEnd = 0; - uFlags.reset(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN); + uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); } // All XM samples have default panning, and XM's autovibrato settings are rather limited. @@ -125,6 +125,7 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType) // Initialize sample slot with default values. void ModSample::Initialize(MODTYPE type) { + FreeSample(); nLength = 0; nLoopStart = nLoopEnd = 0; nSustainStart = nSustainEnd = 0; @@ -144,7 +145,7 @@ void ModSample::Initialize(MODTYPE type) nVibDepth = 0; nVibRate = 0; rootNote = 0; - filename[0] = '\0'; + filename = ""; SetDefaultCuePoints(); } @@ -189,7 +190,7 @@ void *ModSample::AllocateSample(SmpLength numFrames, size_t bytesPerSample) if(allocSize != 0) { - char *p = new (std::nothrow) char[allocSize]; + char *p = new(std::nothrow) char[allocSize]; if(p != nullptr) { memset(p, 0, allocSize); @@ -209,7 +210,7 @@ size_t ModSample::GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerS // * 2x InterpolationMaxLookahead before the loop point (because we start at InterpolationMaxLookahead before the loop point and will look backwards from there as well) // * 2x InterpolationMaxLookahead after the loop point (for wrap-around) // * 4x InterpolationMaxLookahead for the sustain loop (same as the two points above) - + const SmpLength maxSize = Util::MaxValueOfType(numSamples); const SmpLength lookaheadBufferSize = (MaxSamplingPointSize + 1 + 4 + 4) * InterpolationMaxLookahead; @@ -239,7 +240,7 @@ void ModSample::FreeSample(void *samplePtr) { if(samplePtr) { - delete[] (((char *)samplePtr) - (InterpolationMaxLookahead * MaxSamplingPointSize)); + delete[](((char *)samplePtr) - (InterpolationMaxLookahead * MaxSamplingPointSize)); } } @@ -282,9 +283,144 @@ void ModSample::SetSustainLoop(SmpLength start, SmpLength end, bool enable, bool } +namespace // Unnamed namespace for local implementation functions. +{ + +template +class PrecomputeLoop +{ +protected: + T *target; + const T *sampleData; + SmpLength loopEnd; + int numChannels; + bool pingpong; + bool ITPingPongMode; + +public: + PrecomputeLoop(T *target, const T *sampleData, SmpLength loopEnd, int numChannels, bool pingpong, bool ITPingPongMode) + : target(target), sampleData(sampleData), loopEnd(loopEnd), numChannels(numChannels), pingpong(pingpong), ITPingPongMode(ITPingPongMode) + { + if(loopEnd > 0) + { + CopyLoop(true); + CopyLoop(false); + } + } + + void CopyLoop(bool direction) const + { + // Direction: true = start reading and writing forward, false = start reading and writing backward (write direction never changes) + const int numSamples = 2 * InterpolationMaxLookahead + (direction ? 1 : 0); // Loop point is included in forward loop expansion + T *dest = target + numChannels * (2 * InterpolationMaxLookahead - 1); // Write buffer offset + SmpLength readPosition = loopEnd - 1; + const int writeIncrement = direction ? 1 : -1; + int readIncrement = writeIncrement; + + for(int i = 0; i < numSamples; i++) + { + // Copy sample over to lookahead buffer + for(int c = 0; c < numChannels; c++) + { + dest[c] = sampleData[readPosition * numChannels + c]; + } + dest += writeIncrement * numChannels; + + if(readPosition == loopEnd - 1 && readIncrement > 0) + { + // Reached end of loop while going forward + if(pingpong) + { + readIncrement = -1; + if(ITPingPongMode && readPosition > 0) + { + readPosition--; + } + } else + { + readPosition = 0; + } + } else if(readPosition == 0 && readIncrement < 0) + { + // Reached start of loop while going backward + if(pingpong) + { + readIncrement = 1; + } else + { + readPosition = loopEnd - 1; + } + } else + { + readPosition += readIncrement; + } + } + } +}; + + +template +void PrecomputeLoopsImpl(ModSample &smp, const CSoundFile &sndFile) +{ + const int numChannels = smp.GetNumChannels(); + const int copySamples = numChannels * InterpolationMaxLookahead; + + T *sampleData = static_cast(smp.samplev()); + T *afterSampleStart = sampleData + smp.nLength * numChannels; + T *loopLookAheadStart = afterSampleStart + copySamples; + T *sustainLookAheadStart = loopLookAheadStart + 4 * copySamples; + + // Hold sample on the same level as the last sampling point at the end to prevent extra pops with interpolation. + // Do the same at the sample start, too. + for(int i = 0; i < (int)InterpolationMaxLookahead; i++) + { + for(int c = 0; c < numChannels; c++) + { + afterSampleStart[i * numChannels + c] = afterSampleStart[-numChannels + c]; + sampleData[-(i + 1) * numChannels + c] = sampleData[c]; + } + } + + if(smp.uFlags[CHN_LOOP]) + { + PrecomputeLoop(loopLookAheadStart, + sampleData + smp.nLoopStart * numChannels, + smp.nLoopEnd - smp.nLoopStart, + numChannels, + smp.uFlags[CHN_PINGPONGLOOP], + sndFile.m_playBehaviour[kITPingPongMode]); + } + if(smp.uFlags[CHN_SUSTAINLOOP]) + { + PrecomputeLoop(sustainLookAheadStart, + sampleData + smp.nSustainStart * numChannels, + smp.nSustainEnd - smp.nSustainStart, + numChannels, + smp.uFlags[CHN_PINGPONGSUSTAIN], + sndFile.m_playBehaviour[kITPingPongMode]); + } +} + +} // unnamed namespace + + void ModSample::PrecomputeLoops(CSoundFile &sndFile, bool updateChannels) { - ctrlSmp::PrecomputeLoops(*this, sndFile, updateChannels); + if(!HasSampleData()) + return; + + SanitizeLoops(); + + // Update channels with possibly changed loop values + if(updateChannels) + { + ctrlSmp::UpdateLoopPoints(*this, sndFile); + } + + if(GetElementarySampleSize() == 2) + PrecomputeLoopsImpl(*this, sndFile); + else if(GetElementarySampleSize() == 1) + PrecomputeLoopsImpl(*this, sndFile); } @@ -322,9 +458,12 @@ void ModSample::TransposeToFrequency() // Return tranpose.finetune as 25.7 fixed point value. -int ModSample::FrequencyToTranspose(uint32 freq) +int32 ModSample::FrequencyToTranspose(uint32 freq) { - return mpt::saturate_round(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2))); + if(!freq) + return 0; + else + return mpt::saturate_round(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2))); } @@ -332,8 +471,8 @@ void ModSample::FrequencyToTranspose() { int f2t = 0; if(nC5Speed) - f2t = FrequencyToTranspose(nC5Speed); - RelativeTone = static_cast(f2t >> 7); + f2t = Clamp(FrequencyToTranspose(nC5Speed), -16384, 16383); + RelativeTone = static_cast(f2t / 128); nFineTune = static_cast(f2t & 0x7F); } @@ -352,7 +491,8 @@ bool ModSample::HasCustomCuePoints() const { for(SmpLength i = 0; i < CountOf(cues); i++) { - if(cues[i] != (i + 1) << 11) return true; + if(cues[i] != (i + 1) << 11) + return true; } } return false; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h index 924e8c52b..c23769ca3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h @@ -42,7 +42,9 @@ struct ModSample 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]; + mpt::charbuf filename; + std::string GetFilename() const { return filename; } + union { SmpLength cues[9]; @@ -69,13 +71,13 @@ struct ModSample { return pData.pSample; } - MPT_FORCEINLINE const mpt::byte *sampleb() const noexcept + MPT_FORCEINLINE const std::byte *sampleb() const noexcept { - return mpt::void_cast(pData.pSample); + return mpt::void_cast(pData.pSample); } - MPT_FORCEINLINE mpt::byte *sampleb() noexcept + MPT_FORCEINLINE std::byte *sampleb() noexcept { - return mpt::void_cast(pData.pSample); + return mpt::void_cast(pData.pSample); } MPT_FORCEINLINE const int8 *sample8() const noexcept { @@ -144,7 +146,7 @@ struct ModSample // Transpose <-> Frequency conversions static uint32 TransposeToFrequency(int transpose, int finetune = 0); void TransposeToFrequency(); - static int FrequencyToTranspose(uint32 freq); + static int32 FrequencyToTranspose(uint32 freq); void FrequencyToTranspose(); // Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h index d03cbf35c..ce20e79ce 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h @@ -29,10 +29,10 @@ size_t CopyMonoSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourc MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t frameSize = SampleConversion::input_inc; - const size_t countFrames = std::min(sourceSize / frameSize, sample.nLength); + const size_t countFrames = std::min(sourceSize / frameSize, static_cast(sample.nLength)); size_t numFrames = countFrames; SampleConversion sampleConv(conv); - const mpt::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); + const std::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { @@ -52,11 +52,11 @@ size_t CopyStereoInterleavedSample(ModSample &sample, const Tbyte *sourceBuffer, MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t frameSize = 2 * SampleConversion::input_inc; - const size_t countFrames = std::min(sourceSize / frameSize, sample.nLength); + const size_t countFrames = std::min(sourceSize / frameSize, static_cast(sample.nLength)); size_t numFrames = countFrames; SampleConversion sampleConvLeft(conv); SampleConversion sampleConvRight(conv); - const mpt::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); + const std::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { @@ -79,14 +79,14 @@ size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_ MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t sampleSize = SampleConversion::input_inc; - const size_t sourceSizeLeft = std::min(sample.nLength * SampleConversion::input_inc, sourceSize); - const size_t sourceSizeRight = std::min(sample.nLength * SampleConversion::input_inc, sourceSize - sourceSizeLeft); + const size_t sourceSizeLeft = std::min(static_cast(sample.nLength) * SampleConversion::input_inc, sourceSize); + const size_t sourceSizeRight = std::min(static_cast(sample.nLength) * SampleConversion::input_inc, sourceSize - sourceSizeLeft); const size_t countSamplesLeft = sourceSizeLeft / sampleSize; const size_t countSamplesRight = sourceSizeRight / sampleSize; size_t numSamplesLeft = countSamplesLeft; SampleConversion sampleConvLeft(conv); - const mpt::byte * MPT_RESTRICT inBufLeft = mpt::byte_cast(sourceBuffer); + const std::byte * MPT_RESTRICT inBufLeft = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBufLeft = static_cast(sample.samplev()); while(numSamplesLeft--) { @@ -97,7 +97,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; + const std::byte * MPT_RESTRICT inBufRight = mpt::byte_cast(sourceBuffer) + sample.nLength * SampleConversion::input_inc; typename SampleConversion::output_t * MPT_RESTRICT outBufRight = static_cast(sample.samplev()) + 1; while(numSamplesRight--) { @@ -121,7 +121,7 @@ size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size size_t numSamples = sample.nLength * sample.GetNumChannels(); LimitMax(numSamples, sourceSize / inSize); - const mpt::byte * inBuf = mpt::byte_cast(sourceBuffer); + const std::byte * inBuf = mpt::byte_cast(sourceBuffer); // Finding max value SampleConversion sampleConv(conv); 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 15c3945c9..69ee1f279 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp @@ -12,9 +12,6 @@ #include "ModSequence.h" #include "Sndfile.h" #include "mod_specifications.h" -#ifdef MODPLUG_TRACKER -#include "../mptrack/Reporting.h" -#endif // MODPLUG_TRACKER #include "../common/version.h" #include "../common/serialization_utils.h" @@ -25,13 +22,13 @@ OPENMPT_NAMESPACE_BEGIN ModSequence::ModSequence(CSoundFile &sndFile) : m_sndFile(sndFile) - , m_restartPos(0) { } ModSequence& ModSequence::operator=(const ModSequence &other) { + MPT_ASSERT(&other.m_sndFile == &m_sndFile); if(&other == this) return *this; std::vector::assign(other.begin(), other.end()); @@ -234,6 +231,9 @@ ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bo PATTERNINDEX ModSequence::EnsureUnique(ORDERINDEX ord) { PATTERNINDEX pat = at(ord); + if(!IsValidPat(ord)) + return pat; + for(const auto &sequence : m_sndFile.Order) { ORDERINDEX ords = sequence.GetLength(); @@ -281,18 +281,11 @@ void ModSequenceSet::SetSequence(SEQUENCEINDEX n) } -SEQUENCEINDEX ModSequenceSet::AddSequence(bool duplicate) +SEQUENCEINDEX ModSequenceSet::AddSequence() { - if(GetNumSequences() == MAX_SEQUENCES) + if(GetNumSequences() >= MAX_SEQUENCES) return SEQUENCEINDEX_INVALID; - if(duplicate) - { - m_Sequences.push_back(m_Sequences[m_currentSeq]); - m_Sequences.back().m_name.clear(); // Don't copy sequence name. - } else - { - m_Sequences.push_back(ModSequence(m_sndFile)); - } + m_Sequences.push_back(ModSequence{m_sndFile}); SetSequence(GetNumSequences() - 1); return GetNumSequences() - 1; } @@ -311,86 +304,97 @@ void ModSequenceSet::RemoveSequence(SEQUENCEINDEX i) #ifdef MODPLUG_TRACKER +bool ModSequenceSet::Rearrange(const std::vector &newOrder) +{ + if(newOrder.empty() || newOrder.size() > MAX_SEQUENCES) + return false; + + const auto oldSequences = std::move(m_Sequences); + m_Sequences.assign(newOrder.size(), ModSequence{m_sndFile}); + for(size_t i = 0; i < newOrder.size(); i++) + { + if(newOrder[i] < oldSequences.size()) + m_Sequences[i] = oldSequences[newOrder[i]]; + } + + if(m_currentSeq > m_Sequences.size()) + m_currentSeq = GetNumSequences() - 1u; + return true; +} + + void ModSequenceSet::OnModTypeChanged(MODTYPE oldType) { for(auto &seq : m_Sequences) { seq.AdjustToNewModType(oldType); } - // Multisequences not suppported by other formats - if(oldType != MOD_TYPE_NONE && m_sndFile.GetModSpecifications().sequencesMax <= 1) + if(m_sndFile.GetModSpecifications(oldType).sequencesMax > 1 && m_sndFile.GetModSpecifications().sequencesMax <= 1) MergeSequences(); - - // Convert sequence with separator patterns into multiple sequences? - if(oldType != MOD_TYPE_NONE && m_sndFile.GetModSpecifications().sequencesMax > 1 && GetNumSequences() == 1) - ConvertSubsongsToMultipleSequences(); } -bool ModSequenceSet::ConvertSubsongsToMultipleSequences() +bool ModSequenceSet::CanSplitSubsongs() const { - // Allow conversion only if there's only one sequence. - if(GetNumSequences() != 1 || m_sndFile.GetModSpecifications().sequencesMax <= 1) + return GetNumSequences() == 1 && m_sndFile.GetModSpecifications().sequencesMax > 1 && m_Sequences[0].HasSubsongs(); +} + + +bool ModSequenceSet::SplitSubsongsToMultipleSequences() +{ + if(!CanSplitSubsongs()) return false; - m_Sequences[0].Shrink(); - bool hasSepPatterns = std::find_if(m_Sequences[0].begin(), m_Sequences[0].end(), - [&] (PATTERNINDEX pat) { return pat != GetIgnoreIndex() && !m_sndFile.Patterns.IsValidPat(pat); }) != m_Sequences[0].end(); bool modified = false; + const ORDERINDEX length = m_Sequences[0].GetLengthTailTrimmed(); - if(hasSepPatterns && - Reporting::Confirm("The order list contains separator items.\nThe new format supports multiple sequences, do you want to convert those separate tracks into multiple song sequences?", - "Order list conversion", false, true) == cnfYes) + for(ORDERINDEX ord = 0; ord < length; ord++) { - ORDERINDEX length = m_Sequences[0].GetLength(); - for(ORDERINDEX ord = 0; ord < length; ord++) + // End of subsong? + if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex()) { - // End of subsong? - if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex()) + // Remove all separator patterns between current and next subsong first + while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord])) { - // Remove all separator patterns between current and next subsong first - while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord])) - { - m_Sequences[0][ord] = GetInvalidPatIndex(); - ord++; - modified = true; - } - if(ord >= length) - break; - - const SEQUENCEINDEX newSeq = AddSequence(false); - if(newSeq == SEQUENCEINDEX_INVALID) - break; - - const ORDERINDEX startOrd = ord; - m_Sequences[newSeq].reserve(length - startOrd); + m_Sequences[0][ord] = GetInvalidPatIndex(); + ord++; modified = true; + } + if(ord >= length) + break; - // Now, move all following orders to the new sequence - while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex()) + const SEQUENCEINDEX newSeq = AddSequence(); + if(newSeq == SEQUENCEINDEX_INVALID) + break; + + const ORDERINDEX startOrd = ord; + m_Sequences[newSeq].reserve(length - startOrd); + modified = true; + + // Now, move all following orders to the new sequence + while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex()) + { + PATTERNINDEX copyPat = m_Sequences[0][ord]; + m_Sequences[newSeq].push_back(copyPat); + m_Sequences[0][ord] = GetInvalidPatIndex(); + ord++; + + // Is this a valid pattern? adjust pattern jump commands, if necessary. + if(m_sndFile.Patterns.IsValidPat(copyPat)) { - PATTERNINDEX copyPat = m_Sequences[0][ord]; - m_Sequences[newSeq].push_back(copyPat); - m_Sequences[0][ord] = GetInvalidPatIndex(); - ord++; - - // Is this a valid pattern? adjust pattern jump commands, if necessary. - if(m_sndFile.Patterns.IsValidPat(copyPat)) + for(auto &m : m_sndFile.Patterns[copyPat]) { - for(auto &m : m_sndFile.Patterns[copyPat]) + if(m.command == CMD_POSITIONJUMP && m.param >= startOrd) { - if(m.command == CMD_POSITIONJUMP && m.param >= startOrd) - { - m.param = static_cast(m.param - startOrd); - } + m.param = static_cast(m.param - startOrd); } } } - ord--; } + ord--; } - SetSequence(0); } + SetSequence(0); return modified; } @@ -443,7 +447,7 @@ bool ModSequenceSet::MergeSequences() const ORDERINDEX lengthTrimmed = seq.GetLengthTailTrimmed(); if(firstOrder + lengthTrimmed > m_sndFile.GetModSpecifications().ordersMax) { - m_sndFile.AddToLog(mpt::format("WARNING: Cannot merge Sequence %1 (too long!)")(seqNum)); + m_sndFile.AddToLog(mpt::format("WARNING: Cannot merge Sequence %1 (too long!)")(seqNum + 1)); continue; } firstSeq.reserve(firstOrder + lengthTrimmed); @@ -497,6 +501,14 @@ bool ModSequence::IsPositionLocked(ORDERINDEX position) const return(m_sndFile.m_lockOrderStart != ORDERINDEX_INVALID && (position < m_sndFile.m_lockOrderStart || position > m_sndFile.m_lockOrderEnd)); } + + +bool ModSequence::HasSubsongs() const +{ + const auto endPat = begin() + GetLengthTailTrimmed(); + return std::find_if(begin(), endPat, + [&](PATTERNINDEX pat) { return pat != GetIgnoreIndex() && !m_sndFile.Patterns.IsValidPat(pat); }) != endPat; +} #endif // MODPLUG_TRACKER @@ -566,7 +578,9 @@ void WriteModSequence(std::ostream& oStrm, const ModSequence& seq) { srlztn::SsbWrite ssb(oStrm); ssb.BeginWrite(FileIdSequence, Version::Current().GetRawVersion()); - ssb.WriteItem(seq.GetName(), "n"); + int8 useUTF8 = 1; + ssb.WriteItem(useUTF8, "u"); + ssb.WriteItem(mpt::ToCharset(mpt::Charset::UTF8, seq.GetName()), "n"); const uint16 length = seq.GetLengthTailTrimmed(); ssb.WriteItem(length, "l"); ssb.WriteItem(seq, "a", srlztn::VectorWriter(length)); @@ -577,15 +591,17 @@ void WriteModSequence(std::ostream& oStrm, const ModSequence& seq) #endif // MODPLUG_NO_FILESAVE -void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t) +void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdSequence, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; + int8 useUTF8 = 0; + ssb.ReadItem(useUTF8, "u"); std::string str; ssb.ReadItem(str, "n"); - seq.SetName(str); + seq.SetName(mpt::ToUnicode(useUTF8 ? mpt::Charset::UTF8 : defaultCharset, str)); ORDERINDEX nSize = 0; ssb.ReadItem(nSize, "l"); LimitMax(nSize, ModSpecs::mptm.ordersMax); @@ -615,7 +631,7 @@ void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq) #endif // MODPLUG_NO_FILESAVE -void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t) +void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdSequences, Version::Current().GetRawVersion()); @@ -637,7 +653,7 @@ void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t) for(SEQUENCEINDEX i = 0; i < seqs; i++) { seq(i).SetRestartPos(legacyRestartPos); - ssb.ReadItem(seq(i), srlztn::ID::FromInt(i), &ReadModSequence); + ssb.ReadItem(seq(i), srlztn::ID::FromInt(i), [defaultCharset](std::istream &iStrm, ModSequence &seq, std::size_t dummy) { return ReadModSequence(iStrm, seq, dummy, defaultCharset); }); } seq.m_currentSeq = (currentSeq < seq.GetNumSequences()) ? currentSeq : 0; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h index 09e0454ce..cca8d623b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h @@ -26,9 +26,9 @@ class ModSequence: public std::vector friend class ModSequenceSet; protected: - std::string m_name; // Sequence name. - CSoundFile &m_sndFile; // Associated CSoundFile. - ORDERINDEX m_restartPos; // Restart position when playback of this order ended + mpt::ustring m_name; // Sequence name. + CSoundFile &m_sndFile; // Associated CSoundFile. + ORDERINDEX m_restartPos = 0; // Restart position when playback of this order ended public: ModSequence(CSoundFile &sndFile); @@ -80,9 +80,9 @@ public: void AdjustToNewModType(const MODTYPE oldtype); // Returns the internal representation of a stop '---' index - static PATTERNINDEX GetInvalidPatIndex() { return uint16_max; } + static constexpr PATTERNINDEX GetInvalidPatIndex() { return uint16_max; } // Returns the internal representation of an ignore '+++' index - static PATTERNINDEX GetIgnoreIndex() { return uint16_max - 1; } + static constexpr PATTERNINDEX GetIgnoreIndex() { return uint16_max - 1; } // Returns the previous/next order ignoring skip indices (+++). // If no previous/next order exists, return first/last order, and zero @@ -108,11 +108,13 @@ public: #ifdef MODPLUG_TRACKER // Check if a playback position is currently locked (inaccessible) bool IsPositionLocked(ORDERINDEX position) const; + // Check if this sequence has subsongs separated by invalid ("---" or non-existing) patterns + bool HasSubsongs() const; #endif // MODPLUG_TRACKER // Sequence name setter / getter - inline void SetName(const std::string &newName) { m_name = newName;} - inline std::string GetName() const { return m_name; } + inline void SetName(const mpt::ustring &newName) { m_name = newName;} + inline mpt::ustring GetName() const { return m_name; } // Restart position setter / getter inline void SetRestartPos(ORDERINDEX restartPos) { m_restartPos = restartPos; } @@ -123,12 +125,12 @@ public: class ModSequenceSet { friend void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); - friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t); + friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset); protected: - std::vector m_Sequences; // Array of sequences. + std::vector m_Sequences; // Array of sequences. CSoundFile &m_sndFile; - SEQUENCEINDEX m_currentSeq; // Index of current sequence. + SEQUENCEINDEX m_currentSeq = 0; // Index of current sequence. public: ModSequenceSet(CSoundFile &sndFile); @@ -149,26 +151,31 @@ public: // Sets working sequence. void SetSequence(SEQUENCEINDEX); - // Add new sequence. - // If duplicate is true, new sequence is a duplicate of the current sequence. + // Add new empty sequence. // Returns the ID of the new sequence, or SEQUENCEINDEX_INVALID on failure. - SEQUENCEINDEX AddSequence(bool duplicate = true); + SEQUENCEINDEX AddSequence(); // Removes given sequence. void RemoveSequence(SEQUENCEINDEX); // Returns the internal representation of a stop '---' index - static PATTERNINDEX GetInvalidPatIndex() { return ModSequence::GetInvalidPatIndex(); } + static constexpr PATTERNINDEX GetInvalidPatIndex() { return ModSequence::GetInvalidPatIndex(); } // Returns the internal representation of an ignore '+++' index - static PATTERNINDEX GetIgnoreIndex() { return ModSequence::GetIgnoreIndex(); } + static constexpr PATTERNINDEX GetIgnoreIndex() { return ModSequence::GetIgnoreIndex(); } #ifdef MODPLUG_TRACKER + // Assigns a new set of sequences. The vector contents indicate which existing sequences to keep / duplicate or if a new sequences should be inserted (SEQUENCEINDEX_INVALID) + // The function fails if the vector is empty or contains too many sequences. + bool Rearrange(const std::vector &newOrder); + // Adjust sequence when converting between module formats void OnModTypeChanged(MODTYPE oldType); + // Check if there is a single sequences that qualifies for subsong splitting + bool CanSplitSubsongs() const; // If there are subsongs (separated by "---" patterns) in the module, // asks user whether to convert these into multiple sequences (given that the // modformat supports multiple sequences). // Returns true if sequences were modified, false otherwise. - bool ConvertSubsongsToMultipleSequences(); + bool SplitSubsongsToMultipleSequences(); // Convert the sequence's restart position information to a pattern command. bool RestartPosToPattern(SEQUENCEINDEX seq); @@ -192,12 +199,12 @@ 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); +void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize, mpt::Charset defaultCharset); #ifndef MODPLUG_NO_FILESAVE void WriteModSequence(std::ostream& oStrm, const ModSequence& seq); #endif // MODPLUG_NO_FILESAVE -void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t); +void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset); #ifndef MODPLUG_NO_FILESAVE void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp index 4f949528e..297ba14af 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp @@ -15,11 +15,9 @@ OPENMPT_NAMESPACE_BEGIN -OPL::OPL() +OPL::OPL(uint32 samplerate) { - m_KeyOnBlock.fill(0); - m_OPLtoChan.fill(CHANNELINDEX_INVALID); - m_ChanToOPL.fill(OPL_CHANNEL_INVALID); + Initialize(samplerate); } @@ -32,7 +30,7 @@ OPL::~OPL() void OPL::Initialize(uint32 samplerate) { if(m_opl == nullptr) - m_opl = mpt::make_unique(samplerate); + m_opl = std::make_unique(samplerate); else m_opl->SetSampleRate(samplerate); Reset(); @@ -45,7 +43,7 @@ void OPL::Mix(int32 *target, size_t count, uint32 volumeFactorQ16) 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); + const int32 factor = Util::muldiv_unsigned(volumeFactorQ16, 6169, (1 << 16)); while(count--) { int16 l, r; @@ -69,7 +67,7 @@ uint16 OPL::ChannelToRegister(uint8 oplCh) // 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 }; + static constexpr uint8 OPLChannelToOperator[] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; if(oplCh < 9) return OPLChannelToOperator[oplCh]; else @@ -79,33 +77,49 @@ uint16 OPL::OperatorToRegister(uint8 oplCh) uint8 OPL::GetVoice(CHANNELINDEX c) const { - return m_ChanToOPL[c]; + if((m_ChanToOPL[c] & OPL_CHANNEL_CUT) || m_ChanToOPL[c] == OPL_CHANNEL_INVALID) + return OPL_CHANNEL_INVALID; + return m_ChanToOPL[c] & OPL_CHANNEL_MASK; } uint8 OPL::AllocateVoice(CHANNELINDEX c) { // Can we re-use a previous channel? - if(m_ChanToOPL[c] != OPL_CHANNEL_INVALID) + if(auto oplCh = m_ChanToOPL[c]; oplCh != OPL_CHANNEL_INVALID) { - return GetVoice(c); + if(!(m_ChanToOPL[c] & OPL_CHANNEL_CUT)) + return oplCh; + // Check re-use hint + oplCh &= OPL_CHANNEL_MASK; + if(m_OPLtoChan[oplCh] == CHANNELINDEX_INVALID || m_OPLtoChan[oplCh] == c) + { + m_OPLtoChan[oplCh] = c; + m_ChanToOPL[c] = oplCh; + return oplCh; + } } // Search for unused channel or channel with released note - uint8 releasedChn = OPL_CHANNEL_INVALID; + uint8 releasedChn = OPL_CHANNEL_INVALID, releasedCutChn = OPL_CHANNEL_INVALID; for(uint8 oplCh = 0; oplCh < OPL_CHANNELS; oplCh++) { if(m_OPLtoChan[oplCh] == CHANNELINDEX_INVALID) { m_OPLtoChan[oplCh] = c; m_ChanToOPL[c] = oplCh; - return GetVoice(c); + return oplCh; } else if(!(m_KeyOnBlock[oplCh] & KEYON_BIT)) { releasedChn = oplCh; + if(m_ChanToOPL[m_OPLtoChan[oplCh]] & OPL_CHANNEL_CUT) + releasedCutChn = oplCh; } } if(releasedChn != OPL_CHANNEL_INVALID) { + // Prefer channel that has been marked as cut over channel that has just been released + if(releasedCutChn != OPL_CHANNEL_INVALID) + releasedChn = releasedCutChn; m_ChanToOPL[m_OPLtoChan[releasedChn]] = OPL_CHANNEL_INVALID; m_OPLtoChan[releasedChn] = c; m_ChanToOPL[c] = releasedChn; @@ -116,7 +130,7 @@ uint8 OPL::AllocateVoice(CHANNELINDEX c) void OPL::MoveChannel(CHANNELINDEX from, CHANNELINDEX to) { - uint8 oplCh = m_ChanToOPL[from]; + uint8 oplCh = GetVoice(from); if(oplCh == OPL_CHANNEL_INVALID) return; m_OPLtoChan[oplCh] = to; @@ -135,10 +149,15 @@ void OPL::NoteOff(CHANNELINDEX c) } -void OPL::NoteCut(CHANNELINDEX c) +void OPL::NoteCut(CHANNELINDEX c, bool unassign) { + uint8 oplCh = GetVoice(c); + if(oplCh == OPL_CHANNEL_INVALID) + return; NoteOff(c); - Volume(c, 0, false); + Volume(c, 0, false); // Note that a volume of 0 is not complete silence; the release portion of the sound will still be heard at -48dB + if(unassign) + m_ChanToOPL[c] |= OPL_CHANNEL_CUT; } @@ -169,7 +188,7 @@ void OPL::Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beating // 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 = std::min(static_cast(fnum + (c & 3)), uint16(1023)); fnum |= (block << 10); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h index 331be872b..1d4ee6fb0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h @@ -67,21 +67,21 @@ public: STEREO_BITS = VOICE_TO_LEFT | VOICE_TO_RIGHT, }; - OPL(); + OPL(uint32 samplerate); ~OPL(); void Initialize(uint32 samplerate); void Mix(int32 *buffer, size_t count, uint32 volumeFactorQ16); void NoteOff(CHANNELINDEX c); - void NoteCut(CHANNELINDEX c); + void NoteCut(CHANNELINDEX c, bool unassign = true); void Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beatingOscillators); void Volume(CHANNELINDEX c, uint8 vol, bool applyToModulator); int8 Pan(CHANNELINDEX c, int32 pan); void Patch(CHANNELINDEX c, const OPLPatch &patch); - void Reset(); - bool IsActive(CHANNELINDEX c) { return GetVoice(c) != OPL_CHANNEL_INVALID; } + bool IsActive(CHANNELINDEX c) const { return GetVoice(c) != OPL_CHANNEL_INVALID; } void MoveChannel(CHANNELINDEX from, CHANNELINDEX to); + void Reset(); protected: static uint16 ChannelToRegister(uint8 oplCh); @@ -92,7 +92,9 @@ protected: enum { - OPL_CHANNELS = 18, // 9 for OPL2 or 18 for OPL3 + OPL_CHANNELS = 18, // 9 for OPL2 or 18 for OPL3 + OPL_CHANNEL_CUT = 0x80, // Indicates that the channel has been cut and used as a hint to re-use the channel for the same tracker channel if possible + OPL_CHANNEL_MASK = 0x7F, OPL_CHANNEL_INVALID = 0xFF, OPL_BASERATE = 49716, }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp index b42a5c83b..7f05f6380 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp @@ -34,6 +34,15 @@ uint16 PageInfo::GetPagePhysicalSize() const } +uint16 PageInfo::GetPageHeaderSize() const +{ + uint16 size = 0; + size += sizeof(PageHeader); + size += header.page_segments; + return size; +} + + uint16 PageInfo::GetPageDataSize() const { uint16 size = 0; @@ -70,10 +79,13 @@ bool AdvanceToPageMagic(FileReader &file) } -bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData) +bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector *pageData) { pageInfo = PageInfo(); - pageData.clear(); + if(pageData) + { + (*pageData).clear(); + } if(!file.ReadMagic("OggS")) { return false; @@ -98,7 +110,13 @@ bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData { return false; } - filePageReader.ReadVector(pageData, pageDataSize); + if(pageData) + { + filePageReader.ReadVector(*pageData, pageDataSize); + } else + { + filePageReader.Skip(pageDataSize); + } filePageReader.SkipBack(pageInfo.GetPagePhysicalSize()); { mpt::crc32_ogg calculatedCRC; @@ -107,8 +125,18 @@ bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData filePageReader.ReadArray(rawHeader); std::memset(rawHeader + 22, 0, 4); // clear out old crc calculatedCRC.process(rawHeader, rawHeader + sizeof(rawHeader)); + filePageReader.Skip(pageInfo.header.page_segments); calculatedCRC.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments); - calculatedCRC.process(pageData); + if(pageData) + { + filePageReader.Skip(pageDataSize); + calculatedCRC.process(*pageData); + } else + { + FileReader pageDataReader = filePageReader.ReadChunk(pageDataSize); + auto pageDataView = pageDataReader.GetPinnedRawDataView(); + calculatedCRC.process(pageDataView.GetSpan()); + } if(calculatedCRC != pageInfo.header.CRC_checksum) { return false; @@ -119,6 +147,19 @@ bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData } +bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData) +{ + return ReadPage(file, pageInfo, &pageData); +} + + +bool ReadPage(FileReader &file) +{ + PageInfo pageInfo; + return ReadPage(file, pageInfo); +} + + bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector &pageData) { pageInfo = PageInfo(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h index 4d97f9240..c11b4b92d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h @@ -51,6 +51,7 @@ struct PageInfo MemsetZero(segment_table); } uint16 GetPagePhysicalSize() const; + uint16 GetPageHeaderSize() const; uint16 GetPageDataSize() const; }; @@ -58,7 +59,9 @@ struct PageInfo // returns false on EOF bool AdvanceToPageMagic(FileReader &file); +bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector *pageData = nullptr); bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData); +bool ReadPage(FileReader &file); bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector &pageData); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp index 4b357d6e9..619bc21fd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp @@ -2,7 +2,7 @@ * Paula.cpp * --------- * Purpose: Emulating the Amiga's sound chip, Paula, by implementing resampling using band-limited steps (BLEPs) -* Notes : (currently none) +* Notes : The BLEP table generator code is a translation of Antti S. Lankila's original Python code. * Authors: OpenMPT Devs * Antti S. Lankila * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -10,266 +10,257 @@ #include "stdafx.h" #include "Paula.h" +#include "TinyFFT.h" + +#include +#include OPENMPT_NAMESPACE_BEGIN +// Compute Bessel function Izero(y) using a series approximation +double Izero(double y); + namespace Paula { -// Tables are: A500 (filter off), A500 (filter on) -static constexpr int32 WinSincIntegral[2][2048] = +namespace { + +MPT_NOINLINE std::vector KaiserFIR(int numTaps, double cutoff, double beta) +{ + const double izeroBeta = Izero(beta); + const double kPi = 4.0 * std::atan(1.0) * cutoff; + const double xDiv = 1.0 / ((numTaps / 2) * (numTaps / 2)); + const int numTapsDiv2 = numTaps / 2; + std::vector result(numTaps); + for(int i = 0; i < numTaps; i++) { - 131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, - 131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131071,131071, - 131071,131071,131071,131071,131071,131071,131071,131071,131071,131070,131070,131070, - 131070,131070,131069,131069,131069,131068,131068,131068,131067,131067,131066,131066, - 131065,131065,131064,131063,131063,131062,131061,131060,131059,131058,131056,131055, - 131054,131052,131050,131049,131047,131045,131043,131040,131038,131035,131033,131030, - 131026,131023,131020,131016,131012,131008,131003,130998,130993,130988,130982,130976, - 130970,130963,130956,130949,130941,130932,130924,130914,130905,130895,130884,130872, - 130861,130848,130835,130821,130807,130792,130776,130759,130742,130724,130705,130685, - 130664,130642,130620,130596,130571,130545,130518,130490,130461,130430,130398,130365, - 130331,130295,130257,130219,130178,130136,130093,130047,130000,129951,129901,129848, - 129794,129737,129679,129618,129555,129490,129423,129353,129281,129207,129130,129050, - 128968,128883,128795,128704,128611,128514,128415,128312,128206,128097,127985,127869, - 127750,127627,127501,127371,127237,127100,126959,126813,126664,126510,126353,126191, - 126025,125854,125679,125499,125315,125126,124933,124734,124531,124323,124110,123891, - 123668,123439,123205,122965,122720,122470,122214,121952,121685,121412,121133,120849, - 120558,120261,119959,119650,119335,119014,118687,118354,118014,117668,117315,116956, - 116591,116219,115840,115455,115063,114665,114260,113849,113430,113005,112574,112135, - 111690,111239,110780,110315,109843,109364,108879,108387,107888,107383,106871,106352, - 105827,105295,104757,104212,103661,103104,102540,101970,101394,100812,100223,99629, - 99028,98422,97810,97192,96568,95939,95305,94665,94020,93370,92714,92054,91389,90719, - 90045,89366,88682,87995,87303,86607,85908,85205,84498,83788,83075,82358,81639,80916, - 80191,79464,78734,78002,77268,76533,75795,75056,74316,73575,72833,72090,71346,70602, - 69858,69114,68370,67626,66883,66140,65399,64658,63919,63181,62445,61711,60979,60249, - 59521,58796,58074,57355,56639,55926,55217,54512,53810,53113,52419,51731,51046,50367, - 49693,49023,48359,47701,47048,46400,45759,45124,44495,43872,43256,42646,42043,41447, - 40858,40276,39702,39134,38575,38023,37478,36941,36413,35892,35379,34874,34378,33890, - 33410,32938,32475,32020,31574,31137,30708,30288,29876,29473,29079,28693,28317,27948, - 27589,27238,26896,26562,26238,25921,25613,25314,25023,24740,24466,24200,23942,23692, - 23451,23217,22991,22773,22562,22359,22164,21975,21794,21621,21454,21294,21140,20994, - 20853,20719,20592,20470,20354,20244,20139,20040,19946,19857,19774,19694,19620,19550, - 19484,19422,19364,19310,19260,19213,19169,19128,19090,19054,19022,18991,18963,18936, - 18912,18889,18867,18847,18828,18810,18792,18776,18759,18743,18727,18711,18695,18679, - 18662,18644,18626,18607,18587,18565,18542,18518,18492,18465,18436,18404,18371,18336, - 18298,18259,18216,18172,18124,18074,18022,17966,17908,17847,17783,17716,17646,17572, - 17496,17416,17334,17248,17159,17066,16971,16872,16770,16664,16556,16444,16329,16211, - 16090,15966,15839,15709,15576,15440,15301,15159,15015,14868,14718,14566,14412,14255, - 14096,13935,13771,13606,13439,13270,13099,12927,12753,12578,12401,12224,12045,11866, - 11685,11504,11322,11140,10958,10775,10592,10409,10226,10044,9862,9680,9499,9319,9139, - 8961,8783,8607,8432,8258,8086,7915,7747,7580,7415,7252,7091,6932,6776,6622,6471, - 6322,6176,6032,5892,5754,5619,5488,5359,5234,5111,4992,4877,4764,4655,4550,4448, - 4349,4254,4163,4075,3990,3910,3832,3759,3689,3622,3560,3500,3445,3393,3344,3299, - 3257,3219,3184,3153,3124,3099,3078,3059,3044,3031,3022,3015,3011,3010,3012,3016, - 3023,3033,3044,3058,3075,3093,3113,3136,3160,3186,3213,3242,3273,3305,3338,3372, - 3408,3444,3481,3520,3558,3597,3637,3677,3718,3758,3799,3839,3880,3920,3960,4000, - 4039,4077,4115,4152,4188,4224,4258,4291,4323,4354,4384,4412,4439,4464,4488,4510, - 4530,4549,4566,4581,4594,4606,4615,4623,4628,4631,4633,4632,4629,4624,4617,4608, - 4597,4583,4568,4550,4530,4508,4484,4458,4429,4399,4366,4332,4296,4257,4217,4175, - 4130,4085,4037,3988,3937,3884,3830,3774,3717,3658,3598,3537,3475,3411,3347,3281, - 3215,3147,3079,3010,2940,2870,2799,2728,2657,2585,2513,2440,2368,2296,2224,2151, - 2080,2008,1937,1866,1796,1726,1657,1589,1521,1454,1389,1324,1260,1197,1135,1075, - 1016,958,901,846,792,740,689,640,592,546,502,459,419,379,342,307,273,241,211,183, - 156,132,109,88,69,52,37,24,12,2,-5,-11,-16,-18,-19,-18,-16,-11,-6,2,11,21,33,47,61, - 77,95,113,133,154,176,200,224,249,275,302,329,358,387,416,447,477,508,540,572,604, - 636,669,702,734,767,800,832,864,896,928,960,991,1021,1051,1081,1110,1138,1166,1193, - 1219,1245,1270,1293,1316,1338,1359,1379,1398,1416,1433,1448,1463,1476,1488,1499, - 1509,1518,1525,1531,1536,1540,1542,1543,1543,1542,1539,1536,1530,1524,1517,1508, - 1498,1487,1475,1462,1447,1432,1415,1397,1379,1359,1338,1317,1294,1271,1247,1222, - 1196,1170,1143,1115,1086,1057,1028,998,967,936,905,874,842,809,777,744,712,679,646, - 613,581,548,515,483,450,418,387,355,324,293,263,233,204,175,147,119,92,66,40,15, - -10,-33,-56,-78,-99,-120,-139,-158,-176,-193,-209,-224,-238,-252,-264,-275,-286, - -295,-304,-311,-318,-324,-329,-332,-335,-337,-338,-338,-338,-336,-333,-330,-326, - -321,-315,-308,-301,-293,-284,-274,-264,-253,-242,-229,-217,-203,-190,-175,-161, - -145,-130,-114,-98,-81,-64,-47,-29,-11,6,24,42,61,79,97,115,134,152,170,188,206,223, - 241,258,275,291,308,324,340,355,370,384,399,412,425,438,450,462,473,484,494,503, - 512,521,529,536,542,548,553,558,562,566,568,570,572,573,573,573,572,570,568,565, - 562,558,553,548,543,537,530,523,515,507,498,489,479,469,459,448,437,426,414,402, - 389,377,364,351,337,324,310,296,282,268,254,239,225,211,196,182,168,153,139,125, - 111,97,83,70,56,43,30,17,5,-7,-19,-31,-42,-53,-64,-75,-85,-94,-104,-113,-121,-129, - -137,-144,-151,-158,-164,-170,-175,-180,-184,-188,-192,-195,-198,-200,-202,-203, - -204,-205,-205,-204,-204,-203,-201,-199,-197,-195,-192,-188,-185,-181,-176,-172, - -167,-162,-156,-151,-145,-139,-132,-126,-119,-112,-104,-97,-90,-82,-74,-66,-59,-51, - -42,-34,-26,-18,-10,-2,7,15,23,31,39,47,54,62,70,77,85,92,99,106,112,119,125,131, - 137,143,148,154,159,163,168,172,176,180,183,187,190,192,195,197,199,201,202,203, - 204,205,205,205,205,204,203,202,201,200,198,196,194,192,189,186,183,180,177,173, - 169,165,161,157,153,148,143,139,134,129,124,119,113,108,103,97,92,86,81,75,70,64, - 59,53,48,42,37,32,26,21,16,11,6,1,-4,-9,-13,-18,-22,-26,-30,-34,-38,-42,-46,-49, - -52,-55,-58,-61,-64,-66,-69,-71,-73,-74,-76,-78,-79,-80,-81,-82,-82,-83,-83,-83, - -83,-83,-83,-82,-82,-81,-80,-79,-78,-77,-75,-74,-72,-70,-68,-66,-64,-62,-60,-57, - -55,-52,-50,-47,-44,-42,-39,-36,-33,-30,-27,-24,-21,-18,-15,-12,-9,-6,-3,0,3,6,8, - 11,14,17,20,22,25,27,30,32,35,37,39,41,43,45,47,49,50,52,54,55,56,58,59,60,61,61, - 62,63,63,64,64,65,65,65,65,65,64,64,64,63,63,62,61,61,60,59,58,57,56,55,53,52,51, - 49,48,46,45,43,41,40,38,36,35,33,31,29,28,26,24,22,20,19,17,15,13,11,10,8,6,5,3,1, - 0,-2,-3,-5,-6,-8,-9,-10,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-23, - -24,-24,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24, - -24,-23,-23,-22,-21,-21,-20,-19,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-10,-9, - -8,-7,-6,-5,-4,-3,-2,-1,0,1,1,2,3,4,5,6,6,7,8,9,9,10,11,11,12,12,13,14,14,15,15, - 15,16,16,16,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,16,16, - 16,16,15,15,14,14,14,13,13,12,12,11,11,11,10,10,9,9,8,8,7,7,6,6,5,5,4,4,3,3,2,2,1, - 1,0,0,-1,-1,-1,-2,-2,-3,-3,-3,-4,-4,-4,-4,-5,-5,-5,-5,-6,-6,-6,-6,-6,-6,-6,-7,-7, - -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-6,-6,-6,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-4,-4,-4, - -4,-4,-3,-3,-3,-3,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,3,3,3, - 3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3, - 3,3,3,3,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,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,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,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,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,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,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,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,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,0,0,0 - }, + double fsinc; + if(i == numTapsDiv2) + { + fsinc = 1.0; + } else + { + const double x = i - numTapsDiv2; + const double xPi = x * kPi; + // - sinc - - Kaiser window - -sinc- + fsinc = std::sin(xPi) * Izero(beta * std::sqrt(1 - x * x * xDiv)) / (izeroBeta * xPi); + } + + result[i] = fsinc * cutoff; + } + return result; +} + + +MPT_NOINLINE void FIR_MinPhase(std::vector &table, const TinyFFT &fft) +{ + std::vector> cepstrum(fft.Size()); + MPT_ASSERT(cepstrum.size() >= table.size()); + for(size_t i = 0; i < table.size(); i++) + cepstrum[i] = table[i]; + // Compute the real cepstrum: fft -> abs + ln -> ifft -> real + fft.FFT(cepstrum); + for(auto &v : cepstrum) + v = std::log(std::abs(v)); + fft.IFFT(cepstrum); + fft.Normalize(cepstrum); + + // Window the cepstrum in such a way that anticausal components become rejected + for(size_t i = 1; i < cepstrum.size() / 2; i++) { - 131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, - 131072,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, - 131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, - 131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131070, - 131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131069,131069, - 131069,131069,131069,131069,131069,131069,131069,131069,131069,131069,131069,131069, - 131069,131068,131068,131068,131068,131068,131068,131068,131068,131068,131068,131067, - 131067,131067,131067,131067,131067,131067,131066,131066,131066,131066,131066,131065, - 131065,131065,131065,131064,131064,131064,131064,131063,131063,131062,131062,131062, - 131061,131061,131060,131060,131059,131059,131058,131058,131057,131056,131056,131055, - 131054,131053,131052,131052,131051,131050,131049,131048,131046,131045,131044,131043, - 131041,131040,131038,131037,131035,131034,131032,131030,131028,131026,131024,131022, - 131020,131017,131015,131012,131010,131007,131004,131001,130998,130994,130991,130987, - 130984,130980,130976,130972,130968,130963,130959,130954,130949,130944,130938,130933, - 130927,130921,130915,130909,130902,130895,130888,130881,130873,130865,130857,130849, - 130840,130831,130822,130813,130803,130793,130782,130771,130760,130749,130737,130725, - 130712,130699,130685,130672,130657,130643,130628,130612,130596,130580,130563,130545, - 130528,130509,130490,130471,130451,130430,130409,130388,130365,130343,130319,130295, - 130270,130245,130219,130193,130165,130137,130109,130079,130049,130018,129987,129954, - 129921,129887,129853,129817,129781,129744,129706,129667,129627,129587,129545,129503, - 129459,129415,129370,129324,129276,129228,129179,129129,129078,129026,128972,128918, - 128862,128806,128748,128690,128630,128569,128507,128443,128379,128313,128246,128178, - 128109,128038,127966,127893,127819,127743,127667,127588,127509,127428,127346,127262, - 127177,127091,127004,126915,126824,126732,126639,126545,126449,126351,126252,126152, - 126050,125947,125842,125736,125628,125519,125408,125296,125182,125067,124950,124832, - 124712,124591,124468,124344,124218,124090,123961,123830,123698,123564,123429,123292, - 123154,123014,122872,122729,122585,122438,122291,122141,121990,121838,121684,121528, - 121371,121212,121052,120891,120727,120562,120396,120228,120059,119888,119715,119541, - 119366,119189,119011,118831,118649,118467,118282,118097,117909,117721,117531,117339, - 117146,116952,116757,116560,116361,116161,115960,115758,115554,115349,115143,114935, - 114726,114516,114305,114092,113878,113663,113447,113229,113011,112791,112570,112348, - 112124,111900,111675,111448,111220,110992,110762,110531,110300,110067,109833,109598, - 109363,109126,108889,108650,108411,108171,107930,107688,107445,107201,106957,106712, - 106466,106219,105972,105723,105474,105225,104974,104723,104471,104219,103966,103712, - 103458,103203,102948,102692,102435,102178,101920,101662,101403,101144,100885,100624, - 100364,100103,99841,99579,99317,99054,98791,98527,98264,97999,97735,97470,97204,96939, - 96673,96407,96140,95873,95606,95339,95071,94804,94536,94267,93999,93730,93461,93192, - 92923,92653,92383,92114,91844,91573,91303,91033,90762,90491,90220,89949,89678,89407, - 89136,88865,88593,88321,88050,87778,87506,87234,86962,86690,86418,86146,85874,85602, - 85330,85057,84785,84513,84240,83968,83696,83423,83151,82878,82606,82333,82061,81789, - 81516,81244,80971,80699,80427,80154,79882,79610,79337,79065,78793,78521,78249,77977, - 77705,77433,77161,76889,76617,76345,76074,75802,75531,75259,74988,74717,74446,74175, - 73904,73633,73362,73092,72821,72551,72280,72010,71740,71470,71201,70931,70661,70392, - 70123,69854,69585,69317,69048,68780,68512,68244,67976,67709,67441,67174,66907,66641, - 66374,66108,65842,65576,65311,65046,64781,64516,64252,63988,63724,63460,63197,62934, - 62672,62409,62147,61886,61624,61363,61103,60843,60583,60323,60064,59805,59547,59289, - 59031,58774,58517,58261,58005,57749,57494,57239,56985,56731,56478,56225,55973,55721, - 55470,55219,54968,54718,54469,54220,53971,53724,53476,53229,52983,52737,52492,52247, - 52003,51759,51516,51274,51032,50791,50550,50310,50070,49831,49593,49355,49118,48881, - 48645,48410,48175,47941,47707,47474,47242,47010,46779,46549,46319,46090,45861,45633, - 45406,45179,44953,44728,44503,44279,44056,43833,43611,43389,43168,42948,42728,42510, - 42291,42074,41857,41640,41425,41210,40995,40782,40568,40356,40144,39933,39723,39513, - 39304,39095,38887,38680,38473,38267,38062,37857,37653,37449,37247,37044,36843,36642, - 36442,36242,36043,35845,35647,35450,35253,35057,34862,34667,34473,34280,34087,33894, - 33703,33512,33321,33132,32942,32754,32566,32378,32192,32006,31820,31635,31451,31267, - 31084,30901,30719,30538,30357,30177,29997,29818,29640,29462,29285,29108,28932,28756, - 28581,28407,28233,28060,27888,27716,27545,27374,27204,27034,26865,26697,26529,26362, - 26195,26029,25863,25699,25534,25371,25207,25045,24883,24722,24561,24401,24241,24082, - 23924,23766,23609,23453,23297,23142,22987,22833,22679,22526,22374,22222,22071,21921, - 21771,21622,21473,21325,21177,21031,20884,20739,20594,20449,20306,20162,20020,19878, - 19737,19596,19456,19316,19178,19039,18902,18765,18628,18493,18358,18223,18089,17956, - 17823,17691,17560,17429,17299,17169,17040,16912,16784,16657,16531,16405,16280,16155, - 16031,15908,15785,15663,15541,15420,15300,15180,15061,14942,14824,14707,14590,14474, - 14358,14243,14129,14015,13901,13789,13677,13565,13454,13344,13234,13125,13016,12908, - 12801,12694,12588,12482,12377,12272,12168,12064,11961,11859,11757,11655,11554,11454, - 11354,11255,11156,11058,10960,10863,10766,10670,10575,10480,10385,10291,10197,10104, - 10012,9920,9828,9737,9646,9556,9466,9377,9289,9200,9113,9025,8939,8852,8766,8681, - 8596,8512,8428,8344,8261,8178,8096,8014,7933,7852,7772,7692,7612,7533,7455,7376, - 7299,7221,7144,7068,6992,6916,6841,6766,6692,6618,6544,6471,6398,6326,6254,6182, - 6111,6041,5970,5901,5831,5762,5693,5625,5557,5490,5423,5356,5290,5224,5159,5093, - 5029,4965,4901,4837,4774,4711,4649,4587,4525,4464,4404,4343,4283,4224,4164,4105, - 4047,3989,3931,3874,3817,3760,3704,3648,3593,3538,3483,3429,3375,3321,3268,3215, - 3163,3111,3059,3008,2957,2906,2856,2806,2756,2707,2658,2610,2562,2514,2466,2419, - 2373,2326,2280,2235,2189,2144,2100,2056,2012,1968,1925,1882,1839,1797,1755,1714, - 1672,1631,1591,1551,1511,1471,1432,1393,1354,1316,1278,1240,1203,1165,1129,1092, - 1056,1020,984,949,914,879,845,811,777,743,710,677,644,612,580,548,516,485,454,423, - 393,362,332,303,273,244,215,186,158,129,101,74,46,19,-8,-35,-61,-88,-114,-140,-165, - -191,-216,-241,-265,-290,-314,-338,-362,-386,-409,-432,-455,-478,-501,-523,-545, - -567,-589,-610,-632,-653,-674,-694,-715,-735,-756,-775,-795,-815,-834,-853,-872, - -891,-910,-928,-947,-965,-983,-1000,-1018,-1035,-1052,-1069,-1086,-1103,-1119,-1136, - -1152,-1168,-1184,-1199,-1215,-1230,-1245,-1260,-1275,-1289,-1304,-1318,-1332,-1346, - -1360,-1373,-1387,-1400,-1413,-1426,-1439,-1452,-1464,-1477,-1489,-1501,-1513,-1524, - -1536,-1547,-1558,-1570,-1581,-1591,-1602,-1612,-1623,-1633,-1643,-1653,-1663,-1672, - -1682,-1691,-1700,-1709,-1718,-1727,-1735,-1744,-1752,-1760,-1768,-1776,-1784,-1792, - -1799,-1807,-1814,-1821,-1828,-1835,-1842,-1848,-1855,-1861,-1867,-1873,-1879,-1885, - -1891,-1896,-1902,-1907,-1913,-1918,-1923,-1928,-1932,-1937,-1942,-1946,-1950,-1955, - -1959,-1963,-1967,-1971,-1974,-1978,-1981,-1985,-1988,-1991,-1994,-1997,-2000,-2003, - -2006,-2009,-2011,-2013,-2016,-2018,-2020,-2022,-2024,-2026,-2028,-2030,-2032,-2033, - -2035,-2036,-2037,-2039,-2040,-2041,-2042,-2043,-2044,-2045,-2045,-2046,-2047,-2047, - -2048,-2048,-2048,-2049,-2049,-2049,-2049,-2049,-2049,-2049,-2049,-2048,-2048,-2048, - -2047,-2047,-2046,-2046,-2045,-2044,-2043,-2043,-2042,-2041,-2040,-2039,-2038,-2036, - -2035,-2034,-2033,-2031,-2030,-2028,-2027,-2025,-2024,-2022,-2020,-2019,-2017,-2015, - -2013,-2011,-2009,-2007,-2005,-2003,-2001,-1998,-1996,-1994,-1992,-1989,-1987,-1984, - -1982,-1979,-1977,-1974,-1971,-1969,-1966,-1963,-1960,-1957,-1954,-1952,-1949,-1946, - -1942,-1939,-1936,-1933,-1930,-1927,-1923,-1920,-1917,-1913,-1910,-1906,-1903,-1899, - -1896,-1892,-1889,-1885,-1881,-1878,-1874,-1870,-1866,-1863,-1859,-1855,-1851,-1847, - -1843,-1839,-1835,-1831,-1827,-1823,-1819,-1814,-1810,-1806,-1802,-1798,-1793,-1789, - -1785,-1780,-1776,-1771,-1767,-1763,-1758,-1754,-1749,-1745,-1740,-1736,-1731,-1726, - -1722,-1717,-1713,-1708,-1703,-1698,-1694,-1689,-1684,-1680,-1675,-1670,-1665,-1660, - -1656,-1651,-1646,-1641,-1636,-1631,-1626,-1622,-1617,-1612,-1607,-1602,-1597,-1592, - -1587,-1582,-1577,-1572,-1567,-1562,-1557,-1552,-1547,-1542,-1537,-1532,-1527,-1522, - -1517,-1512,-1507,-1502,-1497,-1492,-1487,-1482,-1477,-1471,-1466,-1461,-1456,-1451, - -1446,-1441,-1436,-1431,-1426,-1421,-1416,-1411,-1406,-1401,-1395,-1390,-1385,-1380, - -1375,-1370,-1365,-1360,-1355,-1350,-1345,-1340,-1335,-1330,-1325,-1320,-1315,-1310, - -1305,-1300,-1295,-1290,-1285,-1280,-1275,-1270,-1265,-1260,-1255,-1250,-1245,-1240, - -1235,-1230,-1225,-1220,-1215,-1210,-1205,-1200,-1195,-1190,-1185,-1180,-1175,-1171, - -1166,-1161,-1156,-1151,-1146,-1141,-1136,-1131,-1127,-1122,-1117,-1112,-1107,-1102, - -1098,-1093,-1088,-1083,-1078,-1074,-1069,-1064,-1059,-1055,-1050,-1045,-1040,-1036, - -1031,-1026,-1022,-1017,-1012,-1007,-1003,-998,-994,-989,-984,-980,-975,-970,-966, - -961,-957,-952,-948,-943,-938,-934,-929,-925,-920,-916,-911,-907,-902,-898,-894, - -889,-885,-880,-876,-872,-867,-863,-858,-854,-850,-845,-841,-837,-833,-828,-824, - -820,-816,-811,-807,-803,-799,-795,-790,-786,-782,-778,-774,-770,-766,-762,-757, - -753,-749,-745,-741,-737,-733,-729,-725,-721,-717,-714,-710,-706,-702,-698,-694, - -690,-686,-683,-679,-675,-671,-667,-664,-660,-656,-652,-649,-645,-641,-638,-634, - -630,-627,-623,-620,-616,-612,-609,-605,-602,-598,-595,-591,-588,-584,-581,-577, - -574,-571,-567,-564,-560,-557,-554,-550,-547,-544,-540,-537,-534,-530,-527,-524, - -521,-518,-514,-511,-508,-505,-502,-499,-495,-492,-489,-486,-483,-480,-477,-474, - -471,-468,-465,-462,-459,-456,-453,-450,-447,-444,-441,-438,-435,-433,-430,-427, - -424,-421,-418,-416,-413,-410,-407,-405,-402,-399,-396,-394,-391,-388,-386,-383, - -380,-378,-375,-373,-370,-367,-365,-362,-360,-357,-355,-352,-350,-347,-345,-342, - -340,-337,-335,-333,-330,-328,-325,-323,-321,-318,-316,-314,-311,-309,-307,-305, - -302,-300,-298,-296,-293,-291,-289,-287,-285,-282,-280,-278,-276,-274,-272,-270, - -268,-266,-264,-261,-259,-257,-255,-253,-251,-249,-247,-246,-244,-242,-240,-238, - -236,-234,-232,-230,-228,-227,-225,-223,-221,-219,-217,-216,-214,-212,-210,-209, - -207,-205,-203,-202,-200,-198,-197,-195,-193,-192,-190,-188,-187,-185,-184,-182, - -180,-179,-177,-176,-174,-173,-171,-170,-168,-167,-165,-164,-162,-161,-159,-158, - -156,-155,-154,-152,-151,-149,-148,-147,-145,-144,-143,-141,-140,-139,-137,-136, - -135,-133,-132,-131,-130,-128,-127,-126,-125,-123,-122,-121,-120,-119,-117,-116, - -115,-114,-113,-112,-111,-109,-108,-107,-106,-105,-104,-103,-102,-101,-100,-99,-98, - -97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78, - -78,-77,-76,-75,-74,-73,-72,-71,-71,-70,-69,-68,-67,-67,-66,-65,-64,-63,-63,-62, - -61,-60,-60,-59,-58,-58,-57,-56,-55,-55,-54,-53,-53,-52,-51,-51,-50,-49,-49,-48, - -47,-47,-46,-45,-45,-44,-44,-43,-42,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36, - -36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-28,-27,-27, - -26,-26,-25,-25,-25,-24,-24,-23,-23,-23,-22,-22,-21,-21,-21,-20,-20,-20,-19,-19, - -19,-18,-18,-17,-17,-17,-17,-16,-16,-16,-15,-15,-15,-14,-14,-14,-13,-13,-13,-13, - -12,-12,-12,-12,-11,-11,-11,-11,-10,-10,-10,-10,-9,-9,-9,-9,-9,-8,-8,-8,-8,-7,-7, - -7,-7,-7,-7,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,-4,-4,-3,-3,-3,-3, - -3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + cepstrum[i] *= 2; + cepstrum[i + cepstrum.size() / 2] *= 0; + } + + // Now cancel the previous steps: fft -> exp -> ifft -> real + fft.FFT(cepstrum); + for(auto &v : cepstrum) + v = std::exp(v); + fft.IFFT(cepstrum); + fft.Normalize(cepstrum); + for(size_t i = 0; i < table.size(); i++) + table[i] = cepstrum[i].real(); +} + + +class BiquadFilter +{ + double b0, b1, b2, a1, a2, x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0; + + double Filter(double x0) + { + double y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y0; + return y0; + } + +public: + BiquadFilter(double b0_, double b1_, double b2_, double a1_, double a2_) + : b0(b0_), b1(b1_), b2(b2_), a1(a1_), a2(a2_) + { } + + std::vector Run(std::vector table) + { + x1 = 0.0; + x2 = 0.0; + y1 = 0.0; + y2 = 0.0; + + // Initialize filter to stable state + for(int i = 0; i < 10000; i++) + Filter(table[0]); + // Now run the filter + for(auto &v : table) + v = Filter(v); + + return table; } }; +// Observe: a and b are reversed here. To be absolutely clear: +// a is the nominator and b is the denominator. :-/ +BiquadFilter ZTransform(double a0, double a1, double a2, double b0, double b1, double b2, double fc, double fs) +{ + // Prewarp s - domain coefficients + const double wp = 2.0 * fs * std::tan(M_PI * fc / fs); + a2 /= wp * wp; + a1 /= wp; + b2 /= wp * wp; + b1 /= wp; + + // Compute bilinear transform and return it + const double bd = 4 * b2 * fs * fs + 2 * b1 * fs + b0; + return BiquadFilter( + (4 * a2 * fs * fs + 2 * a1 * fs + a0) / bd, + (2 * a0 - 8 * a2 * fs * fs) / bd, + (4 * a2 * fs * fs - 2 * a1 * fs + a0) / bd, + (2 * b0 - 8 * b2 * fs * fs) / bd, + (4 * b2 * fs * fs - 2 * b1 * fs + b0) / bd); +} + + +BiquadFilter MakeRCLowpass(double sampleRate, double freq) +{ + const double omega = 2 * M_PI * freq / sampleRate; + const double term = 1 + 1 / omega; + return BiquadFilter(1 / term, 0.0, 0.0, -1.0 + 1.0 / term, 0.0); +} + + +BiquadFilter MakeButterworth(double fs, double fc, double res_dB = 0) +{ + // 2nd-order Butterworth s-domain coefficients are: + // + // b0 = 1.0 b1 = 0 b2 = 0 + // a0 = 1 a1 = sqrt(2) a2 = 1 + // + // by tweaking the a1 parameter, some resonance can be produced. + + const double res = std::pow(10.0, (-res_dB / 10.0 / 2.0)); + return ZTransform(1, 0, 0, 1, std::sqrt(2) * res, 1, fc, fs); +} + + +MPT_NOINLINE void Integrate(std::vector &table) +{ + const double total = std::accumulate(table.begin(), table.end(), 0.0); + double startVal = -total; + + for(auto &v : table) + { + startVal += v; + v = startVal; + } +} + + +MPT_NOINLINE void Quantize(const std::vector &in, Paula::BlepArray &quantized) +{ + MPT_ASSERT(in.size() == Paula::BLEP_SIZE); + constexpr int fact = 1 << Paula::BLEP_SCALE; + const double cv = fact / (in.back() - in.front()); + + for(int i = 0; i < Paula::BLEP_SIZE; i++) + { + double val = in[i] * cv; +#ifdef MPT_INTMIXER + val = mpt::round(val); +#endif + quantized[i] = static_cast(-val); + } +} + +} // namespace + +void BlepTables::InitTables() +{ + constexpr double sampleRate = Paula::PAULA_HZ; + + // Because Amiga only has 84 dB SNR, the noise floor is low enough with -90 dB. + // A500 model uses slightly lower-quality kaiser window to obtain slightly + // steeper stopband attenuation. The fixed filters attenuates the sidelobes by + // 12 dB, compensating for the worse performance of the kaiser window. + + // 21 kHz stopband is not fully attenuated by 22 kHz. If the sampling frequency + // is 44.1 kHz, all frequencies above 22 kHz will alias over 20 kHz, thus inaudible. + // The output should be aliasingless for 48 kHz sampling frequency. + auto unfilteredA500 = KaiserFIR(Paula::BLEP_SIZE, 21000.0 / sampleRate * 2.0, 8.0); + auto unfilteredA1200 = KaiserFIR(Paula::BLEP_SIZE, 21000.0 / sampleRate * 2.0, 9.0); + // Move filtering effects to start to allow IIRs more time to settle + constexpr size_t padSize = 8; + constexpr int fftSize = static_cast(mpt::bit_width(size_t(Paula::BLEP_SIZE)) + mpt::bit_width(padSize) - 2); + const TinyFFT fft(fftSize); + FIR_MinPhase(unfilteredA500, fft); + FIR_MinPhase(unfilteredA1200, fft); + + // Make digital models for the filters on Amiga 500 and 1200. + auto filterFixed5kHz = MakeRCLowpass(sampleRate, 4900.0); + // The leakage filter seems to reduce treble in both models a bit + // The A500 filter seems to be well modelled only with a 4.9 kHz + // filter although the component values would suggest 5 kHz filter. + auto filterLeakage = MakeRCLowpass(sampleRate, 32000.0); + auto filterLED = MakeButterworth(sampleRate, 3275.0, -0.70); + + // Apply fixed filter to A500 + auto amiga500Off = filterFixed5kHz.Run(unfilteredA500); + // Produce the filtered outputs + auto amiga1200Off = filterLeakage.Run(unfilteredA1200); + + // Produce LED filters + auto amiga500On = filterLED.Run(amiga500Off); + auto amiga1200On = filterLED.Run(amiga1200Off); + + // Integrate to produce blep + Integrate(amiga500Off); + Integrate(amiga500On); + Integrate(amiga1200Off); + Integrate(amiga1200On); + Integrate(unfilteredA1200); + + // Quantize and scale + Quantize(amiga500Off, WinSincIntegral[A500Off]); + Quantize(amiga500On, WinSincIntegral[A500On]); + Quantize(amiga1200Off, WinSincIntegral[A1200Off]); + Quantize(amiga1200On, WinSincIntegral[A1200On]); + Quantize(unfilteredA1200, WinSincIntegral[Unfiltered]); +} + + +const Paula::BlepArray &BlepTables::GetAmigaTable(Resampling::AmigaFilter amigaType, bool enableFilter) const +{ + if(amigaType == Resampling::AmigaFilter::A500) + return enableFilter ? WinSincIntegral[A500On] : WinSincIntegral[A500Off]; + if(amigaType == Resampling::AmigaFilter::A1200) + return enableFilter ? WinSincIntegral[A1200On] : WinSincIntegral[A1200Off]; + return WinSincIntegral[Unfiltered]; +} + + // we do not initialize blepState here // cppcheck-suppress uninitMemberVar State::State(uint32 sampleRate) @@ -278,8 +269,6 @@ State::State(uint32 sampleRate) numSteps = static_cast(amigaClocksPerSample / MINIMUM_INTERVAL); stepRemainder = SamplePosition::FromDouble(amigaClocksPerSample - numSteps * MINIMUM_INTERVAL); remainder = SamplePosition(0); - activeBleps = 0; - globalOutputLevel = 0; } @@ -287,6 +276,7 @@ void State::Reset() { remainder = SamplePosition(0); activeBleps = 0; + firstBlep = MAX_BLEPS / 2u; globalOutputLevel = 0; } @@ -295,28 +285,26 @@ void State::InputSample(int16 sample) { if(sample != globalOutputLevel) { - LimitMax(activeBleps, static_cast(mpt::size(blepState) - 1)); - - // Make room for new blep in the sorted list - std::move_backward(blepState, blepState + activeBleps, blepState + activeBleps + 1); - // Start a new blep: level is the difference, age (or phase) is 0 clocks. - activeBleps++; - blepState[0].age = 0; - blepState[0].level = sample - globalOutputLevel; + firstBlep = (firstBlep - 1u) % MAX_BLEPS; + if(activeBleps < std::size(blepState)) + activeBleps++; + blepState[firstBlep].age = 0; + blepState[firstBlep].level = sample - globalOutputLevel; globalOutputLevel = sample; } } // Return output simulated as series of bleps -int State::OutputSample(bool filter) +int State::OutputSample(const BlepArray &WinSincIntegral) { int output = globalOutputLevel * (1 << Paula::BLEP_SCALE); - for(uint16 i = 0; i < activeBleps; i++) + uint32 lastBlep = firstBlep + activeBleps; + for(uint32 i = firstBlep; i != lastBlep; i++) { - const auto &blep = blepState[i]; - output -= WinSincIntegral[filter][blep.age] * blep.level; + const auto &blep = blepState[i % MAX_BLEPS]; + output -= WinSincIntegral[blep.age] * blep.level; } output /= (1 << (Paula::BLEP_SCALE - 2)); // - 2 to compensate for the fact that we reduced the input sample bit depth @@ -327,12 +315,14 @@ int State::OutputSample(bool filter) // Advance the simulation by given number of clock ticks void State::Clock(int cycles) { - for(uint16 i = 0; i < activeBleps; i++) + uint32 lastBlep = firstBlep + activeBleps; + for(uint32 i = firstBlep; i != lastBlep; i++) { - blepState[i].age += static_cast(cycles); - if(blepState[i].age >= mpt::size(WinSincIntegral[0])) + auto &blep = blepState[i % MAX_BLEPS]; + blep.age += static_cast(cycles); + if(blep.age >= Paula::BLEP_SIZE) { - activeBleps = i; + activeBleps = static_cast(i - firstBlep); return; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h index 5de16a192..d790ee1bf 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h @@ -13,20 +13,51 @@ #include "BuildSettings.h" #include "Snd_defs.h" +#include "Mixer.h" OPENMPT_NAMESPACE_BEGIN namespace Paula { -const int PAULA_HZ = 3546895; -const int MINIMUM_INTERVAL = 16; -const int BLEP_SCALE = 17; -const int BLEP_SIZE = 2048; -const int MAX_BLEPS = (BLEP_SIZE / MINIMUM_INTERVAL); +constexpr int PAULA_HZ = 3546895; +constexpr int MINIMUM_INTERVAL = 4; // Tradeoff between quality and speed (lower = less aliasing) +constexpr int BLEP_SCALE = 17; // TODO: Should be 1 for float mixer +constexpr int BLEP_SIZE = 2048; + +using BlepArray = std::array; + + +class BlepTables +{ + enum AmigaFilter + { + A500Off = 0, + A500On, + A1200Off, + A1200On, + Unfiltered, + NumFilterTypes + }; + + std::array WinSincIntegral; + +public: + void InitTables(); + const Paula::BlepArray &GetAmigaTable(Resampling::AmigaFilter amigaType, bool enableFilter) const; +}; + class State { + // MAX_BLEPS configures how many BLEPs (volume steps) are being kept track of per channel, + // and thus directly influences how much memory this feature wastes per virtual channel. + // Paula::BLEP_SIZE / Paula::MINIMUM_INTERVAL would be a safe maximum, + // but even a sample (alternating at +1/-1, thus causing a step on every frame) played at 200 kHz, + // which is way out of spec for the Amiga, will only get close to 128 active BLEPs with Paula::MINIMUM_INTERVAL == 4. + // Hence 128 is chosen as a tradeoff between quality and memory consumption. + static constexpr uint16 MAX_BLEPS = 128; + struct Blep { int16 level; @@ -35,10 +66,10 @@ class State public: SamplePosition remainder, stepRemainder; - int numSteps; // Number of full-length steps + int numSteps; // Number of full-length steps private: - uint16 activeBleps; // Count of simultaneous bleps to keep track of - int16 globalOutputLevel; // The instantenous value of Paula output + uint16 activeBleps = 0, firstBlep = 0; // Count of simultaneous bleps to keep track of + int16 globalOutputLevel = 0; // The instantenous value of Paula output Blep blepState[MAX_BLEPS]; public: @@ -46,7 +77,7 @@ public: void Reset(); void InputSample(int16 sample); - int OutputSample(bool filter); + int OutputSample(const BlepArray &WinSincIntegral); void Clock(int cycles); }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h index 5c4b290b1..65d9a686c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h @@ -15,6 +15,7 @@ #include "WindowedFIR.h" #include "Mixer.h" #include "MixerSettings.h" +#include "Paula.h" OPENMPT_NAMESPACE_BEGIN @@ -50,24 +51,18 @@ typedef mixsample_t SINC_TYPE; #endif // MPT_INTMIXER #define SINC_MASK (SINC_PHASES-1) -STATIC_ASSERT((SINC_MASK & 0xffff) == SINC_MASK); // exceeding fractional freq +static_assert((SINC_MASK & 0xffff) == SINC_MASK); // exceeding fractional freq class CResamplerSettings { public: - ResamplingMode SrcMode; - double gdWFIRCutoff; - uint8 gbWFIRType; - bool emulateAmiga; + ResamplingMode SrcMode = Resampling::Default(); + double gdWFIRCutoff = 0.97; + uint8 gbWFIRType = WFIR_KAISER4T; + Resampling::AmigaFilter emulateAmiga = Resampling::AmigaFilter::Off; public: - MPT_CONSTEXPR11_FUN CResamplerSettings() - : SrcMode(Resampling::Default()) - , gdWFIRCutoff(0.97) - , gbWFIRType(WFIR_KAISER4T) - , emulateAmiga(false) - { - } + constexpr CResamplerSettings() = default; bool operator == (const CResamplerSettings &cmp) const { return SrcMode == cmp.SrcMode && gdWFIRCutoff == cmp.gdWFIRCutoff && gbWFIRType == cmp.gbWFIRType && emulateAmiga == cmp.emulateAmiga; @@ -94,6 +89,7 @@ public: RESAMPLER_TABLE SINC_TYPE gKaiserSinc[SINC_PHASES * 8]; // Upsampling RESAMPLER_TABLE SINC_TYPE gDownsample13x[SINC_PHASES * 8]; // Downsample 1.333x RESAMPLER_TABLE SINC_TYPE gDownsample2x[SINC_PHASES * 8]; // Downsample 2x + RESAMPLER_TABLE Paula::BlepTables blepTables; // Amiga BLEP resampler #ifndef MPT_INTMIXER RESAMPLER_TABLE mixsample_t FastSincTablef[256 * 4]; // Cubic spline LUT @@ -127,6 +123,7 @@ public: { InitializeTablesFromScratch(false); } + 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 c670b5d7f..d3c810798 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp @@ -35,10 +35,9 @@ RowVisitor::RowVisitor(const CSoundFile &sf, SEQUENCEINDEX sequence) } -RowVisitor& RowVisitor::operator=(RowVisitor &&other) +void RowVisitor::MoveVisitedRowsFrom(RowVisitor &other) { m_visitedRows = std::move(other.m_visitedRows); - return *this; } @@ -254,4 +253,13 @@ void RowVisitor::AddVisitedRow(ORDERINDEX ord, ROWINDEX row) } +// Returns the last visited row index of the current pattern, or ROWINDEX_INVALID if there is none. +ROWINDEX RowVisitor::GetLastVisitedRow() const +{ + if(m_visitOrder.empty()) + return ROWINDEX_INVALID; + return m_visitOrder.back(); +} + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h index 6a72c948b..f8df2c9dd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h @@ -34,7 +34,8 @@ protected: public: RowVisitor(const CSoundFile &sf, SEQUENCEINDEX sequence = SEQUENCEINDEX_INVALID); - RowVisitor& operator=(RowVisitor &&other); + + void MoveVisitedRowsFrom(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). @@ -69,6 +70,9 @@ public: // Set all rows of a previous pattern loop as unvisited. void ResetPatternLoop(ORDERINDEX ord, ROWINDEX startRow); + // Returns the last visited row index of the current pattern, or ROWINDEX_INVALID if there is none. + ROWINDEX GetLastVisitedRow() const; + protected: // (Un)sets a given row as visited. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp index e69f5723b..d7305412d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp @@ -20,7 +20,7 @@ OPENMPT_NAMESPACE_BEGIN void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(MOD_TYPE_S3M); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); if(sampleType == typePCM || sampleType == typeNone) { @@ -28,8 +28,8 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const if(sampleType == typePCM) { mptSmp.nLength = length; - mptSmp.nLoopStart = MIN(loopStart, mptSmp.nLength - 1); - mptSmp.nLoopEnd = MIN(loopEnd, mptSmp.nLength); + mptSmp.nLoopStart = std::min(static_cast(loopStart), mptSmp.nLength - 1); + mptSmp.nLoopEnd = std::min(static_cast(loopEnd), mptSmp.nLength); mptSmp.uFlags.set(CHN_LOOP, (flags & smpLoop) != 0); } @@ -48,7 +48,7 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const } // Volume / Panning - mptSmp.nVolume = std::min(defaultVolume, 64) * 4; + mptSmp.nVolume = std::min(static_cast(defaultVolume), uint8(64)) * 4; // C-5 frequency mptSmp.nC5Speed = c5speed; @@ -67,7 +67,7 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp) { SmpLength smpLength = 0; - mpt::String::Write(filename, mptSmp.filename); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, filename) = mptSmp.filename; memcpy(magic, "SCRS", 4); if(mptSmp.uFlags[CHN_ADLIB]) @@ -100,7 +100,7 @@ SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp) sampleType = typeNone; } - defaultVolume = static_cast(MIN(mptSmp.nVolume / 4, 64)); + defaultVolume = static_cast(std::min(static_cast(mptSmp.nVolume / 4), uint16(64))); if(mptSmp.nC5Speed != 0) { c5speed = mptSmp.nC5Speed; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h index c27e70797..a78ad2281 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h @@ -24,70 +24,71 @@ struct S3MFileHeader // Magic Bytes enum S3MMagic { - idEOF = 0x1A, - idS3MType = 0x10, - idPanning = 0xFC, + idEOF = 0x1A, + idS3MType = 0x10, + idPanning = 0xFC, }; // Tracker Versions in the cwtv field enum S3MTrackerVersions { - trackerMask = 0xF000, - versionMask = 0x0FFF, + trackerMask = 0xF000, + versionMask = 0x0FFF, - trkScreamTracker = 0x1000, - trkImagoOrpheus = 0x2000, - trkImpulseTracker = 0x3000, - trkSchismTracker = 0x4000, - trkOpenMPT = 0x5000, - trkBeRoTracker = 0x6000, - trkCreamTracker = 0x7000, + trkScreamTracker = 0x1000, + trkImagoOrpheus = 0x2000, + trkImpulseTracker = 0x3000, + trkSchismTracker = 0x4000, + trkOpenMPT = 0x5000, + trkBeRoTracker = 0x6000, + trkCreamTracker = 0x7000, - trkST3_20 = 0x1320, - trkIT2_07 = 0x3207, - trkIT2_14 = 0x3214, - trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012 - trkCamoto = 0xCA00, + trkST3_00 = 0x1300, + trkST3_20 = 0x1320, + trkIT2_07 = 0x3207, + trkIT2_14 = 0x3214, + trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012 + trkCamoto = 0xCA00, }; // Flags enum S3MHeaderFlags { - st2Vibrato = 0x01, // Vibrato is twice as deep. Cannot be enabled from UI. - zeroVolOptim = 0x08, // Volume 0 optimisations - amigaLimits = 0x10, // Enforce Amiga limits - fastVolumeSlides = 0x40, // Fast volume slides (like in ST3.00) + st2Vibrato = 0x01, // Vibrato is twice as deep. Cannot be enabled from UI. + zeroVolOptim = 0x08, // Volume 0 optimisations + amigaLimits = 0x10, // Enforce Amiga limits + fastVolumeSlides = 0x40, // Fast volume slides (like in ST3.00) }; // S3M Format Versions enum S3MFormatVersion { - oldVersion = 0x01, // Old Version, signed samples - newVersion = 0x02, // New Version, unsigned samples + oldVersion = 0x01, // Old Version, signed samples + newVersion = 0x02, // New Version, unsigned samples }; - char name[28]; // Song Title - uint8le dosEof; // Supposed to be 0x1A, but even ST3 seems to ignore this sometimes (see STRSHINE.S3M by Purple Motion) - uint8le fileType; // File Type, 0x10 = ST3 module - char reserved1[2]; // Reserved - uint16le ordNum; // Number of order items - uint16le smpNum; // Number of sample parapointers - uint16le patNum; // Number of pattern parapointers - uint16le flags; // Flags, see S3MHeaderFlags - uint16le cwtv; // "Made With" Tracker ID, see S3MTrackerVersions - uint16le formatVersion; // Format Version, see S3MFormatVersion - char magic[4]; // "SCRM" magic bytes - uint8le globalVol; // Default Global Volume (0...64) - uint8le speed; // Default Speed (1...254) - uint8le tempo; // Default Tempo (33...255) - uint8le masterVolume; // Sample Volume (0...127, stereo if high bit is set) - uint8le ultraClicks; // Number of channels used for ultra click removal - uint8le usePanningTable; // 0xFC => read extended panning table - uint16le reserved2; // Schism Tracker uses this for its extended version information - uint32le reserved3; // Impulse Tracker hides its edit timer here + char name[28]; // Song Title + uint8le dosEof; // Supposed to be 0x1A, but even ST3 seems to ignore this sometimes (see STRSHINE.S3M by Purple Motion) + uint8le fileType; // File Type, 0x10 = ST3 module + char reserved1[2]; // Reserved + uint16le ordNum; // Number of order items + uint16le smpNum; // Number of sample parapointers + uint16le patNum; // Number of pattern parapointers + uint16le flags; // Flags, see S3MHeaderFlags + uint16le cwtv; // "Made With" Tracker ID, see S3MTrackerVersions + uint16le formatVersion; // Format Version, see S3MFormatVersion + char magic[4]; // "SCRM" magic bytes + uint8le globalVol; // Default Global Volume (0...64) + uint8le speed; // Default Speed (1...254) + uint8le tempo; // Default Tempo (33...255) + uint8le masterVolume; // Sample Volume (0...127, stereo if high bit is set) + uint8le ultraClicks; // Number of channels used for ultra click removal + uint8le usePanningTable; // 0xFC => read extended panning table + uint16le reserved2; // Schism Tracker uses this for its extended version information + uint32le reserved3; // Impulse Tracker hides its edit timer here uint16le reserved4; - uint16le special; // Pointer to special custom data (unused) - uint8le channels[32]; // Channel setup + uint16le special; // Pointer to special custom data (unused) + uint8le channels[32]; // Channel setup }; MPT_BINARY_STRUCT(S3MFileHeader, 96) @@ -98,39 +99,39 @@ struct S3MSampleHeader { enum SampleType { - typeNone = 0, - typePCM = 1, - typeAdMel = 2, + typeNone = 0, + typePCM = 1, + typeAdMel = 2, }; enum SampleFlags { - smpLoop = 0x01, - smpStereo = 0x02, - smp16Bit = 0x04, + smpLoop = 0x01, + smpStereo = 0x02, + smp16Bit = 0x04, }; enum SamplePacking { - pUnpacked = 0x00, // PCM - pDP30ADPCM = 0x01, // Unused packing type - pADPCM = 0x04, // MODPlugin ADPCM :( + pUnpacked = 0x00, // PCM + pDP30ADPCM = 0x01, // Unused packing type + pADPCM = 0x04, // MODPlugin ADPCM :( }; - uint8le sampleType; // Sample type, see SampleType - char filename[12]; // Sample filename - uint8le dataPointer[3]; // Pointer to sample data (divided by 16) - uint32le length; // Sample length, in samples - uint32le loopStart; // Loop start, in samples - uint32le loopEnd; // Loop end, in samples - uint8le defaultVolume; // Default volume (0...64) - char reserved1; // Reserved - uint8le pack; // Packing algorithm, SamplePacking - uint8le flags; // Sample flags - uint32le c5speed; // Middle-C frequency - char reserved2[12]; // Reserved + Internal ST3 stuff - char name[28]; // Sample name - char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments) + uint8le sampleType; // Sample type, see SampleType + char filename[12]; // Sample filename + uint8le dataPointer[3]; // Pointer to sample data (divided by 16) + uint32le length; // Sample length, in samples + uint32le loopStart; // Loop start, in samples + uint32le loopEnd; // Loop end, in samples + uint8le defaultVolume; // Default volume (0...64) + char reserved1; // Reserved + uint8le pack; // Packing algorithm, SamplePacking + uint8le flags; // Sample flags + uint32le c5speed; // Middle-C frequency + char reserved2[12]; // Reserved + Internal ST3 stuff + char name[28]; // Sample name + char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments) // Convert an S3M sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatBRR.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatBRR.cpp new file mode 100644 index 000000000..c7bc9fc91 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatBRR.cpp @@ -0,0 +1,134 @@ +/* + * SampleFormatBRR.cpp + * ------------------- + * Purpose: BRR (SNES Bit Rate Reduction) sample format import. + * Notes : This format has no magic bytes, so frame headers are thoroughly validated. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Sndfile.h" +#include "../common/FileReader.h" + + +OPENMPT_NAMESPACE_BEGIN + + +static void ProcessBRRSample(int32 sample, int16 *output, uint8 range, uint8 filter) +{ + if(sample >= 8) + sample -= 16; + + if(range <= 12) + sample = mpt::rshift_signed(mpt::lshift_signed(sample, range), 1); + else + sample = (sample < 0) ? -2048 : 0; // Implementations do not fully agree on what to do in this case. This is what bsnes does. + + // Apply prediction filter + // Note 1: It is okay that we may access data before the first sample point because this memory is reserved for interpolation + // Note 2: The use of signed shift arithmetic is crucial for some samples (e.g. killer lead.brr, Mac2.brr) + // Note 3: Divisors are twice of what is written in the respective comments, as all sample data is divided by 2 (again crucial for accuracy) + static_assert(InterpolationMaxLookahead >= 2); + switch(filter) + { + case 1: // y(n) = x(n) + x(n-1) * 15/16 + sample += mpt::rshift_signed(output[-1] * 15, 5); + break; + case 2: // y(n) = x(n) + x(n-1) * 61/32 - x(n-2) * 15/16 + sample += mpt::rshift_signed(output[-1] * 61, 6) + mpt::rshift_signed(output[-2] * -15, 5); + break; + case 3: // y(n) = x(n) + x(n-1) * 115/64 - x(n-2) * 13/16 + sample += mpt::rshift_signed(output[-1] * 115, 7) + mpt::rshift_signed(output[-2] * -13, 5); + break; + } + + sample = std::clamp(sample, int32(-32768), int32(32767)) * 2; + if(sample > 32767) + sample -= 65536; + else if(sample < -32768) + sample += 65536; + output[0] = static_cast(sample); +} + + +bool CSoundFile::ReadBRRSample(SAMPLEINDEX sample, FileReader &file) +{ + const auto fileSize = file.GetLength(); + if(fileSize < 9 || fileSize > uint16_max) + return false; + const bool hasLoopInfo = (fileSize % 9) == 2; + if((fileSize % 9) != 0 && !hasLoopInfo) + return false; + + file.Rewind(); + + SmpLength loopStart = 0; + if(hasLoopInfo) + { + loopStart = file.ReadUint16LE(); + if(loopStart >= fileSize) + return false; + if((loopStart % 9) != 0) + return false; + } + + // First scan the file for validity and consistency + // Note: There are some files with loop start set but ultimately the loop is never enabled. Cannot use this as a consistency check. + // Very few files also have a filter set on the first block, so we cannot reject those either. + bool enableLoop = false, first = true; + while(!file.EndOfFile()) + { + const auto block = file.ReadArray(); + const bool isLast = (block[0] & 0x01) != 0; + const bool isLoop = (block[0] & 0x02) != 0; + const uint8 range = block[0] >> 4u; + if(isLast != file.EndOfFile()) + return false; + if(!first && enableLoop != isLoop) + return false; + // While a range of 13 is technically invalid as well, it can be found in the wild. + if(range > 13) + return false; + enableLoop = isLoop; + first = false; + } + + file.Seek(hasLoopInfo ? 2 : 0); + + DestroySampleThreadsafe(sample); + ModSample &mptSmp = Samples[sample]; + mptSmp.Initialize(); + mptSmp.uFlags = CHN_16BIT; + mptSmp.nLength = mpt::saturate_cast((fileSize - (hasLoopInfo ? 2 : 0)) * 16 / 9); + if(enableLoop) + mptSmp.SetLoop(loopStart * 16 / 9, mptSmp.nLength, true, false, *this); + mptSmp.nC5Speed = 32000; + m_szNames[sample] = ""; + + if(!mptSmp.AllocateSample()) + return false; + + int16 *output = mptSmp.sample16(); + while(!file.EndOfFile()) + { + const auto block = file.ReadArray(); + const uint8 range = block[0] >> 4u; + const uint8 filter = (block[0] >> 2) & 0x03; + + for(int i = 0; i < 8; i++) + { + ProcessBRRSample(block[i + 1] >> 4u, output, range, filter); + ProcessBRRSample(block[i + 1] & 0x0F, output + 1, range, filter); + output += 2; + } + } + mptSmp.Convert(MOD_TYPE_IT, GetType()); + mptSmp.PrecomputeLoops(*this, false); + + return true; +} + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp index 63de71cd3..24e8c4c2e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp @@ -28,12 +28,26 @@ //#include "../common/mptCRC.h" #include "OggStream.h" #ifdef MPT_WITH_OGG +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #ifdef MPT_WITH_FLAC +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include #include #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_FLAC @@ -167,7 +181,7 @@ struct FLACDecoder { // Init sample information client.sndFile.DestroySampleThreadsafe(client.sample); - strcpy(client.sndFile.m_szNames[client.sample], ""); + client.sndFile.m_szNames[client.sample] = ""; sample.Initialize(); sample.uFlags.set(CHN_16BIT, metadata->data.stream_info.bits_per_sample > 8); sample.uFlags.set(CHN_STEREO, metadata->data.stream_info.channels > 1); @@ -195,9 +209,7 @@ struct FLACDecoder const FLAC__uint32 length = metadata->data.vorbis_comment.comments[i].length; if(length > 6 && !mpt::CompareNoCaseAscii(tag, "TITLE=", 6)) { - mpt::ustring sampleName; - mpt::String::Read(sampleName, mpt::CharsetUTF8, tag + 6, length - 6); - mpt::String::Copy(client.sndFile.m_szNames[client.sample], mpt::ToCharset(client.sndFile.GetCharsetInternal(), sampleName)); + client.sndFile.m_szNames[client.sample] = mpt::ToCharset(client.sndFile.GetCharsetInternal(), mpt::Charset::UTF8, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, tag + 6, length - 6)); } else if(length > 11 && !mpt::CompareNoCaseAscii(tag, "SAMPLERATE=", 11)) { uint32 sampleRate = ConvertStrTo(tag + 11); @@ -246,7 +258,7 @@ bool CSoundFile::ReadFLACSample(SAMPLEINDEX sample, FileReader &file) file.Rewind(); bool oggOK = false; bool needMoreData = true; - static const long bufsize = 65536; + constexpr long bufsize = 65536; std::size_t readSize = 0; char *buf = nullptr; ogg_sync_state oy; @@ -539,13 +551,14 @@ public: bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const { #ifdef MPT_WITH_FLAC + const ModSample &sample = Samples[nSample]; + if(sample.uFlags[CHN_ADLIB]) + return false; + FLAC__StreamEncoder_RAII encoder(f); if(encoder == nullptr) - { return false; - } - const ModSample &sample = Samples[nSample]; uint32 sampleRate = sample.GetSampleRate(GetType()); // First off, set up all the metadata... @@ -562,9 +575,9 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const { // Store sample name FLAC__StreamMetadata_VorbisComment_Entry entry; - 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_entry_from_name_value_pair(&entry, "TITLE", mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[nSample]).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); - FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ENCODER", mpt::ToCharset(mpt::CharsetUTF8, Version::Current().GetOpenMPTVersionString()).c_str()); + FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ENCODER", mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); if(sampleRate > FLAC__MAX_SAMPLE_RATE) { @@ -679,7 +692,7 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const FLAC__int32 buffer[mpt::IO::BUFFERSIZE_TINY]; while(framesRemain && success) { - const SmpLength copyFrames = std::min(framesRemain, mpt::saturate_cast(mpt::size(buffer) / numChannels)); + const SmpLength copyFrames = std::min(framesRemain, mpt::saturate_cast(std::size(buffer) / numChannels)); // First, convert to a 32-bit integer buffer switch(sample.GetElementarySampleSize()) @@ -690,7 +703,7 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const } // Now do the actual encoding - success = FLAC__stream_encoder_process_interleaved(encoder, buffer, copyFrames) != false; + success = FLAC__stream_encoder_process_interleaved(encoder, buffer, copyFrames) != static_cast(false); framesRead += copyFrames; framesRemain -= copyFrames; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp index f43743d8e..b33e3f8e6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp @@ -56,11 +56,7 @@ typedef size_t mpg123_size_t; typedef ssize_t mpg123_ssize_t; class ComponentMPG123 -#if defined(MPT_ENABLE_MPG123_DELAYLOAD) - : public ComponentBundledDLL -#else : public ComponentBuiltin -#endif { MPT_DECLARE_COMPONENT_MEMBERS @@ -90,22 +86,12 @@ public: public: ComponentMPG123() -#if defined(MPT_ENABLE_MPG123_DELAYLOAD) - : ComponentBundledDLL(P_("openmpt-mpg123")) -#else : ComponentBuiltin() -#endif { return; } bool DoInitialize() override { -#if defined(MPT_ENABLE_MPG123_DELAYLOAD) - if(!ComponentBundledDLL::DoInitialize()) - { - return false; - } -#endif if(mpg123_init() != 0) { return false; @@ -133,7 +119,7 @@ static mpt::ustring ReadMPG123String(const mpg123_string &str) { return result; } - result = mpt::ToUnicode(mpt::CharsetUTF8, std::string(str.p, str.p + str.fill - 1)); + result = mpt::ToUnicode(mpt::Charset::UTF8, std::string(str.p, str.p + str.fill - 1)); return result; } @@ -151,7 +137,7 @@ static mpt::ustring ReadMPG123String(const mpg123_string *str) template static mpt::ustring ReadMPG123String(const char (&str)[N]) { - return mpt::ToUnicode(mpt::CharsetISO8859_1, mpt::String::ReadBuf(mpt::String::spacePadded, str)); + return mpt::ToUnicode(mpt::Charset::ISO8859_1, mpt::String::ReadBuf(mpt::String::spacePadded, str)); } #endif // MPT_WITH_MPG123 @@ -500,7 +486,7 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b } } - std::vector buf_bytes; + std::vector buf_bytes; std::vector buf_samples; bool decode_error = false; bool decode_done = false; @@ -564,7 +550,7 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b DestroySampleThreadsafe(sample); if(!mo3Decode) { - mpt::String::Copy(m_szNames[sample], mpt::ToCharset(GetCharsetInternal(), sampleName)); + m_szNames[sample] = mpt::ToCharset(GetCharsetInternal(), sampleName); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; } @@ -623,8 +609,8 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b 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)); + stream_pos += std::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); + bytes_left -= std::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); if(frame_samples > 0) { try @@ -656,7 +642,7 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b DestroySampleThreadsafe(sample); if(!mo3Decode) { - strcpy(m_szNames[sample], ""); + m_szNames[sample] = ""; Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp index b40a13248..8cc9c3d2a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp @@ -263,7 +263,7 @@ std::vector CSoundFile::GetMediaFoundationFileTypes() guidMap[guid].AddExtension(mpt::PathString::FromWide(handlerType.substr(1))); } else { - guidMap[guid].AddMimeType(mpt::ToCharset(mpt::CharsetASCII, handlerType)); + guidMap[guid].AddMimeType(mpt::ToCharset(mpt::Charset::ASCII, handlerType)); } } @@ -408,7 +408,7 @@ bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, DestroySampleThreadsafe(sample); if(!mo3Decode) { - mpt::String::Copy(m_szNames[sample], sampleName); + m_szNames[sample] = sampleName; Samples[sample].Initialize(); Samples[sample].nC5Speed = samplesPerSecond; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp index 83c97a73a..9918a64c6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp @@ -24,11 +24,18 @@ //#include "../common/mptCRC.h" #include "OggStream.h" #ifdef MPT_WITH_OGG +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #if defined(MPT_WITH_OPUSFILE) #include -#endif +#endif // MPT_WITH_OPUSFILE OPENMPT_NAMESPACE_BEGIN @@ -41,7 +48,7 @@ OPENMPT_NAMESPACE_BEGIN static mpt::ustring UStringFromOpus(const char *str) { - return str ? mpt::ToUnicode(mpt::CharsetUTF8, str) : mpt::ustring(); + return str ? mpt::ToUnicode(mpt::Charset::UTF8, str) : mpt::ustring(); } static FileTags GetOpusFileTags(OggOpusFile *of) @@ -156,7 +163,7 @@ bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) } DestroySampleThreadsafe(sample); - mpt::String::Copy(m_szNames[sample], sampleName); + m_szNames[sample] = sampleName; Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = mpt::saturate_cast(raw_sample_data.size() / channels); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp new file mode 100644 index 000000000..b0f870129 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp @@ -0,0 +1,1252 @@ +/* + * SampleFormatSFZ.cpp + * ------------------- + * Purpose: Loading and saving SFZ instruments. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Sndfile.h" +#ifdef MODPLUG_TRACKER +#include "../mptrack/TrackerSettings.h" +#endif // MODPLUG_TRACKER +#ifndef MODPLUG_NO_FILESAVE +#include "../common/mptFileIO.h" +#endif // !MODPLUG_NO_FILESAVE +#include "modsmp_ctrl.h" + +#include + +OPENMPT_NAMESPACE_BEGIN + +#ifdef MPT_EXTERNAL_SAMPLES + +template +static bool SFZStartsWith(const std::string_view &l, const char(&r)[N]) +{ + return l.substr(0, N - 1) == r; +} + +static bool SFZIsNumeric(const std::string_view &str) +{ + return std::find_if(str.begin(), str.end(), [](char c) { return c < '0' || c > '9'; }) == str.end(); +} + +struct SFZControl +{ + std::string defaultPath; + int8 octaveOffset = 0, noteOffset = 0; + + void Parse(const std::string_view key, const std::string &value) + { + if(key == "default_path") + defaultPath = value; + else if(key == "octave_offset") + octaveOffset = ConvertStrTo(value); + else if(key == "note_offset") + noteOffset = ConvertStrTo(value); + } +}; + +struct SFZFlexEG +{ + using PointIndex = decltype(InstrumentEnvelope().nLoopStart); + + std::vector> points; + double amplitude = 0; // percentage (100 = full volume range) + double pan = 0; // percentage (100 = full pan range) + double pitch = 0; // in cents + double cutoff = 0; // in cents + PointIndex sustain = 0; + + void Parse(std::string_view key, const std::string &value) + { + key = key.substr(key.find('_') + 1); + const double v = ConvertStrTo(value); + + const bool isTime = SFZStartsWith(key, "time"), isLevel = SFZStartsWith(key, "level"); + std::string_view pointStr; + if(isTime) + pointStr = key.substr(4); + else if(isLevel) + pointStr = key.substr(5); + + if(!pointStr.empty() && SFZIsNumeric(pointStr)) + { + PointIndex point = ConvertStrTo(std::string(pointStr)); + if(point >= points.size() && point < MAX_ENVPOINTS) + points.resize(point + 1); + + if(point < points.size()) + { + if(isTime) + points[point].first = v; + else + points[point].second = v; + } + return; + } + + if(key == "points") + points.resize(std::min(static_cast(v), static_cast(MAX_ENVPOINTS))); + else if(key == "sustain") + sustain = mpt::saturate_round(v); + else if(key == "amplitude" || key == "ampeg") + amplitude = v; + else if(key == "pan") + pan = v; + else if(key == "pitch") + pitch = v; + else if(key == "cutoff") + cutoff = v; + } + + void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile) const + { + if(amplitude) + ConvertToMPT(ins, sndFile, ENV_VOLUME, amplitude / 100.0, 0.0, 1.0); + if(pan) + ConvertToMPT(ins, sndFile, ENV_PANNING, pan / 100.0, -1.0, 1.0); + if(pitch) + ConvertToMPT(ins, sndFile, ENV_PITCH, pitch / 1600.0, -1.0, 1.0); + if(cutoff) + ConvertToMPT(ins, sndFile, ENV_PITCH, cutoff, 0.0, 1.0, true); + } + + void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType, double scale, double minVal, double maxVal, bool forceFilter = false) const + { + const double tickDuration = sndFile.m_PlayState.m_nSamplesPerTick / static_cast(sndFile.GetSampleRate()); + if(tickDuration <= 0 || points.empty() || scale == 0.0) + return; + + auto &env = ins->GetEnvelope(envType); + std::function conversionFunc = Identity; + if(forceFilter && envType == ENV_PITCH) + { + env.dwFlags.set(ENV_FILTER); + conversionFunc = FilterConversionFunc(*ins, sndFile); + } + + env.clear(); + env.reserve(points.size()); + + const auto ToValue = std::bind(SFZFlexEG::ToValue, std::placeholders::_1, scale, minVal, maxVal, conversionFunc); + + int32 prevTick = -1; + // If the first envelope point's time is greater than 0, we fade in from a neutral value + if(points.front().first > 0) + { + env.push_back({0, ToValue(0.0)}); + prevTick = 0; + } + + for(const auto &point : points) + { + const auto tick = mpt::saturate_cast(prevTick + ToTicks(point.first, tickDuration)); + const auto value = ToValue(point.second); + env.push_back({tick, value}); + prevTick = tick; + if(tick == Util::MaxValueOfType(tick)) + break; + } + + if(sustain < env.size()) + { + env.nSustainStart = env.nSustainEnd = sustain; + env.dwFlags.set(ENV_SUSTAIN); + } else + { + env.dwFlags.reset(ENV_SUSTAIN); + } + env.dwFlags.set(ENV_ENABLED); + + if(envType == ENV_VOLUME && env.nSustainEnd > 0) + env.nReleaseNode = env.nSustainEnd; + } + +protected: + static EnvelopeNode::tick_t ToTicks(double duration, double tickDuration) + { + return std::max(EnvelopeNode::tick_t(1), mpt::saturate_round(duration / tickDuration)); + } + + static EnvelopeNode::value_t ToValue(double value, double scale, double minVal, double maxVal, const std::function &conversionFunc) + { + value = conversionFunc((value * scale - minVal) / (maxVal - minVal)) * ENVELOPE_MAX + ENVELOPE_MIN; + Limit(value, ENVELOPE_MIN, ENVELOPE_MAX); + return mpt::saturate_round(value); + } + + static double Identity(double v) noexcept { return v; } + + static double CentsToFilterCutoff(double v, const CSoundFile &sndFile, int envBaseCutoff, uint32 envBaseFreq) + { + const auto freq = envBaseFreq * std::pow(2.0, v / 1200.0); + return Util::muldivr(sndFile.FrequencyToCutOff(freq), 127, envBaseCutoff) / 127.0; + } + + static std::function FilterConversionFunc(const ModInstrument &ins, const CSoundFile &sndFile) + { + const auto envBaseCutoff = ins.IsCutoffEnabled() ? ins.GetCutoff() : 127; + const auto envBaseFreq = sndFile.CutOffToFrequency(envBaseCutoff); + return std::bind(CentsToFilterCutoff, std::placeholders::_1, std::cref(sndFile), envBaseCutoff, envBaseFreq); + } +}; + +struct SFZEnvelope +{ + double startLevel = 0, delay = 0, attack = 0, hold = 0; + double decay = 0, sustainLevel = 100, release = 0, depth = 0; + + void Parse(std::string_view key, const std::string &value) + { + key = key.substr(key.find('_') + 1); + double v = ConvertStrTo(value); + if(key == "depth") + Limit(v, -12000.0, 12000.0); + else if(key == "start" || key == "sustain") + Limit(v, -100.0, 100.0); + else + Limit(v, 0.0, 100.0); + + if(key == "start") + startLevel = v; + else if(key == "delay") + delay = v; + else if(key == "attack") + attack = v; + else if(key == "hold") + hold = v; + else if(key == "decay") + decay = v; + else if(key == "sustain") + sustainLevel = v; + else if(key == "release") + release = v; + else if(key == "depth") + depth = v; + } + + void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType, bool forceFilter = false) const + { + SFZFlexEG eg; + if(envType == ENV_VOLUME) + eg.amplitude = 1.0; + else if(envType == ENV_PITCH && !forceFilter) + eg.pitch = depth / 100.0; + else if(envType == ENV_PITCH && forceFilter) + eg.cutoff = depth / 100.0; + + auto &env = eg.points; + if(attack > 0 || delay > 0) + { + env.push_back({0, startLevel}); + if(delay > 0) + env.push_back({delay, env.back().second}); + env.push_back({attack, 100.0}); + } + if(hold > 0) + { + if(env.empty()) + env.push_back({0, 100.0}); + env.push_back({hold, env.back().second}); + } + if(env.empty()) + env.push_back({0, 100.0}); + if(env.back().second != sustainLevel) + env.push_back({decay, sustainLevel}); + if(sustainLevel != 0) + { + eg.sustain = static_cast(env.size() - 1); + env.push_back({release, 0.0}); + } else + { + eg.sustain = std::numeric_limits::max(); + } + + eg.ConvertToMPT(ins, sndFile); + } +}; + + +struct SFZRegion +{ + enum class LoopMode + { + kUnspecified, + kContinuous, + kOneShot, + kSustain, + kNoLoop + }; + + enum class LoopType + { + kUnspecified, + kForward, + kBackward, + kAlternate, + }; + + size_t filenameOffset = 0; + std::string filename, name; + SFZEnvelope ampEnv, pitchEnv, filterEnv; + std::vector flexEGs; + SmpLength loopStart = 0, loopEnd = 0; + SmpLength end = MAX_SAMPLE_LENGTH, offset = 0; + LoopMode loopMode = LoopMode::kUnspecified; + LoopType loopType = LoopType::kUnspecified; + double loopCrossfade = 0.0; + double cutoff = 0; // in Hz + double resonance = 0; // 0...40dB + double filterRandom = 0; // 0...9600 cents + double volume = 0; // -144dB...+6dB + double amplitude = 100.0; // 0...100 + double pitchBend = 200; // -9600...9600 cents + double pitchLfoFade = 0; // 0...100 seconds + double pitchLfoDepth = 0; // -1200...12000 + double pitchLfoFreq = 0; // 0...20 Hz + double panning = -128; // -100...+100 + double finetune = 0; // in cents + int8 transpose = 0; + uint8 keyLo = 0, keyHi = 127, keyRoot = 60; + FilterMode filterType = FilterMode::Unchanged; + uint8 polyphony = 255; + bool useSampleKeyRoot = false; + bool invertPhase = false; + + template + static void Read(const std::string &valueStr, T &value, Tc valueMin = std::numeric_limits::min(), Tc valueMax = std::numeric_limits::max()) + { + double valueF = ConvertStrTo(valueStr); + if constexpr(std::numeric_limits::is_integer) + { + valueF = mpt::round(valueF); + } + Limit(valueF, static_cast(valueMin), static_cast(valueMax)); + value = static_cast(valueF); + } + + static uint8 ReadKey(const std::string &value, const SFZControl &control) + { + if(value.empty()) + return 0; + + int key = 0; + if(value[0] >= '0' && value[0] <= '9') + { + // MIDI key + key = ConvertStrTo(value); + } else if(value.length() < 2) + { + return 0; + } else + { + // Scientific pitch + static constexpr int8 keys[] = { 9, 11, 0, 2, 4, 5, 7 }; + static_assert(std::size(keys) == 'g' - 'a' + 1); + auto keyC = value[0]; + if(keyC >= 'A' && keyC <= 'G') + key = keys[keyC - 'A']; + if(keyC >= 'a' && keyC <= 'g') + key = keys[keyC - 'a']; + else + return 0; + + uint8 octaveOffset = 1; + if(value[1] == '#') + { + key++; + octaveOffset = 2; + } else if(value[1] == 'b' || value[1] == 'B') + { + key--; + octaveOffset = 2; + } + if(octaveOffset >= value.length()) + return 0; + + int8 octave = ConvertStrTo(value.c_str() + octaveOffset); + key += (octave + 1) * 12; + } + key += control.octaveOffset * 12 + control.noteOffset; + return static_cast(Clamp(key, 0, 127)); + } + + void Parse(const std::string_view key, const std::string &value, const SFZControl &control) + { + if(key == "sample") + { + filename = control.defaultPath + value; + filenameOffset = control.defaultPath.size(); + } + else if(key == "region_label") + name = value; + else if(key == "lokey") + keyLo = ReadKey(value, control); + else if(key == "hikey") + keyHi = ReadKey(value, control); + else if(key == "pitch_keycenter") + { + keyRoot = ReadKey(value, control); + useSampleKeyRoot = (value == "sample"); + } + else if(key == "key") + { + keyLo = keyHi = keyRoot = ReadKey(value, control); + useSampleKeyRoot = false; + } + else if(key == "bend_up" || key == "bendup") + Read(value, pitchBend, -9600.0, 9600.0); + else if(key == "pitchlfo_fade") + Read(value, pitchLfoFade, 0.0, 100.0); + else if(key == "pitchlfo_depth") + Read(value, pitchLfoDepth, -12000.0, 12000.0); + else if(key == "pitchlfo_freq") + Read(value, pitchLfoFreq, 0.0, 20.0); + else if(key == "volume") + Read(value, volume, -144.0, 6.0); + else if(key == "amplitude") + Read(value, amplitude, 0.0, 100.0); + else if(key == "pan") + Read(value, panning, -100.0, 100.0); + else if(key == "transpose") + Read(value, transpose, -127, 127); + else if(key == "tune") + Read(value, finetune, -100.0, 100.0); + else if(key == "end") + Read(value, end, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "offset") + Read(value, offset, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "loop_start" || key == "loopstart") + Read(value, loopStart, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "loop_end" || key == "loopend") + Read(value, loopEnd, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "loop_crossfade") + Read(value, loopCrossfade, 0.0, DBL_MAX); + else if(key == "loop_mode" || key == "loopmode") + { + if(value == "loop_continuous") + loopMode = LoopMode::kContinuous; + else if(value == "one_shot") + loopMode = LoopMode::kOneShot; + else if(value == "loop_sustain") + loopMode = LoopMode::kSustain; + else if(value == "no_loop") + loopMode = LoopMode::kNoLoop; + } + else if(key == "loop_type" || key == "looptype") + { + if(value == "forward") + loopType = LoopType::kForward; + else if(value == "backward") + loopType = LoopType::kBackward; + else if(value == "alternate") + loopType = LoopType::kAlternate; + } + else if(key == "cutoff") + Read(value, cutoff, 0.0, 96000.0); + else if(key == "fil_random") + Read(value, filterRandom, 0.0, 9600.0); + else if(key == "resonance") + Read(value, resonance, 0.0, 40.0); + else if(key == "polyphony") + Read(value, polyphony, 0, 255); + else if(key == "phase") + invertPhase = (value == "invert"); + else if(key == "fil_type" || key == "filtype") + { + if(value == "lpf_1p" || value == "lpf_2p" || value == "lpf_4p" || value == "lpf_6p") + filterType = FilterMode::LowPass; + else if(value == "hpf_1p" || value == "hpf_2p" || value == "hpf_4p" || value == "hpf_6p") + filterType = FilterMode::HighPass; + // Alternatives: bpf_2p, brf_2p + } + else if(SFZStartsWith(key, "ampeg_")) + ampEnv.Parse(key, value); + else if(SFZStartsWith(key, "fileg_")) + filterEnv.Parse(key, value); + else if(SFZStartsWith(key, "pitcheg_")) + pitchEnv.Parse(key, value); + else if(SFZStartsWith(key, "eg") && SFZIsNumeric(key.substr(2, 2)) && key.substr(4, 1) == "_") + { + uint8 eg = ConvertStrTo(std::string(key.substr(2, 2))); + if(eg >= flexEGs.size()) + flexEGs.resize(eg + 1); + flexEGs[eg].Parse(key, value); + } + } +}; + +struct SFZInputFile +{ + FileReader file; + std::unique_ptr inputFile; // FileReader has pointers into this so its address must not change + std::string remain; + + SFZInputFile(FileReader f = {}, std::unique_ptr i = {}, std::string r = {}) + : file{std::move(f)}, inputFile{std::move(i)}, remain{std::move(r)} {} + SFZInputFile(SFZInputFile &&) = default; +}; + +bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) +{ + file.Rewind(); + + enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kCurve, kEffect, kUnknown } section = kNone; + bool inMultiLineComment = false; + SFZControl control; + SFZRegion group, master, globals; + std::vector regions; + std::map macros; + std::vector files; + files.emplace_back(file); + + std::string s; + while(!files.empty()) + { + if(!files.back().file.ReadLine(s, 1024)) + { + // Finished reading file, so back to remaining characters of the #include line from the previous file + s = std::move(files.back().remain); + files.pop_back(); + } + + if(inMultiLineComment) + { + if(auto commentEnd = s.find("*/"); commentEnd != std::string::npos) + { + s.erase(0, commentEnd + 2); + inMultiLineComment = false; + } else + { + continue; + } + } + + // First, terminate line at the start of a comment block + if(auto commentPos = s.find("//"); commentPos != std::string::npos) + { + s.resize(commentPos); + } + + // Now, read the tokens. + // This format is so funky that no general tokenizer approach seems to work here... + // Consider this jolly good example found at https://stackoverflow.com/questions/5923895/tokenizing-a-custom-text-file-format-file-using-c-sharp + // sample=piano C3.wav key=48 ampeg_release=0.7 // a comment here + // key = 49 sample = piano Db3.wav + // + // group=1 + // key = 48 + // sample = piano D3.ogg + // The original sfz specification claims that spaces around = are not allowed, but a quick look into the real world tells us otherwise. + + while(!s.empty()) + { + s.erase(0, s.find_first_not_of(" \t")); + + const bool isDefine = SFZStartsWith(s, "#define ") || SFZStartsWith(s, "#define\t"); + + // Replace macros (unless this is a #define statement, to allow for macro re-definition) + if(!isDefine) + { + for(const auto &[oldStr, newStr] : macros) + { + std::string::size_type pos = 0; + while((pos = s.find(oldStr, pos)) != std::string::npos) + { + s.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + } + } + + if(s.empty()) + break; + + std::string::size_type charsRead = 0; + + if(s[0] == '<' && (charsRead = s.find('>')) != std::string::npos) + { + // Section header + const auto sec = std::string_view(s).substr(1, charsRead - 1); + section = kUnknown; + if(sec == "global") + { + section = kGlobal; + // Reset global parameters + globals = SFZRegion(); + } else if(sec == "master") + { + section = kMaster; + // Reset master parameters + master = globals; + } else if(sec == "group") + { + section = kGroup; + // Reset group parameters + group = master; + } else if(sec == "region") + { + section = kRegion; + regions.push_back(group); + } else if(sec == "control") + { + section = kControl; + } else if(sec == "curve") + { + section = kCurve; + } else if(sec == "effect") + { + section = kEffect; + } + charsRead++; + } else if(isDefine) + { + // Macro definition + charsRead += 8; + auto keyStart = s.find_first_not_of(" \t", 8); + auto keyEnd = s.find_first_of(" \t", keyStart); + auto valueStart = s.find_first_not_of(" \t", keyEnd); + if(keyStart != std::string::npos && valueStart != std::string::npos) + { + charsRead = s.find_first_of(" \t", valueStart); + const auto key = s.substr(keyStart, keyEnd - keyStart); + if(key.length() > 1 && key[0] == '$') + macros[std::move(key)] = s.substr(valueStart, charsRead - valueStart); + } else + { + break; + } + } else if(SFZStartsWith(s, "#include ") || SFZStartsWith(s, "#include\t")) + { + // Include other sfz file + auto fileStart = s.find("\"", 9); // Yes, there can be arbitrary characters before the opening quote, at least that's how sforzando does it. + auto fileEnd = s.find("\"", fileStart + 1); + if(fileStart != std::string::npos && fileEnd != std::string::npos) + { + charsRead = fileEnd + 1; + fileStart++; + } else + { + break; + } + + std::string filenameU8 = s.substr(fileStart, fileEnd - fileStart); + mpt::PathString filename = mpt::PathString::FromUTF8(filenameU8); + if(!filename.empty()) + { + if(filenameU8.find(':') == std::string::npos) + filename = file.GetFileName().GetPath() + filename; + filename = filename.Simplify(); + // Avoid recursive #include + if(std::find_if(files.begin(), files.end(), [&filename](const SFZInputFile &f) { return f.file.GetFileName() == filename; }) == files.end()) + { + auto f = std::make_unique(filename); + if(f->IsValid()) + { + s.erase(0, charsRead); + files.emplace_back(GetFileReader(*f), std::move(f), std::move(s)); + break; + } else + { + AddToLog(LogWarning, U_("Unable to load include file: ") + filename.ToUnicode()); + } + } else + { + AddToLog(LogWarning, U_("Recursive include file ignored: ") + filename.ToUnicode()); + } + } + } else if(SFZStartsWith(s, "/*")) + { + // Multi-line comment + if(auto commentEnd = s.find("*/", charsRead + 2); commentEnd != std::string::npos) + { + charsRead = commentEnd; + } else + { + inMultiLineComment = true; + charsRead = s.length(); + } + } else if(section == kNone) + { + // Garbage before any section, probably not an sfz file + return false; + } else if(s.find('=') != std::string::npos) + { + // Read key=value pair + auto keyEnd = s.find_first_of(" \t="); + auto valueStart = s.find_first_not_of(" \t=", keyEnd); + if(valueStart == std::string::npos) + { + break; + } + const std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd)); + if(key == "sample" || key == "default_path" || SFZStartsWith(key, "label_cc") || SFZStartsWith(key, "region_label")) + { + // Sample / CC name may contain spaces... + charsRead = s.find_first_of("=\t<", valueStart); + if(charsRead != std::string::npos && s[charsRead] == '=') + { + // Backtrack to end of key + while(charsRead > valueStart && s[charsRead] == ' ') + charsRead--; + // Backtrack to start of key + while(charsRead > valueStart && s[charsRead] != ' ') + charsRead--; + } + } else + { + charsRead = s.find_first_of(" \t<", valueStart); + } + const std::string value = s.substr(valueStart, charsRead - valueStart); + + switch(section) + { + case kGlobal: + globals.Parse(key, value, control); + [[fallthrough]]; + case kMaster: + master.Parse(key, value, control); + [[fallthrough]]; + case kGroup: + group.Parse(key, value, control); + break; + case kRegion: + regions.back().Parse(key, value, control); + break; + case kControl: + control.Parse(key, value); + break; + } + } else + { + // Garbage, probably not an sfz file + return false; + } + + // Remove the token(s) we just read + s.erase(0, charsRead); + } + } + + if(regions.empty()) + return false; + + + ModInstrument *pIns = new (std::nothrow) ModInstrument(); + if(pIns == nullptr) + return false; + + RecalculateSamplesPerTick(); + DestroyInstrument(nInstr, deleteAssociatedSamples); + if(nInstr > m_nInstruments) m_nInstruments = nInstr; + Instruments[nInstr] = pIns; + + SAMPLEINDEX prevSmp = 0; + for(auto ®ion : regions) + { + uint8 keyLo = region.keyLo, keyHi = region.keyHi; + if(keyLo > keyHi) + continue; + Clamp(keyLo, 0, NOTE_MAX - NOTE_MIN); + Clamp(keyHi, 0, NOTE_MAX - NOTE_MIN); + SAMPLEINDEX smp = GetNextFreeSample(nInstr, prevSmp + 1); + if(smp == SAMPLEINDEX_INVALID) + break; + prevSmp = smp; + + ModSample &sample = Samples[smp]; + sample.Initialize(MOD_TYPE_MPT); + if(const auto synthSample = std::string_view(region.filename).substr(region.filenameOffset); SFZStartsWith(synthSample, "*")) + { + sample.nLength = 256; + sample.nC5Speed = mpt::saturate_round(sample.nLength * 261.6255653); + sample.uFlags.set(CHN_16BIT); + std::function generator; + if(synthSample == "*sine") + generator = [](int32 i) { return mpt::saturate_round(std::sin(i * 2.0 * M_PI / 256.0) * int16_max); }; + else if(synthSample == "*square") + generator = [](int32 i) { return i < 128 ? int16_max : int16_min; }; + else if(synthSample == "*triangle" || synthSample == "*tri") + generator = [](int32 i) { return static_cast(i < 128 ? ((63 - i) * 512) : ((i - 192) * 512)); }; + else if(synthSample == "*saw") + generator = [](int32 i) { return static_cast((i - 128) * 256); }; + else if(synthSample == "*silence") + generator = [](int32) { return int16(0); }; + else if(synthSample == "*noise") + { + sample.nLength = sample.nC5Speed; + generator = [this](int32) { return mpt::random(AccessPRNG()); }; + } else + { + AddToLog(LogWarning, U_("Unknown sample type: ") + mpt::ToUnicode(mpt::Charset::UTF8, std::string(synthSample))); + prevSmp--; + continue; + } + if(sample.AllocateSample()) + { + for(SmpLength i = 0; i < sample.nLength; i++) + { + sample.sample16()[i] = generator(static_cast(i)); + } + if(smp > m_nSamples) + m_nSamples = smp; + region.offset = 0; + region.loopMode = SFZRegion::LoopMode::kContinuous; + region.loopStart = 0; + region.loopEnd = sample.nLength - 1; + region.loopCrossfade = 0; + region.keyRoot = 60; + } + } else if(auto filename = mpt::PathString::FromUTF8(region.filename); !filename.empty()) + { + if(region.filename.find(':') == std::string::npos) + { + filename = file.GetFileName().GetPath() + filename; + } + SetSamplePath(smp, filename); + InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); + FileReader smpFile = GetFileReader(f); + if(!ReadSampleFromFile(smp, smpFile, false)) + { + AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); + prevSmp--; + continue; + } + + if(UseFinetuneAndTranspose()) + sample.TransposeToFrequency(); + + sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData()); + } + + if(!region.name.empty()) + m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.name); + if(!m_szNames[smp][0]) + m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::PathString::FromUTF8(region.filename).GetFileName().ToUnicode()); + + if(region.useSampleKeyRoot) + { + if(sample.rootNote != NOTE_NONE) + region.keyRoot = sample.rootNote - NOTE_MIN; + else + region.keyRoot = 60; + } + + const auto origSampleRate = sample.GetSampleRate(GetType()); + int8 transp = region.transpose + (60 - region.keyRoot); + for(uint8 i = keyLo; i <= keyHi; i++) + { + pIns->Keyboard[i] = smp; + if(GetType() != MOD_TYPE_XM) + pIns->NoteMap[i] = NOTE_MIN + i + transp; + } + if(GetType() == MOD_TYPE_XM) + sample.Transpose(transp / 12.0); + + pIns->filterMode = region.filterType; + if(region.cutoff != 0) + pIns->SetCutoff(FrequencyToCutOff(region.cutoff), true); + if(region.resonance != 0) + pIns->SetResonance(mpt::saturate_round(region.resonance * 128.0 / 24.0), true); + pIns->nCutSwing = mpt::saturate_round(region.filterRandom * (m_SongFlags[SONG_EXFILTERRANGE] ? 20 : 24) / 1200.0); + pIns->midiPWD = mpt::saturate_round(region.pitchBend / 100.0); + + pIns->nNNA = NNA_NOTEOFF; + if(region.polyphony == 1) + { + pIns->nDNA = DNA_NOTECUT; + pIns->nDCT = DCT_SAMPLE; + } + region.ampEnv.ConvertToMPT(pIns, *this, ENV_VOLUME); + if(region.pitchEnv.depth) + region.pitchEnv.ConvertToMPT(pIns, *this, ENV_PITCH); + else if(region.filterEnv.depth) + region.filterEnv.ConvertToMPT(pIns, *this, ENV_PITCH, true); + + for(const auto &flexEG : region.flexEGs) + { + flexEG.ConvertToMPT(pIns, *this); + } + + if(region.ampEnv.release > 0) + { + const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(GetSampleRate()); + pIns->nFadeOut = std::min(mpt::saturate_cast(32768.0 * tickDuration / region.ampEnv.release), uint32(32767)); + if(GetType() == MOD_TYPE_IT) + pIns->nFadeOut = std::min((pIns->nFadeOut + 16u) & ~31u, uint32(8192)); + } + + sample.rootNote = region.keyRoot + NOTE_MIN; + sample.nGlobalVol = mpt::saturate_round(64.0 * Clamp(std::pow(10.0, region.volume / 20.0) * region.amplitude / 100.0, 0.0, 1.0)); + if(region.panning != -128) + { + sample.nPan = mpt::saturate_round((region.panning + 100) * 256.0 / 200.0); + sample.uFlags.set(CHN_PANNING); + } + sample.Transpose(region.finetune / 1200.0); + + if(region.pitchLfoDepth && region.pitchLfoFreq) + { + sample.nVibSweep = 255; + if(region.pitchLfoFade > 0) + sample.nVibSweep = mpt::saturate_round(255.0 / region.pitchLfoFade); + sample.nVibDepth = mpt::saturate_round(region.pitchLfoDepth * 32.0 / 100.0); + sample.nVibRate = mpt::saturate_round(region.pitchLfoFreq * 4.0); + } + + if(region.loopMode != SFZRegion::LoopMode::kUnspecified) + { + switch(region.loopMode) + { + case SFZRegion::LoopMode::kContinuous: + case SFZRegion::LoopMode::kOneShot: + sample.uFlags.set(CHN_LOOP); + break; + case SFZRegion::LoopMode::kSustain: + sample.uFlags.set(CHN_SUSTAINLOOP); + break; + case SFZRegion::LoopMode::kNoLoop: + sample.uFlags.reset(CHN_LOOP | CHN_SUSTAINLOOP); + } + } + if(region.loopEnd > region.loopStart) + { + // Loop may also be defined in file, in which case loopStart and loopEnd are unset. + if(region.loopMode == SFZRegion::LoopMode::kSustain) + { + sample.nSustainStart = region.loopStart; + sample.nSustainEnd = region.loopEnd + 1; + } else if(region.loopMode == SFZRegion::LoopMode::kContinuous || region.loopMode == SFZRegion::LoopMode::kOneShot) + { + sample.nLoopStart = region.loopStart; + sample.nLoopEnd = region.loopEnd + 1; + } + } else if(sample.nLoopEnd <= sample.nLoopStart && region.loopMode != SFZRegion::LoopMode::kUnspecified && region.loopMode != SFZRegion::LoopMode::kNoLoop) + { + sample.nLoopEnd = sample.nLength; + } + switch(region.loopType) + { + case SFZRegion::LoopType::kUnspecified: + break; + case SFZRegion::LoopType::kForward: + sample.uFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_REVERSE); + break; + case SFZRegion::LoopType::kBackward: + sample.uFlags.set(CHN_REVERSE); + break; + case SFZRegion::LoopType::kAlternate: + sample.uFlags.set(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN); + break; + default: + break; + } + if(sample.nSustainEnd <= sample.nSustainStart && sample.nLoopEnd > sample.nLoopStart && region.loopMode == SFZRegion::LoopMode::kSustain) + { + // Turn normal loop (imported from sample) into sustain loop + std::swap(sample.nSustainStart, sample.nLoopStart); + std::swap(sample.nSustainEnd, sample.nLoopEnd); + sample.uFlags.set(CHN_SUSTAINLOOP); + sample.uFlags.set(CHN_PINGPONGSUSTAIN, sample.uFlags[CHN_PINGPONGLOOP]); + sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); + } + + mpt::PathString filenameModifier; + + // Loop cross-fade + SmpLength fadeSamples = mpt::saturate_round(region.loopCrossfade * origSampleRate); + LimitMax(fadeSamples, sample.uFlags[CHN_SUSTAINLOOP] ? sample.nSustainStart : sample.nLoopStart); + if(fadeSamples > 0) + { + ctrlSmp::XFadeSample(sample, fadeSamples, 50000, true, sample.uFlags[CHN_SUSTAINLOOP], *this); + sample.uFlags.set(SMP_MODIFIED); + filenameModifier += P_(" (cross-fade)"); + } + + // Sample offset + if(region.offset && region.offset < sample.nLength) + { + auto offset = region.offset * sample.GetBytesPerSample(); + memmove(sample.sampleb(), sample.sampleb() + offset, sample.nLength * sample.GetBytesPerSample() - offset); + if(region.end > region.offset) + region.end -= region.offset; + sample.nLength -= region.offset; + sample.nLoopStart -= region.offset; + sample.nLoopEnd -= region.offset; + sample.uFlags.set(SMP_MODIFIED); + filenameModifier += P_(" (offset)"); + } + LimitMax(sample.nLength, region.end); + + if(region.invertPhase) + { + ctrlSmp::InvertSample(sample, 0, sample.nLength, *this); + sample.uFlags.set(SMP_MODIFIED); + filenameModifier += P_(" (inverted)"); + } + + if(sample.uFlags.test_all(SMP_KEEPONDISK | SMP_MODIFIED)) + { + // Avoid ruining the original samples + if(auto filename = GetSamplePath(smp); !filename.empty()) + { + filename = filename.GetPath() + filename.GetFileName() + filenameModifier + filename.GetFileExt(); + SetSamplePath(smp, filename); + } + } + + sample.PrecomputeLoops(*this, false); + sample.Convert(MOD_TYPE_MPT, GetType()); + } + + pIns->Sanitize(MOD_TYPE_MPT); + pIns->Convert(MOD_TYPE_MPT, GetType()); + return true; +} + +#ifndef MODPLUG_NO_FILESAVE + +static double SFZLinear2dB(double volume) +{ + return (volume > 0.0 ? 20.0 * std::log10(volume) : -144.0); +} + +static void WriteSFZEnvelope(std::ostream &f, double tickDuration, int index, const InstrumentEnvelope &env, const char *type, double scale, std::function convFunc) +{ + if(!env.dwFlags[ENV_ENABLED] || env.empty()) + return; + + const bool sustainAtEnd = (!env.dwFlags[ENV_SUSTAIN] || env.nSustainStart == (env.size() - 1)) && convFunc(env.back().value) != 0.0; + + const auto prefix = mpt::format("\neg%1_")(mpt::fmt::dec0<2>(index)); + f << "\n" << prefix << type << "=" << scale; + f << prefix << "points=" << (env.size() + (sustainAtEnd ? 1 : 0)); + EnvelopeNode::tick_t lastTick = 0; + int nodeIndex = 0; + for(const auto &node : env) + { + const double time = (node.tick - lastTick) * tickDuration; + lastTick = node.tick; + f << prefix << "time" << nodeIndex << "=" << time; + f << prefix << "level" << nodeIndex << "=" << convFunc(node.value); + nodeIndex++; + } + if(sustainAtEnd) + { + // Prevent envelope from going back to neutral + f << prefix << "time" << nodeIndex << "=0"; + f << prefix << "level" << nodeIndex << "=" << convFunc(env.back().value); + } + // We always must write a sustain point, or the envelope will be sustained on the first point of the envelope + f << prefix << "sustain=" << (env.dwFlags[ENV_SUSTAIN] ? env.nSustainStart : (env.size() - 1)); + + if(env.dwFlags[ENV_LOOP]) + f << "\n// Loop: " << static_cast(env.nLoopStart) << "-" << static_cast(env.nLoopEnd); + if(env.dwFlags[ENV_SUSTAIN] && env.nSustainEnd > env.nSustainStart) + f << "\n// Sustain Loop: " << static_cast(env.nSustainStart) << "-" << static_cast(env.nSustainEnd); + if(env.nReleaseNode != ENV_RELEASE_NODE_UNSET) + f << "\n// Release Node: " << static_cast(env.nReleaseNode); +} + +bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const +{ +#ifdef MODPLUG_TRACKER + const mpt::FlushMode flushMode = mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave); +#else + const mpt::FlushMode flushMode = mpt::FlushMode::Full; +#endif + const ModInstrument *ins = Instruments[nInstr]; + if(ins == nullptr) + return false; + + 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; + + const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(m_MixerSettings.gdwMixingFreq); + + f << std::setprecision(10); + if(!ins->name.empty()) + { + f << "// Name: " << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), ins->name) << "\n"; + } + f << "// Created with " << mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()) << "\n"; + f << "// Envelope tempo base: tempo " << m_PlayState.m_nMusicTempo.ToDouble(); + switch(m_nTempoMode) + { + case tempoModeClassic: + f << " (classic tempo mode)"; + break; + case tempoModeAlternative: + f << " (alternative tempo mode)"; + break; + case tempoModeModern: + f << ", " << m_PlayState.m_nMusicSpeed << " ticks per row, " << m_PlayState.m_nCurrentRowsPerBeat << " rows per beat (modern tempo mode)"; + break; + default: + MPT_ASSERT_NOTREACHED(); + break; + } + + f << "\n\n\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n"; + f << ""; + f << "\nbend_up=" << ins->midiPWD * 100; + f << "\nbend_down=" << -ins->midiPWD * 100; + const uint32 cutoff = ins->IsCutoffEnabled() ? ins->GetCutoff() : 127; + // If filter envelope is active but cutoff is not set, we still need to set the base cutoff frequency to be modulated by the envelope. + if(ins->IsCutoffEnabled() || ins->PitchEnv.dwFlags[ENV_FILTER]) + f << "\ncutoff=" << CSoundFile::CutOffToFrequency(cutoff) << " // " << cutoff; + if(ins->IsResonanceEnabled()) + f << "\nresonance=" << Util::muldivr_unsigned(ins->GetResonance(), 24, 128) << " // " << static_cast(ins->GetResonance()); + if(ins->IsCutoffEnabled() || ins->IsResonanceEnabled()) + f << "\nfil_type=" << (ins->filterMode == FilterMode::HighPass ? "hpf_2p" : "lpf_2p"); + if(ins->dwFlags[INS_SETPANNING]) + f << "\npan=" << (Util::muldivr_unsigned(ins->nPan, 200, 256) - 100) << " // " << ins->nPan; + if(ins->nGlobalVol != 64) + f << "\nvolume=" << SFZLinear2dB(ins->nGlobalVol / 64.0) << " // " << ins->nGlobalVol; + if(ins->nFadeOut) + { + f << "\nampeg_release=" << (32768.0 * tickDuration / ins->nFadeOut) << " // " << ins->nFadeOut; + f << "\nampeg_release_shape=0"; + } + + if(ins->nDNA == DNA_NOTECUT && ins->nDCT != DCT_NONE) + f << "\npolyphony=1"; + + WriteSFZEnvelope(f, tickDuration, 1, ins->VolEnv, "amplitude", 100.0, [](int32 val) { return val / static_cast(ENVELOPE_MAX); }); + WriteSFZEnvelope(f, tickDuration, 2, ins->PanEnv, "pan", 100.0, [](int32 val) { return 2.0 * (val - ENVELOPE_MID) / (ENVELOPE_MAX - ENVELOPE_MIN); }); + if(ins->PitchEnv.dwFlags[ENV_FILTER]) + { + const auto envScale = 1200.0 * std::log(CutOffToFrequency(127, 256) / static_cast(CutOffToFrequency(0, -256))) / M_LN2; + const auto cutoffNormal = CutOffToFrequency(cutoff); + WriteSFZEnvelope(f, tickDuration, 3, ins->PitchEnv, "cutoff", envScale, [this, cutoff, cutoffNormal, envScale](int32 val) { + // Convert interval between center frequency and envelope into cents + const auto freq = CutOffToFrequency(cutoff, (val - ENVELOPE_MID) * 256 / (ENVELOPE_MAX - ENVELOPE_MID)); + return 1200.0 * std::log(freq / static_cast(cutoffNormal)) / M_LN2 / envScale; + }); + } else + { + WriteSFZEnvelope(f, tickDuration, 3, ins->PitchEnv, "pitch", 1600.0, [](int32 val) { return 2.0 * (val - ENVELOPE_MID) / (ENVELOPE_MAX - ENVELOPE_MIN); }); + } + + size_t numSamples = 0; + for(size_t i = 0; i < std::size(ins->Keyboard); i++) + { + if(ins->Keyboard[i] < 1 || ins->Keyboard[i] > GetNumSamples()) + continue; + + size_t endOfRegion = i + 1; + while(endOfRegion < std::size(ins->Keyboard)) + { + if(ins->Keyboard[endOfRegion] != ins->Keyboard[i] || ins->NoteMap[endOfRegion] != (ins->NoteMap[i] + endOfRegion - i)) + break; + endOfRegion++; + } + endOfRegion--; + + const ModSample &sample = Samples[ins->Keyboard[i]]; + const bool isAdlib = sample.uFlags[CHN_ADLIB]; + + if(!sample.HasSampleData()) + { + i = endOfRegion; + continue; + } + + numSamples++; + mpt::PathString sampleName = sampleBasePath + sampleBaseName + P_(" ") + mpt::PathString::FromUnicode(mpt::ufmt::val(numSamples)); + if(isAdlib) + sampleName += P_(".s3i"); + else 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(isAdlib) + SaveS3ISample(ins->Keyboard[i], fSmp); + else 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()); + } + + f << "\n\n"; + if(!m_szNames[ins->Keyboard[i]].empty()) + { + f << "\nregion_label=" << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[ins->Keyboard[i]]); + } + f << "\nsample=" << sampleName.GetFullFileName().ToUTF8(); + f << "\nlokey=" << i; + f << "\nhikey=" << endOfRegion; + if(sample.rootNote != NOTE_NONE) + f << "\npitch_keycenter=" << sample.rootNote - NOTE_MIN; + else + f << "\npitch_keycenter=" << NOTE_MIDDLEC + i - ins->NoteMap[i]; + if(sample.uFlags[CHN_PANNING]) + f << "\npan=" << (Util::muldivr_unsigned(sample.nPan, 200, 256) - 100) << " // " << sample.nPan; + if(sample.nGlobalVol != 64) + f << "\nvolume=" << SFZLinear2dB((ins->nGlobalVol * sample.nGlobalVol) / 4096.0) << " // " << sample.nGlobalVol; + const char *loopMode = "no_loop", *loopType = "forward"; + SmpLength loopStart = 0, loopEnd = 0; + if(sample.uFlags[CHN_SUSTAINLOOP]) + { + loopMode = "loop_sustain"; + loopStart = sample.nSustainStart; + loopEnd = sample.nSustainEnd; + if(sample.uFlags[CHN_PINGPONGSUSTAIN]) + loopType = "alternate"; + } else if(sample.uFlags[CHN_LOOP]) + { + loopMode = "loop_continuous"; + loopStart = sample.nLoopStart; + loopEnd = sample.nLoopEnd; + if(sample.uFlags[CHN_PINGPONGLOOP]) + loopType = "alternate"; + else if(sample.uFlags[CHN_REVERSE]) + loopType = "backward"; + } + f << "\nloop_mode=" << loopMode; + if(loopStart < loopEnd) + { + f << "\nloop_start=" << loopStart; + f << "\nloop_end=" << (loopEnd - 1); + f << "\nloop_type=" << loopType; + } + if(sample.uFlags.test_all(CHN_SUSTAINLOOP | CHN_LOOP)) + { + f << "\n// Warning: Only sustain loop was exported!"; + } + i = endOfRegion; + } + + return true; +} + +#endif // MODPLUG_NO_FILESAVE + +#else +bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX, FileReader &) +{ + return false; +} +#endif // MPT_EXTERNAL_SAMPLES + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp index d0ba12ce2..e467cfb5d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp @@ -24,14 +24,35 @@ //#include "../common/mptCRC.h" #include "OggStream.h" #ifdef MPT_WITH_OGG +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #if defined(MPT_WITH_VORBIS) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include -#endif +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#endif // MPT_WITH_VORBIS #if defined(MPT_WITH_VORBISFILE) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include -#endif +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#endif // MPT_WITH_VORBISFILE #ifdef MPT_WITH_STBVORBIS #include #endif // MPT_WITH_STBVORBIS @@ -48,7 +69,7 @@ OPENMPT_NAMESPACE_BEGIN static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) { FileReader &file = *reinterpret_cast(datasource); - return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; + return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; } static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) @@ -119,7 +140,7 @@ static long VorbisfileFilereaderTell(void *datasource) #if defined(MPT_WITH_VORBIS) static mpt::ustring UStringFromVorbis(const char *str) { - return str ? mpt::ToUnicode(mpt::CharsetUTF8, str) : mpt::ustring(); + return str ? mpt::ToUnicode(mpt::Charset::UTF8, str) : mpt::ustring(); } #endif // MPT_WITH_VORBIS @@ -247,7 +268,7 @@ bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) // files, stb_vorbis will include superfluous samples at the beginning. FileReader::PinnedRawDataView fileView = file.GetPinnedRawDataView(); - const mpt::byte* data = fileView.data(); + const std::byte* data = fileView.data(); std::size_t dataLeft = fileView.size(); std::size_t offset = 0; @@ -309,7 +330,7 @@ bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) } DestroySampleThreadsafe(sample); - mpt::String::Copy(m_szNames[sample], sampleName); + m_szNames[sample] = sampleName; Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = mpt::saturate_cast(raw_sample_data.size() / channels); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp index 968848465..3bd949b5e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp @@ -13,13 +13,12 @@ #include "mod_specifications.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Moddoc.h" -#include "../mptrack/TrackerSettings.h" #include "Dlsbank.h" -#endif //MODPLUG_TRACKER +#endif // MODPLUG_TRACKER #include "../soundlib/AudioCriticalSection.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" -#endif +#endif // !MODPLUG_NO_FILESAVE #include "../common/misc_util.h" #include "Tagging.h" #include "ITTools.h" @@ -29,10 +28,10 @@ #include "../common/version.h" #include "Loaders.h" #include "ChunkReader.h" -#include "modsmp_ctrl.h" #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" #include "../soundlib/ModSampleCopy.h" +#include #include @@ -53,6 +52,7 @@ bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool && !ReadS3ISample(nSample, file) && !ReadSBISample(nSample, file) && !ReadAUSample(nSample, file, mayNormalize) + && !ReadBRRSample(nSample, file) && !ReadFLACSample(nSample, file) && !ReadOpusSample(nSample, file) && !ReadVorbisSample(nSample, file) @@ -261,9 +261,9 @@ bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoun } #ifdef MODPLUG_TRACKER - if(!strcmp(pIns->filename, "") && srcSong.GetpModDoc() != nullptr && &srcSong != this) + if(pIns->filename.empty() && srcSong.GetpModDoc() != nullptr && &srcSong != this) { - mpt::String::Copy(pIns->filename, srcSong.GetpModDoc()->GetPathNameMpt().GetFullFileName().ToLocale()); + pIns->filename = srcSong.GetpModDoc()->GetPathNameMpt().GetFullFileName().ToLocale(); } #endif pIns->Convert(srcSong.GetType(), GetType()); @@ -294,7 +294,7 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & if(GetNumSamples() < targetSample) m_nSamples = targetSample; targetSmp = sourceSmp; - strcpy(m_szNames[targetSample], srcSong.m_szNames[sourceSample]); + m_szNames[targetSample] = srcSong.m_szNames[sourceSample]; if(sourceSmp.HasSampleData()) { @@ -314,9 +314,9 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & } #ifdef MODPLUG_TRACKER - if(!strcmp(targetSmp.filename, "") && srcSong.GetpModDoc() != nullptr && &srcSong != this) + if((targetSmp.filename.empty()) && srcSong.GetpModDoc() != nullptr && &srcSong != this) { - mpt::String::Copy(targetSmp.filename, mpt::ToCharset(GetCharsetInternal(), srcSong.GetpModDoc()->GetTitle())); + targetSmp.filename = mpt::ToCharset(GetCharsetInternal(), srcSong.GetpModDoc()->GetTitle()); } #endif @@ -339,8 +339,8 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file, uint16 blockAlign, uint32 numChannels) { - static const int32 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; - static const int32 IMAUnpackTable[90] = + static constexpr int8 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; + static constexpr int16 IMAUnpackTable[90] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, @@ -365,7 +365,7 @@ static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file { FileReader block = file.ReadChunk(blockAlign); FileReader::PinnedRawDataView blockView = block.GetPinnedRawDataView(); - const mpt::byte *data = blockView.data(); + const std::byte *data = blockView.data(); const uint32 blockSize = static_cast(blockView.size()); for(uint32 chn = 0; chn < numChannels; chn++) @@ -431,7 +431,7 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo } DestroySampleThreadsafe(nSample); - strcpy(m_szNames[nSample], ""); + m_szNames[nSample] = ""; ModSample &sample = Samples[nSample]; sample.Initialize(); sample.nLength = wavFile.GetSampleLength(); @@ -538,14 +538,15 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const { + const ModSample &sample = Samples[nSample]; + if(sample.uFlags[CHN_ADLIB]) + return false; + WAVWriter file(&f); if(!file.IsValid()) - { return false; - } - const ModSample &sample = Samples[nSample]; file.WriteFormat(sample.GetSampleRate(GetType()), sample.GetElementarySampleSize() * 8, sample.GetNumChannels(), WAVFormatChunk::fmtPCM); // Write sample data @@ -640,7 +641,7 @@ static void Wave64TagFromLISTINFO(mpt::ustring & dst, uint16 codePage, const Chu 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); + dst = mpt::ToUnicode(codePage, mpt::Charset::Windows1252, str); } @@ -755,7 +756,7 @@ bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNo FileTags tags; - uint16 codePage = 28591; // mpt::CharsetISO8859_1 + uint16 codePage = 28591; // mpt::Charset::ISO8859_1 FileReader csetChunk = chunkList.GetChunk(guidCSET); if(csetChunk.IsValid()) { @@ -804,7 +805,7 @@ bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNo sampleIO.ReadSample(mptSample, audioData); - mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); @@ -989,7 +990,7 @@ static void PatchToSample(CSoundFile *that, SAMPLEINDEX nSample, GF1SampleHeader sample.Convert(MOD_TYPE_IT, that->GetType()); sample.PrecomputeLoops(*that, false); - mpt::String::Read(that->m_szNames[nSample], sampleHeader.name); + that->m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } @@ -1018,7 +1019,7 @@ bool CSoundFile::ReadPATSample(SAMPLEINDEX nSample, FileReader &file) if(instrHeader.name[0] > ' ') { - mpt::String::Read(m_szNames[nSample], instrHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); } return true; } @@ -1054,7 +1055,7 @@ bool CSoundFile::ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file) if (nInstr > m_nInstruments) m_nInstruments = nInstr; Instruments[nInstr] = pIns; - mpt::String::Read(pIns->name, instrHeader.name); + pIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); pIns->nFadeOut = 2048; if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { @@ -1076,7 +1077,7 @@ bool CSoundFile::ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file) GF1SampleHeader sampleHeader; PatchToSample(this, nextSample, sampleHeader, file); int32 nMinNote = (sampleHeader.low_freq > 100) ? PatchFreqToNoteInt(sampleHeader.low_freq) : 0; - int32 nMaxNote = (sampleHeader.high_freq > 100) ? PatchFreqToNoteInt(sampleHeader.high_freq) : NOTE_MAX; + int32 nMaxNote = (sampleHeader.high_freq > 100) ? PatchFreqToNoteInt(sampleHeader.high_freq) : static_cast(NOTE_MAX); int32 nBaseNote = (sampleHeader.root_freq > 100) ? PatchFreqToNoteInt(sampleHeader.root_freq) : -1; if(!sampleHeader.scale_factor && layerHeader.samples == 1) { nMinNote = 0; nMaxNote = NOTE_MAX; } // Fill Note Map @@ -1139,7 +1140,7 @@ bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file) ModSample &sample = Samples[nSample]; sampleHeader.ConvertToMPT(sample); - mpt::String::Read(m_szNames[nSample], sampleHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) sampleHeader.GetSampleFormat(false).ReadSample(sample, file); @@ -1161,8 +1162,8 @@ bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const 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())); + mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader.name) = m_szNames[smp]; + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.reserved2) = mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()); if(length) sampleHeader.dataPointer[1] = sizeof(S3MSampleHeader) >> 4; mpt::IO::Write(f, sampleHeader); @@ -1290,8 +1291,8 @@ bool CSoundFile::ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) mptSample.uFlags &= ~CHN_PANNING; } - mpt::String::Read(mptSample.filename, sampleHeader.name); - mpt::String::Read(m_szNames[sampleMap[i]], sampleHeader.name); + mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); + m_szNames[sampleMap[i]] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); sampleFlags[i] = sampleHeader.GetSampleFormat(); } @@ -1352,7 +1353,7 @@ bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const } sampleFlags[i] = xmSample.GetSampleFormat(); - mpt::String::Write(xmSample.name, m_szNames[samples[i]]); + mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[i]]; mpt::IO::Write(f, xmSample); } @@ -1425,8 +1426,8 @@ bool CSoundFile::ReadXISample(SAMPLEINDEX nSample, FileReader &file) fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample); mptSample.Convert(MOD_TYPE_XM, GetType()); - mpt::String::Read(mptSample.filename, sampleHeader.name); - mpt::String::Read(m_szNames[nSample], sampleHeader.name); + mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); // Read sample data sampleHeader.GetSampleFormat().ReadSample(Samples[nSample], file); @@ -1436,860 +1437,6 @@ bool CSoundFile::ReadXISample(SAMPLEINDEX nSample, FileReader &file) } -///////////////////////////////////////////////////////////////////////////////////////// -// SFZ Instrument - -#ifdef MPT_EXTERNAL_SAMPLES - -struct SFZControl -{ - std::string defaultPath; - int8 octaveOffset = 0, noteOffset = 0; - - void Parse(const std::string &key, const std::string &value) - { - if(key == "default_path") - defaultPath = value; - else if(key == "octave_offset") - octaveOffset = ConvertStrTo(value); - else if(key == "note_offset") - noteOffset = ConvertStrTo(value); - } -}; - -struct SFZEnvelope -{ - float startLevel = 0, delay = 0, attack = 0, hold = 0, - decay = 0, sustainLevel = 100, release = 0, depth = 0; - - void Parse(std::string key, const std::string &value) - { - key.erase(0, key.find('_') + 1); - float v = ConvertStrTo(value); - if(key == "depth") - Limit(v, -12000.0f, 12000.0f); - else if(key == "start" || key == "sustain") - Limit(v, -100.0f, 100.0f); - else - Limit(v, 0.0f, 100.0f); - - if(key == "start") - startLevel = v; - else if(key == "delay") - delay = v; - else if(key == "attack") - attack = v; - else if(key == "hold") - hold = v; - else if(key == "decay") - decay = v; - else if(key == "sustain") - sustainLevel = v; - else if(key == "release") - release = v; - else if(key == "depth") - depth = v; - } - - static EnvelopeNode::tick_t ToTicks(float duration, float tickDuration) - { - return std::max(EnvelopeNode::tick_t(1), mpt::saturate_round(duration / tickDuration)); - } - - EnvelopeNode::value_t ToValue(float value, EnvelopeType envType) const - { - value *= (ENVELOPE_MAX / 100.0f); - if(envType == ENV_PITCH) - { - value *= depth / 3200.0f; - value += ENVELOPE_MID; - } - Limit(value, ENVELOPE_MIN, ENVELOPE_MAX); - return mpt::saturate_round(value); - } - - void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType) const - { - auto &env = ins->GetEnvelope(envType); - float tickDuration = sndFile.m_PlayState.m_nSamplesPerTick / static_cast(sndFile.GetSampleRate()); - if(tickDuration <= 0) - return; - env.clear(); - if(envType != ENV_VOLUME && attack == 0 && delay == 0 && hold == 0 && decay == 0 && sustainLevel == 100 && release == 0 && depth == 0) - { - env.dwFlags.reset(ENV_SUSTAIN | ENV_ENABLED); - return; - } - if(attack > 0 || delay > 0) - { - env.push_back(0, ToValue(startLevel, envType)); - if(delay > 0) - env.push_back(ToTicks(delay, tickDuration), env.back().value); - env.push_back(env.back().tick + ToTicks(attack, tickDuration), ToValue(100, envType)); - } - if(hold > 0) - { - if(env.empty()) - env.push_back(0, ToValue(100, envType)); - env.push_back(env.back().tick + ToTicks(hold, tickDuration), env.back().value); - } - if(env.empty()) - env.push_back(0, ToValue(100, envType)); - auto sustain = ToValue(sustainLevel, envType); - if(env.back().value != sustain) - env.push_back(env.back().tick + ToTicks(decay, tickDuration), sustain); - env.nSustainStart = env.nSustainEnd = static_cast(env.size() - 1); - if(sustainLevel != 0) - { - if(envType == ENV_VOLUME && env.nSustainEnd > 0) - env.nReleaseNode = env.nSustainEnd; - env.push_back(env.back().tick + ToTicks(release, tickDuration), ToValue(0, envType)); - env.dwFlags.set(ENV_SUSTAIN); - } - env.dwFlags.set(ENV_ENABLED); - } -}; - -struct SFZRegion -{ - enum class LoopMode - { - kUnspecified, - kContinuous, - kOneShot, - kSustain, - kNoLoop - }; - - enum class LoopType - { - kUnspecified, - kForward, - kBackward, - kAlternate, - }; - - std::string filename, name; - SFZEnvelope ampEnv, pitchEnv, filterEnv; - SmpLength loopStart = 0, loopEnd = 0; - SmpLength end = MAX_SAMPLE_LENGTH, offset = 0; - double loopCrossfade = 0.0; - LoopMode loopMode = LoopMode::kUnspecified; - LoopType loopType = LoopType::kUnspecified; - int32 cutoff = 0; // in Hz - int32 filterRandom = 0; // 0...9600 cents - int16 volume = 0; // -144dB...+6dB - int16 pitchBend = 200; // -9600...9600 cents - float pitchLfoFade = 0; // 0...100 seconds - int16 pitchLfoDepth = 0; // -1200...12000 - uint8 pitchLfoFreq = 0; // 0...20 Hz - int8 panning = -128; // -100...+100 - int8 transpose = 0; - int8 finetune = 0; - uint8 keyLo = 0, keyHi = 127, keyRoot = 60; - uint8 resonance = 0; // 0...40dB - InstrFilterMode filterType = FLTMODE_UNCHANGED; - uint8 polyphony = 255; - bool useSampleKeyRoot = false; - bool invertPhase = false; - - template - static void Read(const std::string &valueStr, T &value, Tc valueMin = std::numeric_limits::min(), Tc valueMax = std::numeric_limits::max()) - { - double valueF = ConvertStrTo(valueStr); - MPT_CONSTANT_IF(std::numeric_limits::is_integer) - { - valueF = mpt::round(valueF); - } - Limit(valueF, static_cast(valueMin), static_cast(valueMax)); - value = static_cast(valueF); - } - - static uint8 ReadKey(const std::string &value, const SFZControl &control) - { - if(value.empty()) - return 0; - - int key = 0; - if(value[0] >= '0' && value[0] <= '9') - { - // MIDI key - key = ConvertStrTo(value); - } else if(value.length() < 2) - { - return 0; - } else - { - // Scientific pitch - static const int8 keys[] = { 9, 11, 0, 2, 4, 5, 7 }; - STATIC_ASSERT(CountOf(keys) == 'g' - 'a' + 1); - auto keyC = value[0]; - if(keyC >= 'A' && keyC <= 'G') - key = keys[keyC - 'A']; - if(keyC >= 'a' && keyC <= 'g') - key = keys[keyC - 'a']; - else - return 0; - - uint8 octaveOffset = 1; - if(value[1] == '#') - { - key++; - octaveOffset = 2; - } else if(value[1] == 'b' || value[1] == 'B') - { - key--; - octaveOffset = 2; - } - if(octaveOffset >= value.length()) - return 0; - - int8 octave = ConvertStrTo(value.c_str() + octaveOffset); - key += (octave + 1) * 12; - } - key += control.octaveOffset * 12 + control.noteOffset; - return static_cast(Clamp(key, 0, 127)); -} - - void Parse(const std::string &key, const std::string &value, const SFZControl &control) - { - if(key == "sample") - filename = control.defaultPath + value; - else if(key == "region_label") - name = value; - else if(key == "lokey") - keyLo = ReadKey(value, control); - else if(key == "hikey") - keyHi = ReadKey(value, control); - else if(key == "pitch_keycenter") - { - keyRoot = ReadKey(value, control); - useSampleKeyRoot = (value == "sample"); - } - else if(key == "key") - { - keyLo = keyHi = keyRoot = ReadKey(value, control); - useSampleKeyRoot = false; - } - else if(key == "bend_up" || key == "bendup") - Read(value, pitchBend, -9600, 9600); - else if(key == "pitchlfo_fade") - Read(value, pitchLfoFade, 0.0f, 100.0f); - else if(key == "pitchlfo_depth") - Read(value, pitchLfoDepth, -12000, 12000); - else if(key == "pitchlfo_freq") - Read(value, pitchLfoFreq, 0, 20); - else if(key == "volume") - Read(value, volume, -144, 6); - else if(key == "pan") - Read(value, panning, -100, 100); - else if(key == "transpose") - Read(value, transpose, -127, 127); - else if(key == "tune") - Read(value, finetune, -100, 100); - else if(key == "end") - Read(value, end, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "offset") - Read(value, offset, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "loop_start" || key == "loopstart") - Read(value, loopStart, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "loop_end" || key == "loopend") - Read(value, loopEnd, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "loop_crossfade") - Read(value, loopCrossfade, 0.0, DBL_MAX); - else if(key == "loop_mode" || key == "loopmode") - { - if(value == "loop_continuous") - loopMode = LoopMode::kContinuous; - else if(value == "one_shot") - loopMode = LoopMode::kOneShot; - else if(value == "loop_sustain") - loopMode = LoopMode::kSustain; - else if(value == "no_loop") - loopMode = LoopMode::kNoLoop; - } - else if(key == "loop_type" || key == "looptype") - { - if(value == "forward") - loopType = LoopType::kForward; - else if(value == "backward") - loopType = LoopType::kBackward; - else if(value == "alternate") - loopType = LoopType::kAlternate; - } - else if(key == "cutoff") - Read(value, cutoff, 0, 96000); - else if(key == "fil_random") - Read(value, filterRandom, 0, 9600); - else if(key == "resonance") - Read(value, resonance, 0u, 40u); - else if(key == "polyphony") - Read(value, polyphony, 0u, 255u); - else if(key == "phase") - invertPhase = (value == "invert"); - else if(key == "fil_type" || key == "filtype") - { - if(value == "lpf_1p" || value == "lpf_2p" || value == "lpf_4p" || value == "lpf_6p") - filterType = FLTMODE_LOWPASS; - else if(value == "hpf_1p" || value == "hpf_2p" || value == "hpf_4p" || value == "hpf_6p") - filterType = FLTMODE_HIGHPASS; - // Alternatives: bpf_2p, brf_2p - } - else if(key.substr(0, 6) == "ampeg_") - ampEnv.Parse(key, value); - else if(key.substr(0, 6) == "fileg_") - filterEnv.Parse(key, value); - else if(key.substr(0, 8) == "pitcheg_") - pitchEnv.Parse(key, value); - } -}; - -bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) -{ - file.Rewind(); - - enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kCurve, kUnknown } section = kNone; - SFZControl control; - SFZRegion group, master, globals; - std::vector regions; - std::map macros; - - std::string s; - while(file.ReadLine(s, 1024)) - { - // First, terminate line at the start of a comment block - auto commentPos = s.find("//"); - if(commentPos != std::string::npos) - { - s.resize(commentPos); - } - - // Now, read the tokens. - // This format is so funky that no general tokenizer approach seems to work here... - // Consider this jolly good example found at https://stackoverflow.com/questions/5923895/tokenizing-a-custom-text-file-format-file-using-c-sharp - // sample=piano C3.wav key=48 ampeg_release=0.7 // a comment here - // key = 49 sample = piano Db3.wav - // - // group=1 - // key = 48 - // sample = piano D3.ogg - // The original sfz specification claims that spaces around = are not allowed, but a quick look into the real world tells us otherwise. - - while(!s.empty()) - { - s.erase(0, s.find_first_not_of(" \t")); - - // Replace macros - for(const auto &m : macros) - { - auto &oldStr = m.first; - auto &newStr = m.second; - std::string::size_type pos = 0; - while((pos = s.find(oldStr, pos)) != std::string::npos) - { - s.replace(pos, oldStr.length(), newStr); - pos += newStr.length(); - } - } - - if(s.empty()) - { - break; - } - - std::string::size_type charsRead = 0; - - if(s[0] == '<' && (charsRead = s.find('>')) != std::string::npos) - { - // Section header - std::string sec = s.substr(1, charsRead - 1); - section = kUnknown; - if(sec == "global") - { - section = kGlobal; - // Reset global parameters - globals = SFZRegion(); - } else if(sec == "master") - { - section = kMaster; - // Reset master parameters - master = globals; - } else if(sec == "group") - { - section = kGroup; - // Reset group parameters - group = master; - } else if(sec == "region") - { - section = kRegion; - regions.push_back(group); - } else if(sec == "control") - { - section = kControl; - } else if(sec == "curve") - { - section = kCurve; - } - charsRead++; - } else if(s.substr(0, 8) == "#define " || s.substr(0, 8) == "#define\t") - { - // Macro definition - auto keyStart = s.find_first_not_of(" \t", 8); - auto keyEnd = s.find_first_of(" \t", keyStart); - auto valueStart = s.find_first_not_of(" \t", keyEnd); - std::string key = s.substr(keyStart, keyEnd - keyStart); - if(valueStart != std::string::npos && key.length() > 1 && key[0] == '$') - { - charsRead = s.find_first_of(" \t", valueStart); - macros[key] = s.substr(valueStart, charsRead - valueStart); - } - } else if(s.substr(0, 9) == "#include " || s.substr(0, 9) == "#include\t") - { - 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) - { - charsRead = fileEnd + 1; - } else - { - return false; - } - } else if(section == kNone) - { - // Garbage before any section, probably not an sfz file - return false; - } else if(s.find('=') != std::string::npos) - { - // Read key=value pair - auto keyEnd = s.find_first_of(" \t="); - auto valueStart = s.find_first_not_of(" \t=", keyEnd); - std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd)); - if(key == "sample" || key == "default_path" || key.substr(0, 8) == "label_cc" || key.substr(0, 12) == "region_label") - { - // Sample / CC name may contain spaces... - charsRead = s.find_first_of("=\t<", valueStart); - if(charsRead != std::string::npos && s[charsRead] == '=') - { - // Backtrack to end of key - while(charsRead > valueStart && s[charsRead] == ' ') - charsRead--; - // Backtrack to start of key - while(charsRead > valueStart && s[charsRead] != ' ') - charsRead--; - } - } else - { - charsRead = s.find_first_of(" \t<", valueStart); - } - std::string value = s.substr(valueStart, charsRead - valueStart); - - switch(section) - { - case kGlobal: - globals.Parse(key, value, control); - MPT_FALLTHROUGH; - case kMaster: - master.Parse(key, value, control); - MPT_FALLTHROUGH; - case kGroup: - group.Parse(key, value, control); - break; - case kRegion: - regions.back().Parse(key, value, control); - break; - case kControl: - control.Parse(key, value); - break; - } - } else - { - // Garbage, probably not an sfz file - MPT_ASSERT(false); - return false; - } - - // Remove the token(s) we just read - s.erase(0, charsRead); - } - } - - if(regions.empty()) - { - return false; - } - - - ModInstrument *pIns = new (std::nothrow) ModInstrument(); - if(pIns == nullptr) - { - return false; - } - - RecalculateSamplesPerTick(); - DestroyInstrument(nInstr, deleteAssociatedSamples); - if(nInstr > m_nInstruments) m_nInstruments = nInstr; - Instruments[nInstr] = pIns; - - SAMPLEINDEX prevSmp = 0; - for(auto ®ion : regions) - { - uint8 keyLo = region.keyLo, keyHi = region.keyHi; - if(keyLo > keyHi) - continue; - Clamp(keyLo, 0, NOTE_MAX - NOTE_MIN); - Clamp(keyHi, 0, NOTE_MAX - NOTE_MIN); - SAMPLEINDEX smp = GetNextFreeSample(nInstr, prevSmp + 1); - if(smp == SAMPLEINDEX_INVALID) - break; - prevSmp = smp; - - ModSample &sample = Samples[smp]; - mpt::PathString filename = mpt::PathString::FromUTF8(region.filename); - if(!filename.empty()) - { - if(region.filename.find(':') == std::string::npos) - { - filename = file.GetFileName().GetPath() + filename; - } - SetSamplePath(smp, filename); - InputFile f(filename); - FileReader smpFile = GetFileReader(f); - if(!ReadSampleFromFile(smp, smpFile, false)) - { - AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); - prevSmp--; - continue; - } - if(!region.name.empty()) - { - mpt::String::Copy(m_szNames[smp], mpt::ToCharset(GetCharsetInternal(), mpt::CharsetUTF8, region.name)); - } - if(!m_szNames[smp][0]) - { - mpt::String::Copy(m_szNames[smp], filename.GetFileName().ToLocale()); - } - } - sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData()); - - if(region.useSampleKeyRoot) - { - if(sample.rootNote != NOTE_NONE) - region.keyRoot = sample.rootNote - NOTE_MIN; - else - region.keyRoot = 60; - } - - const auto origSampleRate = sample.GetSampleRate(GetType()); - int8 transp = region.transpose + (60 - region.keyRoot); - for(uint8 i = keyLo; i <= keyHi; i++) - { - pIns->Keyboard[i] = smp; - if(GetType() != MOD_TYPE_XM) - pIns->NoteMap[i] = NOTE_MIN + i + transp; - } - if(GetType() == MOD_TYPE_XM) - sample.Transpose(transp / 12.0); - - pIns->nFilterMode = region.filterType; - if(region.cutoff != 0) - pIns->SetCutoff(FrequencyToCutOff(region.cutoff), true); - if(region.resonance != 0) - pIns->SetResonance(mpt::saturate_cast(Util::muldivr(region.resonance, 128, 24)), true); - pIns->nCutSwing = mpt::saturate_cast(Util::muldivr(region.filterRandom, m_SongFlags[SONG_EXFILTERRANGE] ? 20 : 24, 1200)); - pIns->midiPWD = static_cast(region.pitchBend / 100); - - pIns->nNNA = NNA_NOTEOFF; - if(region.polyphony == 1) - { - pIns->nDNA = DNA_NOTECUT; - pIns->nDCT = DCT_SAMPLE; - } - region.ampEnv.ConvertToMPT(pIns, *this, ENV_VOLUME); - region.pitchEnv.ConvertToMPT(pIns, *this, ENV_PITCH); - //region.filterEnv.ConvertToMPT(pIns, *this, ENV_PITCH); - - sample.rootNote = region.keyRoot + NOTE_MIN; - 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)); - sample.uFlags.set(CHN_PANNING); - } - sample.Transpose(region.finetune / 1200.0); - - if(region.pitchLfoDepth && region.pitchLfoFreq) - { - sample.nVibSweep = 255; - if(region.pitchLfoFade > 0) - sample.nVibSweep = mpt::saturate_round(255 / region.pitchLfoFade); - sample.nVibDepth = static_cast(Util::muldivr(region.pitchLfoDepth, 32, 100)); - sample.nVibRate = region.pitchLfoFreq * 4; - } - - if(region.loopMode != SFZRegion::LoopMode::kUnspecified) - { - switch(region.loopMode) - { - case SFZRegion::LoopMode::kContinuous: - case SFZRegion::LoopMode::kOneShot: - sample.uFlags.set(CHN_LOOP); - break; - case SFZRegion::LoopMode::kSustain: - sample.uFlags.set(CHN_SUSTAINLOOP); - break; - case SFZRegion::LoopMode::kNoLoop: - sample.uFlags.reset(CHN_LOOP | CHN_SUSTAINLOOP); - } - } - if(region.loopEnd > region.loopStart) - { - // Loop may also be defined in file, in which case loopStart and loopEnd are unset. - if(region.loopMode == SFZRegion::LoopMode::kSustain) - { - sample.nSustainStart = region.loopStart; - sample.nSustainEnd = region.loopEnd + 1; - } else if(region.loopMode == SFZRegion::LoopMode::kContinuous || region.loopMode == SFZRegion::LoopMode::kOneShot) - { - sample.nLoopStart = region.loopStart; - sample.nLoopEnd = region.loopEnd + 1; - } - } else if(sample.nLoopEnd <= sample.nLoopStart && region.loopMode != SFZRegion::LoopMode::kUnspecified && region.loopMode != SFZRegion::LoopMode::kNoLoop) - { - sample.nLoopEnd = sample.nLength; - } - switch(region.loopType) - { - case SFZRegion::LoopType::kUnspecified: - break; - case SFZRegion::LoopType::kForward: - sample.uFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_REVERSE); - break; - case SFZRegion::LoopType::kBackward: - sample.uFlags.set(CHN_REVERSE); - break; - case SFZRegion::LoopType::kAlternate: - sample.uFlags.set(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN); - break; - default: - break; - } - if(sample.nSustainEnd <= sample.nSustainStart && sample.nLoopEnd > sample.nLoopStart && region.loopMode == SFZRegion::LoopMode::kSustain) - { - // Turn normal loop (imported from sample) into sustain loop - std::swap(sample.nSustainStart, sample.nLoopStart); - std::swap(sample.nSustainEnd, sample.nLoopEnd); - sample.uFlags.set(CHN_SUSTAINLOOP); - sample.uFlags.set(CHN_PINGPONGSUSTAIN, sample.uFlags[CHN_PINGPONGLOOP]); - sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); - } - - // Loop cross-fade - SmpLength fadeSamples = mpt::saturate_round(region.loopCrossfade * origSampleRate); - LimitMax(fadeSamples, sample.uFlags[CHN_SUSTAINLOOP] ? sample.nSustainStart : sample.nLoopStart); - if(fadeSamples > 0) - { - ctrlSmp::XFadeSample(sample, fadeSamples, 50000, true, sample.uFlags[CHN_SUSTAINLOOP], *this); - sample.uFlags.set(SMP_MODIFIED); - } - - // Sample offset - if(region.offset && region.offset < sample.nLength) - { - auto offset = region.offset * sample.GetBytesPerSample(); - memmove(sample.sampleb(), sample.sampleb() + offset, sample.nLength * sample.GetBytesPerSample() - offset); - if(region.end > region.offset) - region.end -= region.offset; - sample.nLength -= region.offset; - sample.nLoopStart -= region.offset; - sample.nLoopEnd -= region.offset; - sample.uFlags.set(SMP_MODIFIED); - } - LimitMax(sample.nLength, region.end); - - if(region.invertPhase) - { - ctrlSmp::InvertSample(sample, 0, sample.nLength, *this); - sample.uFlags.set(SMP_MODIFIED); - } - - sample.PrecomputeLoops(*this, false); - sample.Convert(MOD_TYPE_MPT, GetType()); - } - - pIns->Sanitize(MOD_TYPE_MPT); - pIns->Convert(MOD_TYPE_MPT, GetType()); - return true; -} - -#ifndef MODPLUG_NO_FILESAVE - -static double SFZLinear2dB(double volume) -{ - return (volume > 0.0 ? 20.0 * std::log10(volume) : -144.0); -} - -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 &) -{ - return false; -} -#endif // MPT_EXTERNAL_SAMPLES - - - /////////////// // Apple CAF // @@ -2394,7 +1541,7 @@ static void CAFSetTagFromInfoKey(mpt::ustring & dst, const std::mapsecond); + dst = mpt::ToUnicode(mpt::Charset::UTF8, item->second); } @@ -2591,7 +1738,7 @@ bool CSoundFile::ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo sampleIO.ReadSample(mptSample, audioData); - mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); @@ -2883,7 +2030,7 @@ bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayN nameChunk.ReadString(m_szNames[nSample], nameChunk.GetLength()); } else { - strcpy(m_szNames[nSample], ""); + m_szNames[nSample] = ""; } mptSample.Convert(MOD_TYPE_IT, GetType()); @@ -2892,7 +2039,7 @@ bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayN } -static bool AUIsAnnotationLineWithField(const std::string & line) +static bool AUIsAnnotationLineWithField(const std::string &line) { std::size_t pos = line.find('='); if(pos == std::string::npos) @@ -2903,23 +2050,19 @@ static bool AUIsAnnotationLineWithField(const std::string & line) { return false; } - std::string field = line.substr(0, pos); - bool invalidChars = false; + const auto field = std::string_view(line).substr(0, pos); + // Scan for invalid chars for(auto c : field) { if(!IsInRange(c, 'a', 'z') && !IsInRange(c, 'A', 'Z') && !IsInRange(c, '0', '9') && c != '-' && c != '_') { - invalidChars = true; + return false; } } - if(invalidChars) - { - return false; - } return true; } -static std::string AUTrimFieldFromAnnotationLine(const std::string & line) +static std::string AUTrimFieldFromAnnotationLine(const std::string &line) { if(!AUIsAnnotationLineWithField(line)) { @@ -2929,7 +2072,7 @@ static std::string AUTrimFieldFromAnnotationLine(const std::string & line) return line.substr(pos + 1); } -static std::string AUGetAnnotationFieldFromLine(const std::string & line) +static std::string AUGetAnnotationFieldFromLine(const std::string &line) { if(!AUIsAnnotationLineWithField(line)) { @@ -2944,14 +2087,19 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor file.Rewind(); // Verify header - if(!file.ReadMagic(".snd")) + const auto magic = file.ReadArray(); + const bool bigEndian = !std::memcmp(magic.data(), ".snd", 4); + const bool littleEndian = !std::memcmp(magic.data(), "dns.", 4); + if(!bigEndian && !littleEndian) return false; - 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(); + auto readUint32 = std::bind(bigEndian ? &FileReader::ReadUint32BE : &FileReader::ReadUint32LE, file); + + uint32 dataOffset = readUint32(); // must be divisible by 8 according to spec, however, there are files that ignore this requirement + uint32 dataSize = readUint32(); + uint32 encoding = readUint32(); + uint32 sampleRate = readUint32(); + uint32 channels = readUint32(); // According to spec, a minimum 8 byte annotation field after the header fields is required, // however, there are files in the wild that violate this requirement. @@ -2964,22 +2112,22 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor if(channels < 1 || channels > 2) return false; - SampleIO sampleIO(SampleIO::_8bit, channels == 1 ? SampleIO::mono : SampleIO::stereoInterleaved, SampleIO::bigEndian, SampleIO::signedPCM); + SampleIO sampleIO(SampleIO::_8bit, channels == 1 ? SampleIO::mono : SampleIO::stereoInterleaved, bigEndian ? SampleIO::bigEndian : SampleIO::littleEndian, SampleIO::signedPCM); switch(encoding) { - case 1: sampleIO |= SampleIO::_16bit; // u-law + case 1: sampleIO |= SampleIO::_16bit; // u-law sampleIO |= SampleIO::uLaw; break; - case 2: break; // 8-bit linear PCM - case 3: sampleIO |= SampleIO::_16bit; break; // 16-bit linear PCM - case 4: sampleIO |= SampleIO::_24bit; break; // 24-bit linear PCM - case 5: sampleIO |= SampleIO::_32bit; break; // 32-bit linear PCM - case 6: sampleIO |= SampleIO::_32bit; // 32-bit IEEE floating point + case 2: break; // 8-bit linear PCM + case 3: sampleIO |= SampleIO::_16bit; break; // 16-bit linear PCM + case 4: sampleIO |= SampleIO::_24bit; break; // 24-bit linear PCM + case 5: sampleIO |= SampleIO::_32bit; break; // 32-bit linear PCM + case 6: sampleIO |= SampleIO::_32bit; // 32-bit IEEE floating point sampleIO |= SampleIO::floatPCM; break; - case 7: sampleIO |= SampleIO::_64bit; // 64-bit IEEE floating point + case 7: sampleIO |= SampleIO::_64bit; // 64-bit IEEE floating point sampleIO |= SampleIO::floatPCM; break; - case 27: sampleIO |= SampleIO::_16bit; // a-law + case 27: sampleIO |= SampleIO::_16bit; // a-law sampleIO |= SampleIO::aLaw; break; default: return false; } @@ -2993,46 +2141,41 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor // This reads annotation metadata as written by OpenMPT, sox, ffmpeg. // Additionally, we fall back to just reading the whole field as a single comment. + // We only read up to the first \0 byte. file.Seek(24); - std::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); - } + std::string annotation; + file.ReadString(annotation, dataOffset - 24); 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) + mpt::Charset charset = mpt::IsUTF8(annotation) ? mpt::Charset::UTF8 : mpt::Charset::ISO8859_1; + const auto lines = mpt::String::Split(annotation, "\n"); + bool hasFields = false; + for(const auto &line : lines) { if(AUIsAnnotationLineWithField(line)) { - has_fields = true; + hasFields = true; + break; } } - if(has_fields) + if(hasFields) { - std::map> lines_per_field; - std::string last_field = "comment"; - for(const auto & line : lines) + std::map> linesPerField; + std::string lastField = "comment"; + for(const auto &line : lines) { if(AUIsAnnotationLineWithField(line)) { - last_field = mpt::ToLowerCaseAscii(mpt::String::Trim(AUGetAnnotationFieldFromLine(line))); + lastField = mpt::ToLowerCaseAscii(mpt::String::Trim(AUGetAnnotationFieldFromLine(line))); } - lines_per_field[last_field].push_back(AUTrimFieldFromAnnotationLine(line)); + linesPerField[lastField].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"))); + tags.title = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["title" ], std::string("\n"))); + tags.artist = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["artist" ], std::string("\n"))); + tags.album = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["album" ], std::string("\n"))); + tags.trackno = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["track" ], std::string("\n"))); + tags.genre = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["genre" ], std::string("\n"))); + tags.comments = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["comment"], std::string("\n"))); } else { // Most applications tend to write their own name here, @@ -3051,7 +2194,7 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor LimitMax(length, dataSize); mptSample.nLength = (length * 8u) / (sampleIO.GetEncodedBitsPerSample() * channels); mptSample.nC5Speed = sampleRate; - mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); if(mayNormalize) { @@ -3087,7 +2230,7 @@ bool CSoundFile::ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewin ModSample &sample = Samples[nSample]; file.Seek(sampleHeader.ConvertToMPT(sample)); - mpt::String::Read(m_szNames[nSample], sampleHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePaddedNull, sampleHeader.name); if(sample.uFlags[CHN_ADLIB]) { @@ -3282,7 +2425,7 @@ bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons itss.ConvertToIT(Samples[smp], GetType(), compress, compress, allowExternal); const bool isExternal = itss.cvt == ITSample::cvtExternalSample; - mpt::String::Write(itss.name, m_szNames[smp]); + mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp]; itss.samplepointer = filePos; mpt::IO::Write(f, itss); @@ -3423,7 +2566,7 @@ bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file) nameChunk.ReadString(m_szNames[nSample], nameChunk.GetLength()); } else { - strcpy(m_szNames[nSample], ""); + m_szNames[nSample] = ""; } sample.nLength = mpt::saturate_cast(bodyChunk.GetLength() / bytesPerSample); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp index f7cf942f7..671cad111 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp @@ -44,7 +44,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const FileReader::off_t bytesRead = 0; // Amount of memory that has been read from file FileReader::off_t filePosition = file.GetPosition(); - const mpt::byte * sourceBuf = nullptr; + const std::byte * sourceBuf = nullptr; FileReader::PinnedRawDataView restrictedSampleDataView; FileReader::off_t fileSize = 0; if(UsesFileReaderForDecoding()) @@ -232,7 +232,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const } else if((GetEncoding() == uLaw || GetEncoding() == aLaw) && GetBitDepth() == 16 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved)) { // 8-to-16 bit G.711 u-law / a-law - static const int16 uLawTable[256] = + static constexpr int16 uLawTable[256] = { -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, @@ -268,7 +268,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const 56, 48, 40, 32, 24, 16, 8, 0, }; - static const int16 aLawTable[256] = + static constexpr int16 aLawTable[256] = { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, @@ -861,7 +861,7 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength return 0; } - std::array writeBuffer; + std::array writeBuffer; mpt::IO::WriteBuffer fb{f, mpt::as_span(writeBuffer)}; SmpLength numSamples = sample.nLength; @@ -962,7 +962,7 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength // Stereo signed interleaved MPT_ASSERT(len == numSamples * 2); const int8 *const pSample8 = sample.sample8(); - mpt::IO::WriteRaw(f, reinterpret_cast(pSample8), len); + mpt::IO::WriteRaw(f, reinterpret_cast(pSample8), len); } else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM && GetEndianness() == littleEndian) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h index 48014021b..b5412afe5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h @@ -119,7 +119,7 @@ public: void MayNormalize() { - if(GetBitDepth() == 24 || GetBitDepth() == 32) + if(GetBitDepth() >= 24) { if(GetEncoding() == SampleIO::signedPCM) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h index 02f21d07f..0dbb9db52 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h @@ -19,25 +19,25 @@ OPENMPT_NAMESPACE_BEGIN -typedef uint32 ROWINDEX; +using ROWINDEX = uint32; const ROWINDEX ROWINDEX_INVALID = uint32_max; -typedef uint16 CHANNELINDEX; +using CHANNELINDEX = uint16; const CHANNELINDEX CHANNELINDEX_INVALID = uint16_max; -typedef uint16 ORDERINDEX; +using ORDERINDEX = uint16; const ORDERINDEX ORDERINDEX_INVALID = uint16_max; const ORDERINDEX ORDERINDEX_MAX = uint16_max - 1; -typedef uint16 PATTERNINDEX; +using PATTERNINDEX = uint16; const PATTERNINDEX PATTERNINDEX_INVALID = uint16_max; -typedef uint8 PLUGINDEX; +using PLUGINDEX = uint8; const PLUGINDEX PLUGINDEX_INVALID = uint8_max; -typedef uint16 SAMPLEINDEX; +using SAMPLEINDEX = uint16; const SAMPLEINDEX SAMPLEINDEX_INVALID = uint16_max; -typedef uint16 INSTRUMENTINDEX; +using INSTRUMENTINDEX = uint16; const INSTRUMENTINDEX INSTRUMENTINDEX_INVALID = uint16_max; -typedef uint8 SEQUENCEINDEX; +using SEQUENCEINDEX = uint8; const SEQUENCEINDEX SEQUENCEINDEX_INVALID = uint8_max; -typedef uint32 SmpLength; +using SmpLength = uint32; const SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in frames. Sample size in bytes can be more than this (= 256 MB). @@ -54,15 +54,18 @@ const SEQUENCEINDEX MAX_SEQUENCES = 50; 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() +enum { 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 +enum +{ + MAX_SAMPLENAME = 32, + MAX_SAMPLEFILENAME = 22, + MAX_INSTRUMENTNAME = 32, + MAX_INSTRUMENTFILENAME = 32, + MAX_PATTERNNAME = 32, + MAX_CHANNELNAME = 20, +}; enum MODTYPE { @@ -160,7 +163,7 @@ DECLARE_FLAGSET(ChannelFlags) #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; +using SampleFlags = FlagSet; // Instrument envelope-specific flags @@ -202,11 +205,11 @@ enum EnvelopeType : uint8 }; // Filter Modes -enum InstrFilterMode : uint8 +enum class FilterMode : uint8 { - FLTMODE_UNCHANGED = 0xFF, - FLTMODE_LOWPASS = 0, - FLTMODE_HIGHPASS = 1, + Unchanged = 0xFF, + LowPass = 0, + HighPass = 1, }; @@ -274,6 +277,7 @@ DECLARE_FLAGSET(SongFlags) #ifndef NO_DSP #define SNDDSP_MEGABASS 0x02 // Bass expansion #define SNDDSP_SURROUND 0x08 // Surround mix +#define SNDDSP_BITCRUSH 0x01 #endif // NO_DSP #ifndef NO_REVERB #define SNDDSP_REVERB 0x20 // Apply reverb @@ -310,22 +314,30 @@ enum ResamplingMode : uint8 namespace Resampling { +enum class AmigaFilter +{ + Off = 0, + A500 = 1, + A1200 = 2, + Unfiltered = 3, +}; + 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 constexpr ResamplingMode Default() noexcept { return SRCMODE_SINC8LP; } -static MPT_CONSTEXPR11_FUN bool IsKnownMode(int mode) noexcept { return (mode >= 0) && (mode < SRCMODE_DEFAULT); } +static constexpr bool IsKnownMode(int mode) noexcept { return (mode >= 0) && (mode < SRCMODE_DEFAULT); } -static MPT_CONSTEXPR11_FUN ResamplingMode ToKnownMode(int mode) noexcept +static constexpr 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 +static constexpr int Length(ResamplingMode mode) noexcept { return mode == SRCMODE_NEAREST ? 1 : mode == SRCMODE_LINEAR ? 2 @@ -335,11 +347,11 @@ static MPT_CONSTEXPR11_FUN int Length(ResamplingMode mode) noexcept : 0; } -static MPT_CONSTEXPR11_FUN bool HasAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP); } +static constexpr 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 constexpr 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; } +static constexpr ResamplingMode RemoveAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP) ? SRCMODE_SINC8 : mode; } } @@ -348,7 +360,7 @@ static MPT_CONSTEXPR11_FUN ResamplingMode RemoveAA(ResamplingMode mode) noexcept // Release node defines #define ENV_RELEASE_NODE_UNSET 0xFF #define NOT_YET_RELEASED (-1) -STATIC_ASSERT(ENV_RELEASE_NODE_UNSET > MAX_ENVPOINTS); +static_assert(ENV_RELEASE_NODE_UNSET > MAX_ENVPOINTS); enum PluginPriority @@ -447,7 +459,7 @@ enum PlayBehaviour 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 + kITFT2DontResetNoteOffOnPorta, // Only reset note-off status on portamento in IT Compatible Gxx mode kITVolColMemory, // IT volume column effects share their memory with the effect column kITPortamentoSwapResetsPos, // Portamento with sample swap plays the new sample from the beginning kITEmptyNoteMapSlot, // IT ignores instrument note map entries with no note completely @@ -456,7 +468,7 @@ enum PlayBehaviour 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 + kITPatternLoopWithJumpsOld, // Bxx on the same row as SBx terminates the loop in IT (old implementation of kITPatternLoopWithJumps) kITInstrWithNoteOff, // Instrument number with note-off recalls default volume kFT2Arpeggio, // FT2 arpeggio algorithm @@ -465,7 +477,7 @@ enum PlayBehaviour 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 + kFT2ST3OffsetOutOfRange, // Offset past sample end stops the note kFT2RestrictXCommand, // Don't allow MPT extensions to Xxx command in XM kFT2RetrigWithNoteDelay, // Retrigger envelopes if there is a note delay with no note kFT2SetPanEnvPos, // Lxx only sets the pan env position if the volume envelope's sustain flag is set @@ -488,7 +500,7 @@ enum PlayBehaviour 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. + kST3PortaSampleChange, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself (GUS behaviour). kST3VibratoMemory, // Do not remember vibrato type in effect memory kST3LimitPeriod, // Cut note instead of limiting final period (ModPlug Tracker style) KST3PortaAfterArpeggio, // Portamento after arpeggio continues at the note where the arpeggio left off @@ -515,6 +527,14 @@ enum PlayBehaviour kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off kITInstrWithNoteOffOldEffects, // Instrument number with note-off recalls default volume - special cases with Old Effects enabled kMIDIVolumeOnNoteOffBug, // Update MIDI channel volume on note-off (legacy bug emulation) + kITDoNotOverrideChannelPan, // Sample / instrument pan does not override channel pan for following samples / instruments that are not panned + kITPatternLoopWithJumps, // Bxx right of SBx terminates the loop in IT + kITDCTBehaviour, // DCT="Sample" requires sample instrument, DCT="Note" checks old pattern note against new pattern note (previously was checking old pattern note against new translated note) + kOPLwithNNA, // NNA note-off / fade are applied to OPL channels + kST3RetrigAfterNoteCut, // Qxy does not retrigger note after it has been cut with ^^^ or SCx + kST3SampleSwap, // On-the-fly sample swapping (SoundBlaster behaviour) + kOPLRealRetrig, // Retrigger effect (Qxy) restarts OPL notes + kOPLNoResetAtEnvelopeEnd, // Do not reset OPL channel status at end of envelope (OpenMPT 1.28 inconsistency with samples) // Add new play behaviours here. @@ -539,8 +559,8 @@ public: // Sample position and sample position increment value struct SamplePosition { - typedef int64 value_t; - typedef uint64 unsigned_value_t; + using value_t = int64; + using unsigned_value_t = uint64; protected: value_t v = 0; @@ -548,40 +568,40 @@ protected: public: enum : uint32 { fractMax = 0xFFFFFFFFu }; - SamplePosition() { } - explicit SamplePosition(value_t pos) : v(pos) { } - SamplePosition(int32 intPart, uint32 fractPart) : v((static_cast(intPart) * (1ll<<32)) | fractPart) { } + MPT_CONSTEXPR11_FUN SamplePosition() { } + MPT_CONSTEXPR11_FUN explicit SamplePosition(value_t pos) : v(pos) { } + MPT_CONSTEXPR11_FUN SamplePosition(int32 intPart, uint32 fractPart) : v((static_cast(intPart) * (1ll << 32)) | fractPart) { } static SamplePosition Ratio(uint32 dividend, uint32 divisor) { return SamplePosition((static_cast(dividend) << 32) / divisor); } static SamplePosition FromDouble(double pos) { return SamplePosition(static_cast(pos * 4294967296.0)); } // Set integer and fractional part - MPT_FORCEINLINE SamplePosition &Set(int32 intPart, uint32 fractPart = 0) { v = (static_cast(intPart) << 32) | fractPart; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &Set(int32 intPart, uint32 fractPart = 0) { v = (static_cast(intPart) << 32) | fractPart; return *this; } // Set integer part, keep fractional part - MPT_FORCEINLINE SamplePosition &SetInt(int32 intPart) { v = (static_cast(intPart) << 32) | GetFract(); return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &SetInt(int32 intPart) { v = (static_cast(intPart) << 32) | GetFract(); return *this; } // Get integer part (as sample length / position) - MPT_FORCEINLINE SmpLength GetUInt() const { return static_cast(static_cast(v) >> 32); } + MPT_CONSTEXPR11_FUN SmpLength GetUInt() const { return static_cast(static_cast(v) >> 32); } // Get integer part - MPT_FORCEINLINE int32 GetInt() const { return static_cast(static_cast(v) >> 32); } + MPT_CONSTEXPR11_FUN int32 GetInt() const { return static_cast(static_cast(v) >> 32); } // Get fractional part - MPT_FORCEINLINE uint32 GetFract() const { return static_cast(v); } + MPT_CONSTEXPR11_FUN uint32 GetFract() const { return static_cast(v); } // Get the inverted fractional part - MPT_FORCEINLINE SamplePosition GetInvertedFract() const { return SamplePosition(0x100000000ll - GetFract()); } + MPT_CONSTEXPR11_FUN SamplePosition GetInvertedFract() const { return SamplePosition(0x100000000ll - GetFract()); } // Get the raw fixed-point value - MPT_FORCEINLINE int64 GetRaw() const { return v; } + MPT_CONSTEXPR11_FUN int64 GetRaw() const { return v; } // Negate the current value - MPT_FORCEINLINE SamplePosition &Negate() { v = -v; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &Negate() { v = -v; return *this; } // Multiply and divide by given integer scalars - MPT_FORCEINLINE SamplePosition &MulDiv(uint32 mul, uint32 div) { v = (v * mul) / div; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &MulDiv(uint32 mul, uint32 div) { v = (v * mul) / div; return *this; } // Removes the integer part, only keeping fractions - MPT_FORCEINLINE SamplePosition &RemoveInt() { v &= fractMax; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &RemoveInt() { v &= fractMax; return *this; } // Check if value is 1.0 - MPT_FORCEINLINE bool IsUnity() const { return v == 0x100000000ll; } + MPT_CONSTEXPR11_FUN bool IsUnity() const { return v == 0x100000000ll; } // Check if value is 0 - MPT_FORCEINLINE bool IsZero() const { return v == 0; } + MPT_CONSTEXPR11_FUN bool IsZero() const { return v == 0; } // Check if value is > 0 - MPT_FORCEINLINE bool IsPositive() const { return v > 0; } + MPT_CONSTEXPR11_FUN bool IsPositive() const { return v > 0; } // Check if value is < 0 - MPT_FORCEINLINE bool IsNegative() const { return v < 0; } + MPT_CONSTEXPR11_FUN bool IsNegative() const { return v < 0; } // Addition / subtraction of another fixed-point number SamplePosition operator+ (const SamplePosition &other) const { return SamplePosition(v + other.v); } @@ -600,12 +620,12 @@ public: // Division by scalar; returns fractional point number SamplePosition operator/ (int div) const { return SamplePosition(v / div); } - bool operator== (const SamplePosition &other) const { return v == other.v; } - bool operator!= (const SamplePosition &other) const { return v != other.v; } - bool operator<= (const SamplePosition &other) const { return v <= other.v; } - bool operator>= (const SamplePosition &other) const { return v >= other.v; } - bool operator< (const SamplePosition &other) const { return v < other.v; } - bool operator> (const SamplePosition &other) const { return v > other.v; } + MPT_CONSTEXPR11_FUN bool operator==(const SamplePosition &other) const { return v == other.v; } + MPT_CONSTEXPR11_FUN bool operator!=(const SamplePosition &other) const { return v != other.v; } + MPT_CONSTEXPR11_FUN bool operator<=(const SamplePosition &other) const { return v <= other.v; } + MPT_CONSTEXPR11_FUN bool operator>=(const SamplePosition &other) const { return v >= other.v; } + MPT_CONSTEXPR11_FUN bool operator<(const SamplePosition &other) const { return v < other.v; } + MPT_CONSTEXPR11_FUN bool operator>(const SamplePosition &other) const { return v > other.v; } }; @@ -623,7 +643,7 @@ protected: public: enum : size_t { fractFact = FFact }; - typedef T store_t; + using store_t = T; MPT_CONSTEXPR11_FUN FPInt() : v(0) { } MPT_CONSTEXPR11_FUN FPInt(T intPart, T fractPart) : v((intPart * fractFact) + (fractPart % fractFact)) { } @@ -643,21 +663,21 @@ public: // Formats the stored value as a floating-point value MPT_CONSTEXPR11_FUN double ToDouble() const { return v / double(fractFact); } - MPT_CONSTEXPR11_FUN FPInt operator+ (const FPInt &other) const { return FPInt(v + other.v); } - MPT_CONSTEXPR11_FUN FPInt operator- (const FPInt &other) const { return FPInt(v - other.v); } - MPT_CONSTEXPR14_FUN FPInt operator+= (const FPInt &other) { v += other.v; return *this; } - MPT_CONSTEXPR14_FUN FPInt operator-= (const FPInt &other) { v -= other.v; return *this; } + MPT_CONSTEXPR11_FUN friend FPInt operator+ (const FPInt &a, const FPInt &b) noexcept { return FPInt(a.v + b.v); } + MPT_CONSTEXPR11_FUN friend FPInt operator- (const FPInt &a, const FPInt &b) noexcept { return FPInt(a.v - b.v); } + MPT_CONSTEXPR14_FUN FPInt operator+= (const FPInt &other) noexcept { v += other.v; return *this; } + MPT_CONSTEXPR14_FUN FPInt operator-= (const FPInt &other) noexcept { v -= other.v; return *this; } - MPT_CONSTEXPR11_FUN bool operator== (const FPInt &other) const { return v == other.v; } - MPT_CONSTEXPR11_FUN bool operator!= (const FPInt &other) const { return v != other.v; } - MPT_CONSTEXPR11_FUN bool operator<= (const FPInt &other) const { return v <= other.v; } - MPT_CONSTEXPR11_FUN bool operator>= (const FPInt &other) const { return v >= other.v; } - MPT_CONSTEXPR11_FUN bool operator< (const FPInt &other) const { return v < other.v; } - MPT_CONSTEXPR11_FUN bool operator> (const FPInt &other) const { return v > other.v; } + MPT_CONSTEXPR11_FUN friend bool operator== (const FPInt &a, const FPInt &b) noexcept { return a.v == b.v; } + MPT_CONSTEXPR11_FUN friend bool operator!= (const FPInt &a, const FPInt &b) noexcept { return a.v != b.v; } + MPT_CONSTEXPR11_FUN friend bool operator<= (const FPInt &a, const FPInt &b) noexcept { return a.v <= b.v; } + MPT_CONSTEXPR11_FUN friend bool operator>= (const FPInt &a, const FPInt &b) noexcept { return a.v >= b.v; } + MPT_CONSTEXPR11_FUN friend bool operator< (const FPInt &a, const FPInt &b) noexcept { return a.v < b.v; } + MPT_CONSTEXPR11_FUN friend bool operator> (const FPInt &a, const FPInt &b) noexcept { return a.v > b.v; } }; -typedef FPInt<10000, uint32> TEMPO; +using TEMPO = FPInt<10000, uint32>; -typedef std::array OPLPatch; +using OPLPatch = std::array; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp index 21f735259..6477a7921 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp @@ -81,7 +81,8 @@ int CSoundFile::SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier // Filtering is only ever done in IT if either cutoff is not full or if resonance is set. if(m_playBehaviour[kITFilterBehaviour] && resonance == 0 && computedCutoff >= 254) { - if(chn.rowCommand.IsNote() && !chn.rowCommand.IsPortamento() && !chn.nMasterChn && m_SongFlags[SONG_FIRSTTICK]) + if(chn.rowCommand.IsNote() && !chn.rowCommand.IsPortamento() && !chn.nMasterChn + && chn.position.IsZero() && !chn.dwFlags[CHN_WRAPPED_LOOP]) { // 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 @@ -124,7 +125,7 @@ int CSoundFile::SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier switch(chn.nFilterMode) { - case FLTMODE_HIGHPASS: + case FilterMode::HighPass: chn.nFilter_A0 = FILTER_CONVERT(1.0f - fg); chn.nFilter_B0 = FILTER_CONVERT(fb0); chn.nFilter_B1 = FILTER_CONVERT(fb1); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index 77f86ea1b..eee08c407 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -73,11 +73,11 @@ public: #endif std::vector chnSettings; double elapsedTime; - static const uint32 IGNORE_CHANNEL = uint32_max; + static constexpr uint32 IGNORE_CHANNEL = uint32_max; GetLengthMemory(const CSoundFile &sf) : sndFile(sf) - , state(mpt::make_unique(sf.m_PlayState)) + , state(std::make_unique(sf.m_PlayState)) { Reset(); } @@ -127,22 +127,19 @@ public: if(i >= portaStart) { 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.volcmd == VOLCMD_TONEPORTAMENTO) + const ModCommand &m = *sndFile.Patterns[state->m_nPattern].GetpModCommand(state->m_nRow, channel); + auto command = m.command; + if(m.volcmd == VOLCMD_TONEPORTAMENTO) { - uint32 param = p.vol; - 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 - { - // Close enough. Do not bother with idiosyncratic FT2 behaviour here. - param <<= 4; - } - sndFile.TonePortamento(chn, param); + const auto [porta, clearEffectCommand] = sndFile.GetVolCmdTonePorta(m, 0); + sndFile.TonePortamento(chn, porta); + if(clearEffectCommand) + command = CMD_NONE; } + if(command == CMD_TONEPORTAMENTO) + sndFile.TonePortamento(chn, m.param); + else if(command == CMD_TONEPORTAVOL) + sndFile.TonePortamento(chn, 0); updateInc = true; } @@ -159,6 +156,14 @@ public: if(updateInc || chnSettings[channel].incChanged) { + if(chn.m_CalculateFreq || chn.m_ReCalculateFreqOnFirstTick) + { + chn.RecalcTuningFreq(1, 0, sndFile); + if(!chn.m_CalculateFreq) + chn.m_ReCalculateFreqOnFirstTick = false; + else + chn.m_CalculateFreq = false; + } chn.increment = sndFile.GetChannelIncrement(chn, period, 0); chnSettings[channel].incChanged = false; inc = chn.increment * tickDuration; @@ -277,7 +282,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod const PATTERNINDEX seekPat = orderList[target.pos.order]; if(Patterns.IsValidPat(seekPat) && Patterns[seekPat].IsValidRow(target.pos.row)) { - const ModCommand *m = Patterns[seekPat].GetRow(target.pos.row); + const ModCommand *m = Patterns[seekPat].GetpModCommand(target.pos.row, 0); for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++) { if(m->note == NOTE_NOTECUT || m->note == NOTE_KEYOFF || (m->note == NOTE_FADE && GetNumInstruments()) @@ -319,7 +324,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod // Check if pattern is valid playState.m_nPattern = playState.m_nCurrentOrder < orderList.size() ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex(); - bool positionJumpOnThisRow = false; + bool positionJumpOnThisRow = false, positionJumpRightOfPatternLoop = false; bool patternBreakOnThisRow = false; bool patternLoopEndedOnThisRow = false, patternLoopStartedOnThisRow = false; @@ -541,7 +546,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(chn.nNewIns <= GetNumInstruments() && (pIns = Instruments[chn.nNewIns]) != nullptr) { if(pIns->dwFlags[INS_SETPANNING]) - chn.nPan = pIns->nPan; + chn.SetInstrumentPan(pIns->nPan, *this); if(ModCommand::IsNote(note)) smp = pIns->Keyboard[note - NOTE_MIN]; } @@ -552,7 +557,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(smp > 0 && smp <= GetNumSamples()) { if(Samples[smp].uFlags[CHN_PANNING]) - chn.nPan = Samples[smp].nPan; + chn.SetInstrumentPan(Samples[smp].nPan, *this); if(Samples[smp].uFlags[CHN_ADLIB]) { memory.state->Chn[nChn].Stop(); @@ -571,13 +576,22 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(chn.rowCommand.vol != 0) chn.nOldVolParam = chn.rowCommand.vol; break; + case VOLCMD_TONEPORTAMENTO: + if(chn.rowCommand.vol) + { + const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, 0); + chn.nPortamentoSlide = porta * 4; + if(clearEffectCommand) + command = CMD_NONE; + } + break; } switch(command) { // Position Jump case CMD_POSITIONJUMP: - positionJumpOnThisRow = true; + positionJumpOnThisRow = positionJumpRightOfPatternLoop = true; playState.m_nNextOrder = static_cast(CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn)); playState.m_nNextPatStartRow = 0; // FT2 E60 bug // see https://forum.openmpt.org/index.php?topic=2769.0 - FastTracker resets Dxx if Bxx is called _after_ Dxx @@ -665,11 +679,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case 0xB0: // Pattern Loop - if (param & 0x0F) - { - patternLoopEndedOnThisRow = true; - } else { + positionJumpRightOfPatternLoop = false; CHANNELINDEX firstChn = nChn, lastChn = nChn; if(GetType() == MOD_TYPE_S3M) { @@ -677,13 +688,28 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod firstChn = 0; lastChn = GetNumChannels() - 1; } - for(CHANNELINDEX c = firstChn; c <= lastChn; c++) + if(param & 0x0F) { - memory.chnSettings[c].patLoop = memory.elapsedTime; - memory.chnSettings[c].patLoopSmp = playState.m_lTotalSampleCount; - memory.chnSettings[c].patLoopStart = playState.m_nRow; + if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M)) + { + for(CHANNELINDEX c = firstChn; c <= lastChn; c++) + { + playState.Chn[c].nPatternLoop = playState.m_nRow + 1; + } + } + patternLoopEndedOnThisRow = true; + } + else + { + for(CHANNELINDEX c = firstChn; c <= lastChn; c++) + { + memory.chnSettings[c].patLoop = memory.elapsedTime; + memory.chnSettings[c].patLoopSmp = playState.m_lTotalSampleCount; + memory.chnSettings[c].patLoopStart = playState.m_nRow; + playState.Chn[c].nPatternLoop = playState.m_nRow; + } + patternLoopStartedOnThisRow = true; } - patternLoopStartedOnThisRow = true; } break; @@ -709,6 +735,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod memory.chnSettings[nChn].patLoop = memory.elapsedTime; memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount; memory.chnSettings[nChn].patLoopStart = playState.m_nRow; + playState.Chn[nChn].nPatternLoop = playState.m_nRow; } break; @@ -844,7 +871,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod playState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); } } - MPT_FALLTHROUGH; + [[fallthrough]]; case CMD_S3MCMDEX: if((param & 0xF0) == 0x80) { @@ -855,7 +882,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case CMD_VIBRATOVOL: if (param) chn.nOldVolumeSlide = param; param = 0; - MPT_FALLTHROUGH; + [[fallthrough]]; case CMD_VIBRATO: Vibrato(chn, param); break; @@ -1172,12 +1199,13 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod oldTickDuration = tickDuration; // Pattern loop is not executed in FT2 if there are any position jump or pattern break commands on the same row. - // Pattern loop is not executed in IT if there are any position jump commands on the same row. + // Pattern loop is not executed in IT if there are any position jump commands to the right (and to the left in older OpenMPT versions). // Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm - // Test case for IT: exception: LoopBreak.it + // Test case for IT: exception: LoopBreak.it, sbx-priority.it if(patternLoopEndedOnThisRow - && (!m_playBehaviour[kFT2PatternLoopWithJumps] || !(positionJumpOnThisRow || patternBreakOnThisRow)) - && (!m_playBehaviour[kITPatternLoopWithJumps] || !positionJumpOnThisRow)) + && (!m_playBehaviour[kFT2PatternLoopWithJumps] || !(positionJumpOnThisRow || patternBreakOnThisRow)) + && (!m_playBehaviour[kITPatternLoopWithJumpsOld] || !positionJumpOnThisRow) + && (!m_playBehaviour[kITPatternLoopWithJumps] || !positionJumpRightOfPatternLoop)) { std::map startTimes; // 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. @@ -1193,7 +1221,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { const double start = memory.chnSettings[nChn].patLoop; if(!startTimes[start]) startTimes[start] = 1; - startTimes[start] = mpt::lcm(startTimes[start], 1 + (param & 0x0F)); + startTimes[start] = std::lcm(startTimes[start], 1 + (param & 0x0F)); } } for(const auto &i : startTimes) @@ -1316,7 +1344,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { Order.SetSequence(sequence); } - visitedSongRows = std::move(visitedRows); + visitedSongRows.MoveVisitedRowsFrom(visitedRows); } return results; @@ -1389,11 +1417,11 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo } // 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 + // Test case: PortaSmpChange.mod, PortaSmpChange.s3m, PortaSwap.s3m if((!instrumentChanged && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && pIns) || (GetType() == MOD_TYPE_PLM) || (GetType() == MOD_TYPE_MOD && chn.IsSamplePlaying()) - || m_playBehaviour[kST3PortaSampleChange]) + || (m_playBehaviour[kST3PortaSampleChange] && chn.IsSamplePlaying())) { // FT2 doesn't change the sample in this case, // but still uses the sample info from the old one (bug?) @@ -1448,11 +1476,16 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo } } - if(returnAfterVolumeAdjust && sampleChanged && m_playBehaviour[kMODSampleSwap] && pSmp != nullptr) + if(returnAfterVolumeAdjust && sampleChanged && pSmp != nullptr) { // ProTracker applies new instrument's finetune but keeps the old sample playing. // Test case: PortaSwapPT.mod - chn.nFineTune = pSmp->nFineTune; + if(m_playBehaviour[kMODSampleSwap]) + chn.nFineTune = pSmp->nFineTune; + // ST3 does it similarly for middle-C speed. + // Test case: PortaSwap.s3m, SampleSwap.s3m + if(GetType() == MOD_TYPE_S3M && pSmp->HasSampleData()) + chn.nC5Speed = pSmp->nC5Speed; } if(returnAfterVolumeAdjust) return; @@ -1496,12 +1529,9 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo if(m_playBehaviour[kITEnvelopeReset]) { const bool insNumber = (instr != 0); - // IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes. - // Test case: ResetEnvNoteOffOldFx.it - const bool isKeyOff = chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] || (chn.rowCommand.note == NOTE_KEYOFF && m_playBehaviour[kITInstrWithNoteOffOldEffects]); reset = (!chn.nLength || (insNumber && bPorta && m_SongFlags[SONG_ITCOMPATGXX]) - || (insNumber && !bPorta && isKeyOff && 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 @@ -1558,15 +1588,18 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo if(bPorta && pSmp == chn.pModSample && pSmp != nullptr) { // If channel length is 0, we cut a previous sample using SCx. In that case, we have to update sample length, loop points, etc... - if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && chn.nLength != 0) return; - chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); + if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && chn.nLength != 0) + return; + // FT2 compatibility: Do not reset key-off status on portamento without instrument number + // Test case: Off-Porta.xm + if(GetType() != MOD_TYPE_XM || !m_playBehaviour[kITFT2DontResetNoteOffOnPorta] || chn.rowCommand.instr != 0) + chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); } else //if(!instrumentChanged || chn.rowCommand.instr != 0 || !IsCompatibleMode(TRK_FASTTRACKER2)) // SampleChange.xm? { chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); - // IT compatibility tentative fix: Don't change bidi loop direction when - // no sample nor instrument is changed. + // IT compatibility: Don't change bidi loop direction when no sample nor instrument is changed. if((m_playBehaviour[kITPingPongNoReset] || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) && pSmp == chn.pModSample && !instrumentChanged) chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); else @@ -1616,7 +1649,7 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo } chn.nInsVol = oldInsVol; chn.nVolume = pSmp->nVolume; - if(pSmp->uFlags[CHN_PANNING]) chn.nPan = pSmp->nPan; + if(pSmp->uFlags[CHN_PANNING]) chn.SetInstrumentPan(pSmp->nPan, *this); return; } @@ -1693,7 +1726,7 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE // save the note that's actually used, as it's necessary to properly calculate PPS and stuff const int realnote = note; - if((pIns) && (note - NOTE_MIN < (int)CountOf(pIns->Keyboard))) + if((pIns) && (note - NOTE_MIN < (int)std::size(pIns->Keyboard))) { uint32 n = pIns->Keyboard[note - NOTE_MIN]; if((n) && (n < MAX_SAMPLES)) @@ -1844,9 +1877,9 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE if(pIns->nPanSwing) { chn.nPanSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nPanSwing * 4) / 128)); - if(!m_playBehaviour[kITSwingBehaviour]) + if(!m_playBehaviour[kITSwingBehaviour] && chn.nRestorePanOnNewNote == 0) { - chn.nRestorePanOnNewNote = static_cast(chn.nPan + 1); + chn.nRestorePanOnNewNote = static_cast(chn.nPan + 1); } } // Cutoff Swing @@ -1893,7 +1926,7 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE chn.position.Set(0); if((m_SongFlags[SONG_PT_MODE] || m_playBehaviour[kST3OffsetWithoutInstrument]) && !chn.rowCommand.instr) { - chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - 1)); + chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - SmpLength(1))); } else { chn.prevNoteOffset = 0; @@ -1972,9 +2005,9 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE } } - // 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] || chn.rowCommand.instr == 0)) + // IT compatibility: Don't reset key-off flag on porta notes unless Compat Gxx is enabled. + // Test case: Off-Porta.it, Off-Porta-CompatGxx.it, Off-Porta.xm + if(m_playBehaviour[kITFT2DontResetNoteOffOnPorta] && bPorta && (!m_SongFlags[SONG_ITCOMPATGXX] || chn.rowCommand.instr == 0)) chn.dwFlags.reset(CHN_EXTRALOUD); else chn.dwFlags.reset(CHN_EXTRALOUD | CHN_KEYOFF); @@ -2017,9 +2050,9 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE chn.nCutOff = pIns->GetCutoff(); useFilter = true; } - if(useFilter && (pIns->nFilterMode != FLTMODE_UNCHANGED)) + if(useFilter && (pIns->filterMode != FilterMode::Unchanged)) { - chn.nFilterMode = pIns->nFilterMode; + chn.nFilterMode = pIns->filterMode; } } else { @@ -2063,7 +2096,7 @@ void CSoundFile::ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *in if(newPan != int32_min) { - chn.nPan = newPan; + chn.SetInstrumentPan(newPan, *this); // IT compatibility: Sample and instrument panning overrides channel surround status. // Test case: SmpInsPanSurround.it if(m_playBehaviour[kPanOverride] && !m_SongFlags[SONG_SURROUNDPAN]) @@ -2077,7 +2110,7 @@ void CSoundFile::ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *in CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const { // Check for empty channel - for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) + for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { const ModChannel &c = m_PlayState.Chn[i]; // No sample and no plugin playing @@ -2086,20 +2119,24 @@ CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const // Plugin channel with already released note if(!c.nLength && c.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]) return i; + // Stopped OPL channel + if(c.dwFlags[CHN_ADLIB] && (!m_opl || !m_opl->IsActive(i))) + return i; } uint32 vol = 0x800000; if(nChn < MAX_CHANNELS) { const ModChannel &srcChn = m_PlayState.Chn[nChn]; - if(!srcChn.nFadeOutVol && srcChn.nLength) return 0; + if(!srcChn.nFadeOutVol && srcChn.nLength) + return CHANNELINDEX_INVALID; vol = (srcChn.nRealVolume << 9) | srcChn.nVolume; } // All channels are used: check for lowest volume - CHANNELINDEX result = 0; + CHANNELINDEX result = CHANNELINDEX_INVALID; uint32 envpos = 0; - for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) + for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { const ModChannel &c = m_PlayState.Chn[i]; if(c.nLength && !c.nFadeOutVol) @@ -2108,7 +2145,8 @@ CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const // Rationale: We need volume envelopes in case e.g. all NNA channels are playing at full volume but are looping on a 0-volume envelope node. // But if global volume is not applied to master and the global volume temporarily drops to 0, we would kill arbitrary channels. Hence, add the note volume as well. uint32 v = (c.nRealVolume << 9) | c.nVolume; - if(c.dwFlags[CHN_LOOP]) v /= 2; + if(c.dwFlags[CHN_LOOP]) + v /= 2; if((v < vol) || ((v == vol) && (c.VolEnv.nEnvPosition > envpos))) { envpos = c.VolEnv.nEnvPosition; @@ -2122,23 +2160,20 @@ CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, bool forceCut) { - CHANNELINDEX nnaChn = CHANNELINDEX_INVALID; ModChannel &srcChn = m_PlayState.Chn[nChn]; const ModInstrument *pIns = nullptr; if(!ModCommand::IsNote(static_cast(note))) - { - return nnaChn; - } + return CHANNELINDEX_INVALID; + // Always NNA cut - using if((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_MT2)) || !m_nInstruments || forceCut) && !srcChn.HasMIDIOutput()) { if(!srcChn.nLength || srcChn.dwFlags[CHN_MUTE] || !(srcChn.rightVol | srcChn.leftVol)) - { return CHANNELINDEX_INVALID; - } - nnaChn = GetNNAChannel(nChn); - if(!nnaChn) return CHANNELINDEX_INVALID; + const CHANNELINDEX nnaChn = GetNNAChannel(nChn); + if(nnaChn == CHANNELINDEX_INVALID) + return CHANNELINDEX_INVALID; ModChannel &chn = m_PlayState.Chn[nnaChn]; // Copy Channel chn = srcChn; @@ -2161,18 +2196,24 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo } return nnaChn; } - if(instr > GetNumInstruments()) instr = 0; + if(instr > GetNumInstruments()) + instr = 0; const ModSample *pSample = srcChn.pModSample; // If no instrument is given, assume previous instrument to still be valid. // Test case: DNA-NoInstr.it pIns = instr > 0 ? Instruments[instr] : srcChn.pModInstrument; + auto dnaNote = note; if(pIns != nullptr) { - uint32 n = pIns->Keyboard[note - NOTE_MIN]; - note = pIns->NoteMap[note - NOTE_MIN]; - if ((n) && (n < MAX_SAMPLES)) + auto smp = pIns->Keyboard[note - NOTE_MIN]; + // IT compatibility: DCT = note uses pattern notes for comparison + // Note: This is not applied in case kITRealNoteMapping is not set to keep playback of legacy modules simple (chn.nNote is translated note in that case) + // Test case: dct_smp_note_test.it + if(!m_playBehaviour[kITDCTBehaviour] || !m_playBehaviour[kITRealNoteMapping]) + dnaNote = pIns->NoteMap[note - NOTE_MIN]; + if(smp > 0 && smp < MAX_SAMPLES) { - pSample = &Samples[n]; + pSample = &Samples[smp]; } else if(m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel()) { // Impulse Tracker ignores empty slots. @@ -2181,17 +2222,20 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo return CHANNELINDEX_INVALID; } } - if (srcChn.dwFlags[CHN_MUTE]) + if(srcChn.dwFlags[CHN_MUTE]) return CHANNELINDEX_INVALID; for(CHANNELINDEX i = nChn; i < MAX_CHANNELS; i++) - if(i >= m_nChannels || i == nChn) { + // Only apply to background channels, or the same pattern channel + if(i < m_nChannels && i != nChn) + continue; + ModChannel &chn = m_PlayState.Chn[i]; bool applyDNAtoPlug = false; if((chn.nMasterChn == nChn + 1 || i == nChn) && chn.pModInstrument != nullptr) { - bool bOk = false; + bool applyDNA = false; // Duplicate Check Type switch(chn.pModInstrument->nDCT) { @@ -2199,33 +2243,40 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo break; // Note case DCT_NOTE: - if(note && chn.nNote == note && pIns == chn.pModInstrument) bOk = true; - if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; + if(dnaNote != NOTE_NONE && chn.nNote == dnaNote && pIns == chn.pModInstrument) + applyDNA = true; + if(pIns && pIns->nMixPlug) + applyDNAtoPlug = true; break; // Sample case DCT_SAMPLE: - if(pSample != nullptr && pSample == chn.pModSample) bOk = true; + // IT compatibility: DCT = sample only applies to same instrument + // Test case: dct_smp_note_test.it + if(pSample != nullptr && pSample == chn.pModSample && (pIns == chn.pModInstrument || !m_playBehaviour[kITDCTBehaviour])) + applyDNA = true; break; // Instrument case DCT_INSTRUMENT: - if(pIns == chn.pModInstrument) bOk = true; - if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; + if(pIns == chn.pModInstrument) + applyDNA = true; + if(pIns && pIns->nMixPlug) + applyDNAtoPlug = true; break; // Plugin case DCT_PLUGIN: if(pIns && (pIns->nMixPlug) && (pIns->nMixPlug == chn.pModInstrument->nMixPlug)) { applyDNAtoPlug = true; - bOk = true; + applyDNA = true; } break; - } + // Duplicate Note Action - if (bOk) + if(applyDNA) { #ifndef NO_PLUGINS - if (applyDNAtoPlug && chn.nNote != NOTE_NONE) + if(applyDNAtoPlug && chn.nNote != NOTE_NONE) { switch(chn.pModInstrument->nDNA) { @@ -2258,7 +2309,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo // Note Fade case DNA_NOTEFADE: chn.dwFlags.set(CHN_NOTEFADE); - if(chn.dwFlags[CHN_ADLIB] && m_opl) + if(chn.dwFlags[CHN_ADLIB] && m_opl && !m_playBehaviour[kOPLwithNNA]) m_opl->NoteOff(i); break; } @@ -2293,72 +2344,84 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo #endif // NO_PLUGINS // New Note Action - if(srcChn.IsSamplePlaying() || applyNNAtoPlug) - { - nnaChn = GetNNAChannel(nChn); - if(nnaChn != 0) - { - ModChannel &chn = m_PlayState.Chn[nnaChn]; - // Copy Channel - chn = srcChn; - chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_PORTAMENTO); - chn.nPanbrelloOffset = 0; + if(!srcChn.IsSamplePlaying() && !applyNNAtoPlug) + return CHANNELINDEX_INVALID; - chn.nMasterChn = nChn < GetNumChannels() ? nChn + 1 : 0; - chn.nCommand = CMD_NONE; + CHANNELINDEX nnaChn = GetNNAChannel(nChn); + if(nnaChn == CHANNELINDEX_INVALID) + return CHANNELINDEX_INVALID; + + ModChannel &chn = m_PlayState.Chn[nnaChn]; + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteCut(nnaChn); + // Copy Channel + chn = srcChn; + chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_PORTAMENTO); + chn.nPanbrelloOffset = 0; + + chn.nMasterChn = nChn < GetNumChannels() ? nChn + 1 : 0; + chn.nCommand = CMD_NONE; #ifndef NO_PLUGINS - if(applyNNAtoPlug && pPlugin) - { - switch(srcChn.nNNA) - { - case NNA_NOTEOFF: - case NNA_NOTECUT: - case NNA_NOTEFADE: - // Switch off note played on this plugin, on this tracker channel and midi channel - SendMIDINote(nChn, NOTE_KEYOFF, 0); - srcChn.nArpeggioLastNote = NOTE_NONE; - break; - case NNA_CONTINUE: - break; - } - } -#endif // NO_PLUGINS - - // Key Off the note - switch(srcChn.nNNA) - { - case NNA_NOTEOFF: - 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) - { - chn.nFadeOutVol = 0; - chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); - } - // Stop this channel - srcChn.nLength = 0; - srcChn.position.Set(0); - srcChn.nROfs = srcChn.nLOfs = 0; + if(applyNNAtoPlug && pPlugin) + { + switch(srcChn.nNNA) + { + case NNA_NOTEOFF: + case NNA_NOTECUT: + case NNA_NOTEFADE: + // Switch off note played on this plugin, on this tracker channel and midi channel + SendMIDINote(nChn, NOTE_KEYOFF, 0); + srcChn.nArpeggioLastNote = NOTE_NONE; + break; + case NNA_CONTINUE: + break; } } +#endif // NO_PLUGINS + + // Key Off the note + switch(srcChn.nNNA) + { + case NNA_NOTEOFF: + KeyOff(chn); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + { + m_opl->NoteOff(nChn); + if(m_playBehaviour[kOPLwithNNA]) + m_opl->MoveChannel(nChn, nnaChn); + } + break; + case 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) + { + if(m_playBehaviour[kOPLwithNNA]) + m_opl->MoveChannel(nChn, nnaChn); + else + m_opl->NoteOff(nChn); + } + break; + case NNA_CONTINUE: + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->MoveChannel(nChn, nnaChn); + break; + } + if(!chn.nVolume) + { + chn.nFadeOutVol = 0; + chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); + } + // Stop this channel + srcChn.nLength = 0; + srcChn.position.Set(0); + srcChn.nROfs = srcChn.nLOfs = 0; + return nnaChn; } @@ -2419,7 +2482,7 @@ bool CSoundFile::ProcessEffects() { 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()); + chn.m_plugParamValueStep = (targetvalue - m_MixPlugins[plugin - 1].pMixPlugin->GetParameter(plugparam)) / PlugParamValue(GetNumTicksOnCurrentRow()); } if(m_PlayState.m_nTickCount + 1 == GetNumTicksOnCurrentRow()) { // On last tick, set parameter exactly to target value. @@ -2507,6 +2570,10 @@ bool CSoundFile::ProcessEffects() } nPatLoopRow = nloop; + // IT compatibility: SBx is prioritized over Position Jump (Bxx) effects that are located left of the SBx effect. + // Test case: sbx-priority.it, LoopBreak.it + if(m_playBehaviour[kITPatternLoopWithJumps]) + nPosJump = ORDERINDEX_INVALID; } if(GetType() == MOD_TYPE_S3M) @@ -2664,8 +2731,10 @@ bool CSoundFile::ProcessEffects() // Test cases: keyoff+instr.xm, delay.xm bool reloadSampleSettings = (m_playBehaviour[kFT2ReloadSampleSettings] && instr != 0); // ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it - // Test case: PTSwapEmpty.mod - bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying() && chn.pModSample != nullptr && !chn.pModSample->HasSampleData()); + // Test case: PTSwapEmpty.mod, PTInstrVolume.mod, SampleSwap.s3m + bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) + || m_playBehaviour[kST3SampleSwap] + || (m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying() && (chn.pModSample == nullptr || !chn.pModSample->HasSampleData())); // Now it's time for some FT2 crap... if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) @@ -2735,12 +2804,12 @@ bool CSoundFile::ProcessEffects() if(oldSample != nullptr) { - if(!oldSample->uFlags[SMP_NODEFAULTVOLUME]) + if(!oldSample->uFlags[SMP_NODEFAULTVOLUME] && (GetType() != MOD_TYPE_S3M || oldSample->HasSampleData())) chn.nVolume = oldSample->nVolume; if(reloadSampleSettings) { // Also reload panning - chn.nPan = oldSample->nPan; + chn.SetInstrumentPan(oldSample->nPan, *this); } } } @@ -2752,6 +2821,26 @@ bool CSoundFile::ProcessEffects() chn.nTremorCount = 0x20; } + // IT compatibility: Envelope retriggering with instrument number based on Old Effects and Compatible Gxx flags: + // OldFX CompatGxx Env Behaviour + // ----- --------- ------------- + // off off never reset + // on off reset on instrument without portamento + // off on reset on instrument with portamento + // on on always reset + // Test case: ins-xx.it, ins-ox.it, ins-oc.it, ins-xc.it, ResetEnvNoteOffOldFx.it, ResetEnvNoteOffOldFx2.it, noteoff3.it + if(GetNumInstruments() && m_playBehaviour[kITInstrWithNoteOffOldEffects] + && instr && !ModCommand::IsNote(note)) + { + if((bPorta && m_SongFlags[SONG_ITCOMPATGXX]) + || (!bPorta && m_SongFlags[SONG_ITOLDEFFECTS])) + { + chn.ResetEnvelopes(); + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.nFadeOutVol = 65536; + } + } + if(retrigEnv) //Case: instrument with no note data. { //IT compatibility: Instrument with no note. @@ -2773,7 +2862,7 @@ bool CSoundFile::ProcessEffects() } } - if (GetNumInstruments() && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) + if(GetNumInstruments() && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED))) { chn.ResetEnvelopes(); chn.dwFlags.set(CHN_FASTVOLRAMP); @@ -2829,7 +2918,9 @@ bool CSoundFile::ProcessEffects() { if(chn.nRestorePanOnNewNote > 0) { - chn.nPan = chn.nRestorePanOnNewNote - 1; + chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1; + if(chn.nRestorePanOnNewNote & 0x8000) + chn.dwFlags.set(CHN_SURROUND); chn.nRestorePanOnNewNote = 0; } if(chn.nRestoreResonanceOnNewNote > 0) @@ -2876,11 +2967,18 @@ bool CSoundFile::ProcessEffects() //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 != chn.pModSample && ModCommand::IsNote(note))) + } else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && oldSample != chn.pModSample && ModCommand::IsNote(note)) { // Special IT case: portamento+note causes sample change -> ignore portamento bPorta = false; - } else if(m_playBehaviour[kMODSampleSwap] && chn.increment.IsZero()) + } else if(m_playBehaviour[kST3SampleSwap] && oldSample != chn.pModSample && (bPorta || !ModCommand::IsNote(note)) && chn.position.GetUInt() > chn.nLength) + { + // ST3 with SoundBlaster does sample swapping and continues playing the new sample where the old sample was stopped. + // If the new sample is shorter than that, it is stopped, even if it could be looped. + // This also applies to portamento between different samples. + // Test case: SampleSwap.s3m + chn.nLength = 0; + } else if(m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying()) { // If channel was paused and is resurrected by a lone instrument number, reset the sample position. // Test case: PTSwapEmpty.mod @@ -2958,30 +3056,10 @@ bool CSoundFile::ProcessEffects() { if (volcmd == VOLCMD_TONEPORTAMENTO) { - uint32 porta = 0; - 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 - { - if(cmd == CMD_TONEPORTAMENTO && GetType() == MOD_TYPE_XM) - { - // Yes, FT2 is *that* weird. If there is a Mx command in the volume column - // and a normal 3xx command, the 3xx command is ignored but the Mx command's - // effectiveness is doubled. - // Test case: TonePortamentoMemory.xm - cmd = CMD_NONE; - vol *= 2; - } - porta = vol << 4; + const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, nStartTick); + if(clearEffectCommand) + cmd = CMD_NONE; - // FT2 compatibility: If there's a portamento and a note delay, execute the portamento, but don't update the parameter - // Test case: PortaDelay.xm - if(m_playBehaviour[kFT2PortaDelay] && nStartTick != 0) - { - porta = 0; - } - } TonePortamento(chn, porta); } else { @@ -3001,7 +3079,7 @@ bool CSoundFile::ProcessEffects() { chn.nPan = 0; } - MPT_FALLTHROUGH; + [[fallthrough]]; default: // no memory here. volcmd = VOLCMD_NONE; @@ -3529,9 +3607,12 @@ bool CSoundFile::ProcessEffects() // Pattern Break / Position Jump only if no loop running // Exception: FastTracker 2 in all cases, Impulse Tracker in case of position jump // Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm - // Test case for IT: exception: LoopBreak.it + // Test case for IT: exception: LoopBreak.it, sbx-priority.it if((doBreakRow || doPosJump) - && (!doPatternLoop || m_playBehaviour[kFT2PatternLoopWithJumps] || (m_playBehaviour[kITPatternLoopWithJumps] && doPosJump))) + && (!doPatternLoop + || m_playBehaviour[kFT2PatternLoopWithJumps] + || (m_playBehaviour[kITPatternLoopWithJumps] && doPosJump) + || (m_playBehaviour[kITPatternLoopWithJumpsOld] && doPosJump))) { if(!doPosJump) nPosJump = m_PlayState.m_nCurrentOrder + 1; if(!doBreakRow) nBreakRow = 0; @@ -3561,7 +3642,12 @@ bool CSoundFile::ProcessEffects() // Pattern Loop m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder; m_PlayState.m_nNextRow = nPatLoopRow; - if(m_PlayState.m_nPatternDelay) + // FT2 skips the first row of the pattern loop if there's a pattern delay, ProTracker sometimes does it too (didn't quite figure it out yet). + // But IT and ST3 don't do this. + // Test cases: PatLoopWithDelay.it, PatLoopWithDelay.s3m + if(m_PlayState.m_nPatternDelay + && (GetType() != MOD_TYPE_IT || !m_playBehaviour[kITPatternLoopWithJumps]) + && GetType() != MOD_TYPE_S3M) { m_PlayState.m_nNextRow++; } @@ -3723,7 +3809,10 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const } } // Regular Slide - if(!chn.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 + || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, -int(param) * 4); } @@ -3788,7 +3877,10 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons } } - if(!chn.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 + || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, int(param) * 4); } @@ -3798,7 +3890,7 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons // Send portamento commands to plugins void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides) { - int actualParam = mpt::abs(param); + int actualParam = std::abs(param); int pitchBend = 0; // Old MIDI Pitch Bends: @@ -3845,6 +3937,7 @@ void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides) void CSoundFile::FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -3890,6 +3983,7 @@ void CSoundFile::FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) cons void CSoundFile::FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -3927,6 +4021,7 @@ void CSoundFile::FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) co void CSoundFile::ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -3966,6 +4061,7 @@ void CSoundFile::ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) void CSoundFile::ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -4014,8 +4110,11 @@ void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool ret { chn.nNoteSlideCounter = chn.nNoteSlideSpeed; // update it - chn.nPeriod = GetPeriodFromNote - ((slideUp ? 1 : -1) * chn.nNoteSlideStep + GetNoteFromPeriod(chn.nPeriod), 8363, 0); + const int32 delta = (slideUp ? 1 : -1) * chn.nNoteSlideStep; + if(chn.HasCustomTuning()) + chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount(); + else + chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod), 8363, 0); if(retrig) { @@ -4025,6 +4124,36 @@ void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool ret } } + +std::pair CSoundFile::GetVolCmdTonePorta(const ModCommand &m, uint32 startTick) const +{ + if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) + { + return {ImpulseTrackerPortaVolCmd[m.vol & 0x0F], false}; + } else + { + bool clearEffectColumn = false; + uint32 vol = m.vol; + if(m.command == CMD_TONEPORTAMENTO && GetType() == MOD_TYPE_XM) + { + // Yes, FT2 is *that* weird. If there is a Mx command in the volume column + // and a normal 3xx command, the 3xx command is ignored but the Mx command's + // effectiveness is doubled. + // Test case: TonePortamentoMemory.xm + clearEffectColumn = true; + vol *= 2; + } + + // FT2 compatibility: If there's a portamento and a note delay, execute the portamento, but don't update the parameter + // Test case: PortaDelay.xm + if(m_playBehaviour[kFT2PortaDelay] && startTick != 0) + return {0, clearEffectColumn}; + else + return {vol * 16, clearEffectColumn}; + } +} + + // Portamento Slide void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const { @@ -4037,11 +4166,11 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const chn.nOldPortaUp = chn.nOldPortaDown = static_cast(param); } - if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { //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) ? chn.m_PortamentoTickSlide : 0; + const int32 oldPortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0; if(param) chn.nPortamentoSlide = param; @@ -4062,9 +4191,9 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const //With glissando interpreting param as notes instead of finesteps. } - const int32 slide = chn.m_PortamentoTickSlide - old_PortamentoTickSlide; + const int32 slide = chn.m_PortamentoTickSlide - oldPortamentoTickSlide; - if(mpt::abs(chn.nPortamentoDest) <= mpt::abs(slide)) + if(std::abs(chn.nPortamentoDest) <= std::abs(slide)) { if(chn.nPortamentoDest != 0) { @@ -4080,9 +4209,12 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const } return; - } //End candidate MPT behavior. + } - bool doPorta = !chn.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]) + || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]); if(GetType() == MOD_TYPE_PLM && param >= 0xF0) { param -= 0xF0; @@ -4484,9 +4616,7 @@ void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param) case 0x40: chn.nVibratoType = param & 0x07; break; // E5x: Set FineTune case 0x50: if(!m_SongFlags[SONG_FIRSTTICK]) - { break; - } if(GetType() & (MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_AMF0 | MOD_TYPE_MED)) { chn.nFineTune = MOD2XMFineTune(param); @@ -4551,12 +4681,14 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) // S1x: Set Glissando Control case 0x10: chn.dwFlags.set(CHN_GLISSANDO, param != 0); break; // S2x: Set FineTune - case 0x20: if(!m_SongFlags[SONG_FIRSTTICK]) break; + case 0x20: if(!m_SongFlags[SONG_FIRSTTICK]) + break; if(GetType() != MOD_TYPE_669) { chn.nC5Speed = S3MFineTuneTable[param]; chn.nFineTune = MOD2XMFineTune(param); - if (chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); + if(chn.nPeriod) + chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } else if(chn.pModSample != nullptr) { chn.nC5Speed = chn.pModSample->nC5Speed + param * 80; @@ -4800,7 +4932,7 @@ void CSoundFile::InvertLoop(ModChannel &chn) // TRASH IT!!! (Yes, the sample!) uint8 &sample = mpt::byte_cast(pModSample->sampleb())[pModSample->nLoopStart + chn.nEFxOffset]; sample = ~sample; - ctrlSmp::PrecomputeLoops(*pModSample, *this, false); + pModSample->PrecomputeLoops(*this, false); } @@ -4855,22 +4987,22 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * const int swing = (m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) ? chn.nVolSwing : 0; const int vol = Util::muldiv((chn.nVolume + swing) * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 20); data = static_cast(Clamp(vol / 2, 1, 127)); - //data = (unsigned char)MIN((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127); + //data = (unsigned char)std::min((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127); } else if(macro[pos] == 'u') { // Calculated volume // Same note as with velocity applies here, but apparently also for instrument / sample volumes? const int vol = Util::muldiv(chn.nCalcVolume * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 26); data = static_cast(Clamp(vol / 2, 1, 127)); - //data = (unsigned char)MIN((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127); + //data = (unsigned char)std::min((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127); } else if(macro[pos] == 'x') { // Pan set - data = static_cast(std::min(chn.nPan / 2, 127)); + data = static_cast(std::min(static_cast(chn.nPan / 2), 127)); } else if(macro[pos] == 'y') { // Calculated pan - data = static_cast(std::min(chn.nRealPan / 2, 127)); + data = static_cast(std::min(static_cast(chn.nRealPan / 2), 127)); } else if(macro[pos] == 'a') { // High byte of bank select @@ -5012,7 +5144,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * } else { // Other MIDI messages - sendLen = std::min(MIDIEvents::GetEventLength(out[sendPos]), outPos - sendPos); + sendLen = std::min(static_cast(MIDIEvents::GetEventLength(out[sendPos])), outPos - sendPos); } if(sendLen == 0) @@ -5124,7 +5256,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned // F0.F0.02.xx: Set filter mode (high nibble determines filter mode) if(param < 0x20) { - chn.nFilterMode = (param >> 4); + chn.nFilterMode = static_cast(param >> 4); SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); } @@ -5194,10 +5326,10 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned { if(macro[0] == 0xF0) { - pPlugin->MidiSysexSend(mpt::as_span(mpt::byte_cast(macro), macroLen)); + pPlugin->MidiSysexSend(mpt::as_span(mpt::byte_cast(macro), macroLen)); } else { - uint32 len = std::min(MIDIEvents::GetEventLength(macro[0]), macroLen); + uint32 len = std::min(static_cast(MIDIEvents::GetEventLength(macro[0])), macroLen); uint32 curData = 0; memcpy(&curData, macro, len); pPlugin->MidiSend(curData); @@ -5251,9 +5383,11 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const chn.prevNoteOffset += param; - if(param >= chn.nLoopEnd && GetType() == MOD_TYPE_MTM && chn.dwFlags[CHN_LOOP] && chn.nLoopEnd > 0) + if(param >= chn.nLoopEnd && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_MTM)) && chn.dwFlags[CHN_LOOP] && chn.nLoopEnd > 0) { // Offset wrap-around + // Note that ST3 only does this in GUS mode. SoundBlaster stops the sample entirely instead. + // Test case: OffsetLoopWraparound.s3m param = (param - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart) + chn.nLoopStart; } @@ -5288,7 +5422,14 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const if (chn.position.GetUInt() >= chn.nLength || (chn.dwFlags[CHN_LOOP] && chn.position.GetUInt() >= chn.nLoopEnd)) { // Offset beyond sample size - if (!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MOD | MOD_TYPE_MTM))) + if(m_playBehaviour[kFT2ST3OffsetOutOfRange] || GetType() == MOD_TYPE_MTM) + { + // FT2 Compatibility: Don't play note if offset is beyond sample length + // ST3 Compatibility: Don't play note if offset is beyond sample length (non-looped samples only) + // Test cases: 3xx-no-old-samp.xm, OffsetPastSampleEnd.s3m + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.nPeriod = 0; + } else if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MOD))) { // IT Compatibility: Offset if(m_playBehaviour[kITOffset]) @@ -5305,12 +5446,6 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const chn.position.Set(chn.nLength - 2); } } - } else if(m_playBehaviour[kFT2OffsetOutOfRange] || GetType() == MOD_TYPE_MTM) - { - // FT2 Compatibility: Don't play note if offset is beyond sample length - // Test case: 3xx-no-old-samp.xm - chn.dwFlags.set(CHN_FASTVOLRAMP); - chn.nPeriod = 0; } else if(GetType() == MOD_TYPE_MOD && chn.dwFlags[CHN_LOOP]) { chn.position.Set(chn.nLoopStart); @@ -5332,7 +5467,7 @@ void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) c chn.dwFlags.set(CHN_PINGPONGFLAG); chn.dwFlags.reset(CHN_LOOP); chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length. - chn.position.Set((chn.nLength - 1) - std::min(SmpLength(param) << 8, chn.nLength - 1), 0); + chn.position.Set((chn.nLength - 1) - std::min(SmpLength(param) << 8, chn.nLength - SmpLength(1)), 0); } } @@ -5364,7 +5499,8 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) { // Here are some really stupid things FT2 does on the first tick. // Test case: RetrigTick0.xm - if(chn.rowCommand.instr > 0 && chn.rowCommand.IsNoteOrEmpty()) retrigCount = 1; + if(chn.rowCommand.instr > 0 && chn.rowCommand.IsNoteOrEmpty()) + retrigCount = 1; if(chn.rowCommand.volcmd == VOLCMD_VOLUME && chn.rowCommand.vol != 0) { // I guess this condition simply checked if the volume byte was != 0 in FT2. @@ -5385,8 +5521,10 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) // old routines if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)) { - if (!retrigSpeed) retrigSpeed = 1; - if ((retrigCount) && (!(retrigCount % retrigSpeed))) doRetrig = true; + if(!retrigSpeed) + retrigSpeed = 1; + if(retrigCount && !(retrigCount % retrigSpeed)) + doRetrig = true; retrigCount++; } else if(GetType() == MOD_TYPE_MTM) { @@ -5396,16 +5534,21 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) { int realspeed = retrigSpeed; // FT2 bug: if a retrig (Rxy) occurs together with a volume command, the first retrig interval is increased by one tick - if ((param & 0x100) && (chn.rowCommand.volcmd == VOLCMD_VOLUME) && (chn.rowCommand.param & 0xF0)) realspeed++; + if((param & 0x100) && (chn.rowCommand.volcmd == VOLCMD_VOLUME) && (chn.rowCommand.param & 0xF0)) + realspeed++; if(!m_SongFlags[SONG_FIRSTTICK] || (param & 0x100)) { - if (!realspeed) realspeed = 1; - if ((!(param & 0x100)) && (m_PlayState.m_nMusicSpeed) && (!(m_PlayState.m_nTickCount % realspeed))) doRetrig = true; + if(!realspeed) + realspeed = 1; + if(!(param & 0x100) && m_PlayState.m_nMusicSpeed && !(m_PlayState.m_nTickCount % realspeed)) + doRetrig = true; retrigCount++; - } else if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) retrigCount = 0; + } else if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) + retrigCount = 0; if (retrigCount >= realspeed) { - if ((m_PlayState.m_nTickCount) || ((param & 0x100) && (!chn.rowCommand.note))) doRetrig = true; + if(m_PlayState.m_nTickCount || ((param & 0x100) && !chn.rowCommand.note)) + doRetrig = true; } if(m_playBehaviour[kFT2Retrigger] && param == 0) { @@ -5418,21 +5561,23 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) // IT compatibility: If a sample is shorter than the retrig time (i.e. it stops before the retrig counter hits zero), it is not retriggered. // Test case: retrig-short.it if(chn.nLength == 0 && m_playBehaviour[kITShortSampleRetrig] && !chn.HasMIDIOutput()) - { return; - } + // ST3 compatibility: No retrig after Note Cut + // Test case: RetrigAfterNoteCut.s3m + if(m_playBehaviour[kST3RetrigAfterNoteCut] && !chn.nFadeOutVol) + return; if(doRetrig) { uint32 dv = (param >> 4) & 0x0F; int vol = chn.nVolume; - if (dv) + if(dv) { // FT2 compatibility: Retrig + volume will not change volume of retrigged notes if(!m_playBehaviour[kFT2Retrigger] || !(chn.rowCommand.volcmd == VOLCMD_VOLUME)) { - if (retrigTable1[dv]) + if(retrigTable1[dv]) vol = (vol * retrigTable1[dv]) >> 4; else vol += ((int)retrigTable2[dv]) << 2; @@ -5443,22 +5588,34 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) } uint32 note = chn.nNewNote; int32 oldPeriod = chn.nPeriod; - if (note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength) + const bool retrigAdlib = chn.dwFlags[CHN_ADLIB] && m_playBehaviour[kOPLRealRetrig]; + if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && retrigAdlib) CheckNNA(nChn, 0, note, true); bool resetEnv = false; if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) { - if((chn.rowCommand.instr) && (param < 0x100)) + if(chn.rowCommand.instr && param < 0x100) { InstrumentChange(chn, chn.rowCommand.instr, false, false); resetEnv = true; } - if (param < 0x100) resetEnv = true; + if(param < 0x100) + resetEnv = true; } - bool fading = chn.dwFlags[CHN_NOTEFADE]; + if(retrigAdlib && chn.pModSample && m_opl) + { + m_opl->NoteCut(nChn); + m_opl->Patch(nChn, chn.pModSample->adlib); + } + + const bool fading = chn.dwFlags[CHN_NOTEFADE]; + const auto oldPrevNoteOffset = chn.prevNoteOffset; + chn.prevNoteOffset = 0; // Retriggered notes should not use previous offset (test case: OxxMemoryWithRetrig.s3m) // IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase) // Test case: retrig.it NoteChange(chn, note, m_playBehaviour[kITRetrigger], resetEnv, false, nChn); + if(!chn.rowCommand.instr) + chn.prevNoteOffset = oldPrevNoteOffset; // XM compatibility: Prevent NoteChange from resetting the fade flag in case an instrument number + note-off is present. // Test case: RetrigFade.xm if(fading && GetType() == MOD_TYPE_XM) @@ -5471,22 +5628,27 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) ProcessMidiOut(nChn); //Send retrig to Midi #endif // NO_PLUGINS } - if ((GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT)) && (!chn.rowCommand.note) && (oldPeriod)) chn.nPeriod = oldPeriod; - if (!(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) retrigCount = 0; + if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && chn.rowCommand.note == NOTE_NONE && oldPeriod != 0) + chn.nPeriod = oldPeriod; + if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))) + retrigCount = 0; // IT compatibility: see previous IT compatibility comment =) if(m_playBehaviour[kITRetrigger]) chn.position.Set(0); offset--; if(offset >= 0 && offset <= static_cast(CountOf(chn.pModSample->cues)) && chn.pModSample != nullptr) { - if(offset == 0) offset = chn.oldOffset; - else offset = chn.oldOffset = chn.pModSample->cues[offset - 1]; + if(offset == 0) + offset = chn.oldOffset; + else + offset = chn.oldOffset = chn.pModSample->cues[offset - 1]; SampleOffset(chn, offset); } } // buggy-like-hell FT2 Rxy retrig! - if(m_playBehaviour[kFT2Retrigger] && (param & 0x100)) retrigCount++; + if(m_playBehaviour[kFT2Retrigger] && (param & 0x100)) + retrigCount++; // Now we can also store the retrig value for IT... if(!m_playBehaviour[kITRetrigger]) @@ -5496,7 +5658,10 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) void CSoundFile::DoFreqSlide(ModChannel &chn, int32 nFreqSlide) const { - if(!chn.nPeriod) return; + if(!chn.nPeriod) + return; + MPT_ASSERT(!chn.HasCustomTuning()); + if(GetType() == MOD_TYPE_669) { // Like other oldskool trackers, Composer 669 doesn't have linear slides... @@ -5508,7 +5673,7 @@ void CSoundFile::DoFreqSlide(ModChannel &chn, int32 nFreqSlide) const { // IT Linear slides const auto nOldPeriod = chn.nPeriod; - uint32 n = mpt::abs(nFreqSlide) / 4u; + uint32 n = std::abs(nFreqSlide) / 4u; LimitMax(n, 255u); if(n != 0) { @@ -5559,7 +5724,7 @@ void CSoundFile::NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample) if(chn.dwFlags[CHN_ADLIB] && m_opl) { - m_opl->NoteCut(nChn); + m_opl->NoteCut(nChn, false); } } } @@ -5634,7 +5799,7 @@ void CSoundFile::SetSpeed(PlayState &playState, uint32 param) const if(param > 0) playState.m_nMusicSpeed = param; if(GetType() == MOD_TYPE_STM && param > 0) { - playState.m_nMusicSpeed = std::max(param >> 4u, 1); + playState.m_nMusicSpeed = std::max(param >> 4, uint32(1)); playState.m_nMusicTempo = ConvertST2Tempo(static_cast(param)); } } @@ -5643,8 +5808,8 @@ void CSoundFile::SetSpeed(PlayState &playState, uint32 param) const // Convert a ST2 tempo byte to classic tempo and speed combination TEMPO CSoundFile::ConvertST2Tempo(uint8 tempo) { - static const uint8 ST2TempoFactor[] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 }; - static const uint32 st2MixingRate = 23863; // Highest possible setting in ST2 + static constexpr uint8 ST2TempoFactor[] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 }; + static constexpr uint32 st2MixingRate = 23863; // Highest possible setting in ST2 // This underflows at tempo 06...0F, and the resulting tick lengths depend on the mixing rate. int32 samplesPerTick = st2MixingRate / (49 - ((ST2TempoFactor[tempo >> 4u] * (tempo & 0x0F)) >> 4u)); @@ -5670,12 +5835,7 @@ void CSoundFile::SetTempo(TEMPO param, bool setFromUI) // ProTracker sets the tempo after the first tick. // Note: The case of one tick per row is handled in ProcessRow() instead. // Test case: TempoChange.mod -#if MPT_MSVC_AT_LEAST(2017,8) && MPT_MSVC_BEFORE(2019,0) - // Work-around MSVC getting confused about deduced const input type in noexcept operator inside noexcept condition. - m_PlayState.m_nMusicTempo.SetRaw(std::min(param.GetRaw(), specs.GetTempoMax().GetRaw())); -#else m_PlayState.m_nMusicTempo = std::min(param, specs.GetTempoMax()); -#endif } else if(param < minTempo && !m_SongFlags[SONG_FIRSTTICK]) { // Tempo Slide @@ -5896,7 +6056,7 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe } else { nFineTune = XM2MODFineTune(nFineTune); - if ((nFineTune) || (note < 24) || (note >= 24 + mpt::size(ProTrackerPeriodTable))) + if ((nFineTune) || (note < 24) || (note >= 24 + std::size(ProTrackerPeriodTable))) return (ProTrackerTunedPeriods[nFineTune * 12u + note % 12u] << 5) >> (note / 12u); else return (ProTrackerPeriodTable[note - 24] << 2); @@ -6094,7 +6254,7 @@ IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(CHANNELINDEX chn) const // Get the MIDI channel currently associated with a given tracker channel uint8 CSoundFile::GetBestMidiChannel(CHANNELINDEX trackerChn) const { - if(trackerChn >= mpt::size(m_PlayState.Chn)) + if(trackerChn >= std::size(m_PlayState.Chn)) { return 0; } @@ -6183,9 +6343,9 @@ void CSoundFile::PortamentoFineMPT(ModChannel &chn, int param) const int tickParam = static_cast((m_PlayState.m_nTickCount + 1.0) * param / m_PlayState.m_nMusicSpeed); chn.m_PortamentoFineSteps += (param >= 0) ? tickParam - chn.nOldFinePortaUpDown : tickParam + chn.nOldFinePortaUpDown; if(m_PlayState.m_nTickCount + 1 == m_PlayState.m_nMusicSpeed) - chn.nOldFinePortaUpDown = static_cast(mpt::abs(param)); + chn.nOldFinePortaUpDown = static_cast(std::abs(param)); else - chn.nOldFinePortaUpDown = static_cast(mpt::abs(tickParam)); + chn.nOldFinePortaUpDown = static_cast(std::abs(tickParam)); chn.m_CalculateFreq = true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp index cce5e503b..89f3db9b1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp @@ -43,6 +43,16 @@ OPENMPT_NAMESPACE_BEGIN +bool SettingCacheCompleteFileBeforeLoading() +{ + #ifdef MODPLUG_TRACKER + return TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading; + #else + return false; + #endif +} + + mpt::ustring FileHistory::AsISO8601() const { tm date = loadDate; @@ -73,10 +83,10 @@ CSoundFile::CSoundFile() : m_pModSpecs(&ModSpecs::itEx), m_nType(MOD_TYPE_NONE), Patterns(*this), - Order(*this), #ifdef MODPLUG_TRACKER m_MIDIMapper(*this), #endif + Order(*this), m_PRNG(mpt::make_prng(mpt::global_prng())), visitedSongRows(*this) { @@ -95,7 +105,7 @@ CSoundFile::CSoundFile() : #endif // MODPLUG_TRACKER MemsetZero(Instruments); - MemsetZero(m_szNames); + Clear(m_szNames); m_pTuningsTuneSpecific = new CTuningCollection(); } @@ -170,6 +180,9 @@ void CSoundFile::InitializeGlobals(MODTYPE type) m_modFormat = ModFormatDetails(); m_FileHistory.clear(); m_tempoSwing.clear(); +#ifdef MPT_EXTERNAL_SAMPLES + m_samplePaths.clear(); +#endif // MPT_EXTERNAL_SAMPLES // Note: we do not use the Amiga resampler for DBM as it's a multichannel format and can make use of higher-quality Amiga soundcards instead of Paula. if(GetType() & (/*MOD_TYPE_DBM | */MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_OKT | MOD_TYPE_SFX | MOD_TYPE_STP)) @@ -260,12 +273,12 @@ CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, { const uint64 availableFileSize = file.GetLength(); const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength()); - //const uint64 validFileSize = std::min(fileSize, ProbeRecommendedSize); + //const uint64 validFileSize = std::min(fileSize, static_cast(ProbeRecommendedSize)); const uint64 goalSize = file.GetPosition() + minimumAdditionalSize; - //const uint64 goalMinimumSize = std::min(goalSize, ProbeRecommendedSize); + //const uint64 goalMinimumSize = std::min(goalSize, static_cast(ProbeRecommendedSize)); if(pfilesize) { - if(availableFileSize < std::min(fileSize, ProbeRecommendedSize)) + if(availableFileSize < std::min(fileSize, static_cast(ProbeRecommendedSize))) { if(availableFileSize < goalSize) { @@ -284,9 +297,6 @@ CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, } -const std::size_t CSoundFile::ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; - - #define MPT_DO_PROBE( storedResult , call ) \ MPT_DO { \ ProbeResult lastResult = call ; \ @@ -299,7 +309,7 @@ const std::size_t CSoundFile::ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; /**/ -CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize) +CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize) { ProbeResult result = ProbeFailure; if(pfilesize && (*pfilesize < data.size())) @@ -364,7 +374,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) #endif MemsetZero(Instruments); - MemsetZero(m_szNames); + Clear(m_szNames); #ifndef NO_PLUGINS std::fill(std::begin(m_MixPlugins), std::end(m_MixPlugins), SNDMIXPLUGIN()); #endif // NO_PLUGINS @@ -442,7 +452,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) // Read archive comment if there is no song comment if(m_songMessage.empty()) { - m_songMessage.assign(mpt::ToCharset(mpt::CharsetLocale, unarchiver.GetComment())); + m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment())); } #endif } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) @@ -473,8 +483,6 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) // Checking samples, load external samples for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++) { - // Sanitize sample names - mpt::String::SetNullTerminator(m_szNames[nSmp]); ModSample &sample = Samples[nSmp]; #ifdef MPT_EXTERNAL_SAMPLES @@ -531,6 +539,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) // Set default play state values if (!m_nDefaultTempo.GetInt()) m_nDefaultTempo.Set(125); if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; + if (m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat; @@ -583,8 +592,8 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) { 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)); + mpt::ToUnicode(mpt::Charset::UTF8, plugin.Info.szLibraryName), + mpt::ToUnicode(mpt::Charset::Locale, plugin.Info.szName)); CMainFrame::GetMainFrame()->SetHelpText(mpt::ToCString(s)); } #endif // MODPLUG_TRACKER @@ -606,7 +615,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) notFoundText.append(plugin.GetLibraryName()); notFoundText.append("\n"); #else - AddToLog(LogWarning, U_("Plugin not found: ") + mpt::ToUnicode(mpt::CharsetUTF8, plugin.GetLibraryName())); + AddToLog(LogWarning, U_("Plugin not found: ") + mpt::ToUnicode(mpt::Charset::UTF8, plugin.GetLibraryName())); #endif // MODPLUG_TRACKER } } @@ -626,7 +635,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::ToUnicode(mpt::CharsetUTF8, notFoundText), U_("OpenMPT - Plugins missing"), false, true) == cnfYes) + if(Reporting::Confirm(mpt::ToUnicode(mpt::Charset::UTF8, notFoundText), U_("OpenMPT - Plugins missing"), false, true) == cnfYes) { std::string url = "https://resources.openmpt.org/plugins/search.php?p="; for(const auto &id : notFoundIDs) @@ -676,6 +685,9 @@ bool CSoundFile::Destroy() m_songArtist.clear(); m_songMessage.clear(); m_FileHistory.clear(); +#ifdef MPT_EXTERNAL_SAMPLES + m_samplePaths.clear(); +#endif // MPT_EXTERNAL_SAMPLES for(auto &smp : Samples) { @@ -706,11 +718,6 @@ bool CSoundFile::Destroy() void CSoundFile::SetDspEffects(uint32 DSPMask) { -#ifdef ENABLE_ASM -#ifndef NO_REVERB - if(!(GetRealProcSupport() & PROCSUPPORT_MMX)) DSPMask &= ~SNDDSP_REVERB; -#endif -#endif m_MixerSettings.DSPMask = DSPMask; InitPlayer(false); } @@ -1018,7 +1025,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITNoSurroundPan); playBehaviour.set(kITShortSampleRetrig); playBehaviour.set(kITPortaNoNote); - playBehaviour.set(kITDontResetNoteOffOnPorta); + playBehaviour.set(kITFT2DontResetNoteOffOnPorta); playBehaviour.set(kITVolColMemory); playBehaviour.set(kITPortamentoSwapResetsPos); playBehaviour.set(kITEmptyNoteMapSlot); @@ -1032,9 +1039,13 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITMultiSampleInstrumentNumber); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kITInstrWithNoteOffOldEffects); + playBehaviour.set(kITDoNotOverrideChannelPan); + playBehaviour.set(kITDCTBehaviour); if(type == MOD_TYPE_MPT) { playBehaviour.set(kOPLFlexibleNoteOff); + playBehaviour.set(kOPLwithNNA); + playBehaviour.set(kOPLRealRetrig); } break; @@ -1045,13 +1056,14 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kPerChannelGlobalVolSlide); playBehaviour.set(kPanOverride); playBehaviour.set(kITFT2PatternLoop); + playBehaviour.set(kITFT2DontResetNoteOffOnPorta); playBehaviour.set(kFT2Arpeggio); playBehaviour.set(kFT2Retrigger); playBehaviour.set(kFT2VolColVibrato); playBehaviour.set(kFT2PortaNoNote); playBehaviour.set(kFT2KeyOff); playBehaviour.set(kFT2PanSlide); - playBehaviour.set(kFT2OffsetOutOfRange); + playBehaviour.set(kFT2ST3OffsetOutOfRange); playBehaviour.set(kFT2RestrictXCommand); playBehaviour.set(kFT2RetrigWithNoteDelay); playBehaviour.set(kFT2SetPanEnvPos); @@ -1084,6 +1096,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kTempoClamp); playBehaviour.set(kPanOverride); playBehaviour.set(kITPanbrelloHold); + playBehaviour.set(kFT2ST3OffsetOutOfRange); playBehaviour.set(kST3NoMutedChannels); playBehaviour.set(kST3PortaSampleChange); playBehaviour.set(kST3EffectMemory); @@ -1091,6 +1104,8 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(KST3PortaAfterArpeggio); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kST3OffsetWithoutInstrument); + playBehaviour.set(kST3RetrigAfterNoteCut); + playBehaviour.set(kST3SampleSwap); break; case MOD_TYPE_MOD: @@ -1147,6 +1162,17 @@ PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITPanningReset); playBehaviour.set(kITInstrWithNoteOff); playBehaviour.set(kOPLFlexibleNoteOff); + playBehaviour.set(kITDoNotOverrideChannelPan); + playBehaviour.set(kITDCTBehaviour); + playBehaviour.set(kOPLwithNNA); + playBehaviour.set(kOPLRealRetrig); + break; + + case MOD_TYPE_S3M: + playBehaviour = GetSupportedPlaybackBehaviour(type); + // Default behaviour was chosen to follow GUS, so kST3PortaSampleChange is enabled and kST3SampleSwap is disabled. + // For SoundBlaster behaviour, those two flags would need to be swapped. + playBehaviour.reset(kST3SampleSwap); break; case MOD_TYPE_XM: @@ -1183,7 +1209,7 @@ MODTYPE CSoundFile::GetBestSaveFormat() const case MOD_TYPE_STP: return MOD_TYPE_MOD; case MOD_TYPE_MED: - if(m_nDefaultTempo == TEMPO(125, 0) && m_nDefaultSpeed == 6 && !m_nInstruments) + if(!m_nInstruments) { for(const auto &pat : Patterns) { @@ -1236,7 +1262,7 @@ const char *CSoundFile::GetSampleName(SAMPLEINDEX nSample) const MPT_ASSERT(nSample <= GetNumSamples()); if (nSample < MAX_SAMPLES) { - return m_szNames[nSample]; + return m_szNames[nSample].buf; } else { return ""; @@ -1250,7 +1276,7 @@ const char *CSoundFile::GetInstrumentName(INSTRUMENTINDEX nInstr) const return ""; MPT_ASSERT(nInstr <= GetNumInstruments()); - return Instruments[nInstr]->name; + return Instruments[nInstr]->name.buf; } @@ -1264,8 +1290,7 @@ bool CSoundFile::InitChannel(CHANNELINDEX nChn) #ifdef MODPLUG_TRACKER if(GetpModDoc() != nullptr) { - GetpModDoc()->Record1Channel(nChn, false); - GetpModDoc()->Record2Channel(nChn, false); + GetpModDoc()->SetChannelRecordGroup(nChn, RecordGroup::NoGroup); } #endif // MODPLUG_TRACKER @@ -1279,7 +1304,7 @@ bool CSoundFile::InitChannel(CHANNELINDEX nChn) void CSoundFile::InitAmigaResampler() { - if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga) + if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off) { const Paula::State defaultState(GetSampleRate()); for(auto &chn : m_PlayState.Chn) @@ -1292,7 +1317,8 @@ void CSoundFile::InitAmigaResampler() void CSoundFile::InitOPL() { - if(!m_opl) m_opl = mpt::make_unique(); + if(!m_opl) + m_opl = std::make_unique(m_MixerSettings.gdwMixingFreq); } @@ -1383,7 +1409,7 @@ SAMPLEINDEX CSoundFile::RemoveSelectedSamples(const std::vector &keepSampl if(DestroySample(nSmp)) { - strcpy(m_szNames[nSmp], ""); + m_szNames[nSmp] = ""; nRemoved++; } if((nSmp == GetNumSamples()) && (nSmp > 1)) m_nSamples--; @@ -1435,12 +1461,12 @@ bool CSoundFile::DestroySampleThreadsafe(SAMPLEINDEX nSample) } -CTuning* CSoundFile::CreateTuning12TET(const std::string &name) +std::unique_ptr CSoundFile::CreateTuning12TET(const mpt::ustring &name) { - CTuning* pT = CTuning::CreateGeometric(name, 12, 2, 15); + std::unique_ptr pT = CTuning::CreateGeometric(name, 12, 2, 15); for(ModCommand::NOTE note = 0; note < 12; ++note) { - pT->SetNoteName(note, mpt::ToCharset(mpt::CharsetASCII, mpt::ustring(NoteNamesSharp[note]))); + pT->SetNoteName(note, mpt::ustring(NoteNamesSharp[note])); } return pT; } @@ -1451,7 +1477,7 @@ mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const INSTRUME // 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 mpt::ToUnicode(GetCharsetInternal(), Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC)); + return Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC); } else { return GetNoteName(note); @@ -1471,8 +1497,8 @@ mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const NoteName { // 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); + const mpt::uchar 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)) { @@ -1570,12 +1596,40 @@ void CSoundFile::ChangeModTypeTo(const MODTYPE newType) Order.OnModTypeChanged(oldType); Patterns.OnModTypeChanged(oldType); - m_modFormat.type = mpt::ToUnicode(mpt::CharsetUTF8, GetModSpecifications().fileExtension); + m_modFormat.type = mpt::ToUnicode(mpt::Charset::UTF8, GetModSpecifications().fileExtension); } #endif // MODPLUG_TRACKER +ModMessageHeuristicOrder CSoundFile::GetMessageHeuristic() const +{ + ModMessageHeuristicOrder result = ModMessageHeuristicOrder::Default; + switch(GetType()) + { + case MOD_TYPE_MPT: + result = ModMessageHeuristicOrder::Samples; + break; + case MOD_TYPE_IT: + result = ModMessageHeuristicOrder::Samples; + break; + case MOD_TYPE_XM: + result = ModMessageHeuristicOrder::InstrumentsSamples; + break; + case MOD_TYPE_MDL: + result = ModMessageHeuristicOrder::InstrumentsSamples; + break; + case MOD_TYPE_IMF: + result = ModMessageHeuristicOrder::InstrumentsSamples; + break; + default: + result = ModMessageHeuristicOrder::Default; + break; + } + return result; +} + + bool CSoundFile::SetTitle(const std::string &newTitle) { if(m_songName != newTitle) @@ -1666,7 +1720,7 @@ uint32 CSoundFile::GetTickDuration(PlayState &playState) const bufferCount--; playState.m_dBufferDiff++; } - MPT_ASSERT(mpt::abs(playState.m_dBufferDiff) < 1.0); + MPT_ASSERT(std::abs(playState.m_dBufferDiff) < 1.0); retval = bufferCount; } break; @@ -1836,13 +1890,13 @@ void CSoundFile::PrecomputeSampleLoops(bool updateChannels) bool CSoundFile::LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &filename) { bool ok = false; - InputFile f(filename); + InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); if(f.IsValid()) { const ModSample origSample = Samples[smp]; - char origName[MAX_SAMPLENAME]; - mpt::String::Copy(origName, m_szNames[smp]); + mpt::charbuf origName; + origName = m_szNames[smp]; FileReader file = GetFileReader(f); ok = ReadSampleFromFile(smp, file, false); @@ -1862,7 +1916,7 @@ bool CSoundFile::LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &file sample.uFlags.reset(SMP_MODIFIED); sample.SanitizeLoops(); } - mpt::String::Copy(m_szNames[smp], origName); + m_szNames[smp] = origName; } SetSamplePath(smp, filename); return ok; @@ -1925,7 +1979,7 @@ void TempoSwing::Normalize() i = Util::muldivr_unsigned(i, Unity, static_cast(sum)); remain -= i; } - //MPT_ASSERT(static_cast(mpt::abs(static_cast(remain))) <= size()); + //MPT_ASSERT(static_cast(std::abs(static_cast(remain))) <= size()); at(0) += static_cast(remain); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h index 26ffafe7b..a5b12c99b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h @@ -60,6 +60,9 @@ OPENMPT_NAMESPACE_BEGIN +bool SettingCacheCompleteFileBeforeLoading(); + + // ----------------------------------------------------------------------------- // MODULAR ModInstrument FIELD ACCESS : body content in InstrumentExtensions.cpp // ----------------------------------------------------------------------------- @@ -70,8 +73,6 @@ bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, uint16 fsize // -------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------- -typedef void (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels - #ifdef LIBOPENMPT_BUILD #ifndef NO_PLUGINS @@ -80,7 +81,7 @@ class CVstPluginManager; #endif -typedef std::bitset PlayBehaviourSet; +using PlayBehaviourSet = std::bitset; #ifdef MODPLUG_TRACKER @@ -210,7 +211,7 @@ enum deleteInstrumentSamples namespace Tuning { class CTuningCollection; } // namespace Tuning -typedef Tuning::CTuningCollection CTuningCollection; +using CTuningCollection = Tuning::CTuningCollection; struct CModSpecifications; class OPL; #ifdef MODPLUG_TRACKER @@ -225,9 +226,8 @@ class CModDoc; struct FileHistory { - FileHistory() { MemsetZero(loadDate); } // Date when the file was loaded in the the tracker or created. - tm loadDate; + tm loadDate = {}; // Time the file was open in the editor, in 1/18.2th seconds (frequency of a standard DOS timer, to keep compatibility with Impulse Tracker easy). uint32 openTime = 0; // Return the date as a (possibly truncated if not enough precision is available) ISO 8601 formatted date. @@ -247,6 +247,17 @@ struct TimingInfo }; +enum class ModMessageHeuristicOrder +{ + Instruments, + Samples, + InstrumentsSamples, + SamplesInstruments, + BothInstrumentsSamples, + BothSamplesInstruments, + Default = InstrumentsSamples, +}; + struct ModFormatDetails { mpt::ustring formatName; // "FastTracker 2" @@ -254,7 +265,7 @@ struct ModFormatDetails 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; + mpt::Charset charset = mpt::Charset::UTF8; }; @@ -263,7 +274,8 @@ class IAudioReadTarget protected: virtual ~IAudioReadTarget() = default; public: - virtual void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; + virtual void DataCallback(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; + virtual void DataCallback(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; }; @@ -272,7 +284,8 @@ class IAudioSource public: virtual ~IAudioSource() = default; public: - virtual void FillCallback(int32 * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) = 0; + virtual void FillCallback(MixSampleInt * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) = 0; + virtual void FillCallback(MixSampleFloat * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) = 0; }; @@ -280,7 +293,7 @@ class AudioSourceNone : public IAudioSource { public: - void FillCallback(int32 * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) override + void FillCallback(MixSampleInt * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) override { for(std::size_t channel = 0; channel < channels; ++channel) { @@ -290,10 +303,20 @@ public: } } } + void FillCallback(MixSampleFloat * 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] = MixSampleFloat(0.0); + } + } + } }; -typedef MPT_UCHAR_TYPE NoteName[4]; +using NoteName = mpt::uchar[4]; class CSoundFile @@ -312,7 +335,7 @@ public: //Tuning--> public: - static CTuning* CreateTuning12TET(const std::string &name); + static std::unique_ptr CreateTuning12TET(const mpt::ustring &name); static CTuning *GetDefaultTuning() {return nullptr;} CTuningCollection& GetTuneSpecificTunings() {return *m_pTuningsTuneSpecific;} @@ -358,10 +381,12 @@ private: mixsample_t MixRearBuffer[MIXBUFFERSIZE * 2]; // Non-interleaved plugin processing buffer float MixFloatBuffer[2][MIXBUFFERSIZE]; - mixsample_t gnDryLOfsVol = 0; - mixsample_t gnDryROfsVol = 0; mixsample_t MixInputBuffer[NUMMIXINPUTBUFFERS][MIXBUFFERSIZE]; + // End-of-sample pop reduction tail level + mixsample_t m_dryLOfsVol = 0, m_dryROfsVol = 0; + mixsample_t m_surroundLOfsVol = 0, m_surroundROfsVol = 0; + public: MixerSettings m_MixerSettings; CResampler m_Resampler; @@ -378,8 +403,11 @@ public: #ifndef NO_AGC CAGC m_AGC; #endif +#ifndef NO_DSP + BitCrush m_BitCrush; +#endif - typedef uint32 samplecount_t; // Number of rendered samples + using samplecount_t = uint32; // Number of rendered samples public: // for Editing #ifdef MODPLUG_TRACKER @@ -411,7 +439,7 @@ public: uint32 m_nSamplePreAmp, m_nVSTiVolume; uint32 m_OPLVolumeFactor; // 16.16 - enum : uint32 { m_OPLVolumeFactorScale = (1 << 16) }; + static constexpr uint32 m_OPLVolumeFactorScale = 1 << 16; bool IsGlobalVolumeUnset() const { return IsFirstTick(); } #ifndef MODPLUG_TRACKER @@ -441,7 +469,7 @@ public: #ifndef NO_PLUGINS SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins #endif - char m_szNames[MAX_SAMPLES][MAX_SAMPLENAME]; // Sample names + mpt::charbuf m_szNames[MAX_SAMPLES]; // Sample names Version m_dwCreatedWithVersion; Version m_dwLastSavedWithVersion; @@ -463,44 +491,49 @@ public: struct PlayState { friend class CSoundFile; - protected: - samplecount_t m_nBufferCount; - double m_dBufferDiff; - public: - samplecount_t m_lTotalSampleCount = 0; public: - uint32 m_nTickCount; + samplecount_t m_lTotalSampleCount = 0; // Total number of rendered samples protected: - uint32 m_nPatternDelay, m_nFrameDelay; // m_nPatternDelay = pattern delay (rows), m_nFrameDelay = fine pattern delay (ticks) + samplecount_t m_nBufferCount = 0; // Remaining number samples to render for this tick + double m_dBufferDiff = 0.0; // Modern tempo rounding error compensation + public: - uint32 m_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_nTickCount = 0; // Current tick being processed + protected: + uint32 m_nPatternDelay = 0; // Pattern delay (rows) + uint32 m_nFrameDelay = 0; // Fine pattern delay (ticks) + public: + uint32 m_nSamplesPerTick = 0; + ROWINDEX m_nCurrentRowsPerBeat = 0; // Current time signature + ROWINDEX m_nCurrentRowsPerMeasure = 0; // Current time signature + uint32 m_nMusicSpeed = 0; // Current speed + TEMPO m_nMusicTempo; // Current tempo // Playback position - ROWINDEX m_nRow; - ROWINDEX m_nNextRow; + ROWINDEX m_nRow = 0; // Current row being processed + ROWINDEX m_nNextRow = 0; // Next row to process protected: - ROWINDEX m_nNextPatStartRow; // for FT2's E60 bug + ROWINDEX m_nNextPatStartRow = 0; // For FT2's E60 bug public: - PATTERNINDEX m_nPattern; - ORDERINDEX m_nCurrentOrder, m_nNextOrder, m_nSeqOverride = ORDERINDEX_INVALID; + PATTERNINDEX m_nPattern = 0; // Current pattern being processed + ORDERINDEX m_nCurrentOrder = 0; // Current order being processed + ORDERINDEX m_nNextOrder = 0; // Next order to process + ORDERINDEX m_nSeqOverride = ORDERINDEX_INVALID; // Queued order to be processed next, regardless of what order would normally follow // Global volume public: - int32 m_nGlobalVolume; + int32 m_nGlobalVolume = MAX_GLOBAL_VOLUME; // Current global volume (0...MAX_GLOBAL_VOLUME) protected: - int32 m_nSamplesToGlobalVolRampDest, m_nGlobalVolumeRampAmount, - m_nGlobalVolumeDestination; - int32 m_lHighResRampingGlobalVolume; + int32 m_nSamplesToGlobalVolRampDest = 0, m_nGlobalVolumeRampAmount = 0, + m_nGlobalVolumeDestination = 0; // Global volume ramping + int32 m_lHighResRampingGlobalVolume = 0; // Global volume ramping public: bool m_bPositionChanged = true; // Report to plugins that we jumped around in the module public: - CHANNELINDEX ChnMix[MAX_CHANNELS]; // Channels to be mixed + CHANNELINDEX ChnMix[MAX_CHANNELS]; // Index of channels in Chn to be actually mixed ModChannel Chn[MAX_CHANNELS]; // Mixing channels... First m_nChannels channels are master channels (i.e. they are never NNA channels)! public: @@ -577,13 +610,15 @@ private: public: CSoundFile(); + CSoundFile(const CSoundFile &) = delete; + CSoundFile & operator=(const CSoundFile &) = delete; ~CSoundFile(); public: // logging void SetCustomLog(ILog *pLog) { m_pCustomLog = pLog; } void AddToLog(LogLevel level, const mpt::ustring &text) const; - /*MPT_DEPRECATED*/ void AddToLog(const AnyStringLocale &text) const { AddToLog(LogInformation, mpt::ToUnicode(text)); } + /*[[deprecated]]*/ void AddToLog(const AnyStringLocale &text) const { AddToLog(LogInformation, mpt::ToUnicode(text)); } public: @@ -605,7 +640,7 @@ public: #define PROBE_RECOMMENDED_SIZE 2048u - static const std::size_t ProbeRecommendedSize; + static constexpr std::size_t ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; enum ProbeFlags { @@ -625,7 +660,7 @@ public: static ProbeResult ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize); - static ProbeResult Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize); + static ProbeResult Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize); public: @@ -651,12 +686,14 @@ public: mpt::Charset GetCharsetInternal() const // 8bit string encoding of strings internal in CSoundFile { #if defined(MODPLUG_TRACKER) - return mpt::CharsetLocale; + return mpt::Charset::Locale; #else // MODPLUG_TRACKER return GetCharsetFile(); #endif // MODPLUG_TRACKER } + ModMessageHeuristicOrder GetMessageHeuristic() const; + void SetPreAmp(uint32 vol); uint32 GetPreAmp() const { return m_MixerSettings.m_nPreAmp; } @@ -671,7 +708,7 @@ public: CHANNELINDEX GetNumChannels() const { return m_nChannels; } #ifndef NO_PLUGINS - IMixPlugin* GetInstrumentPlugin(INSTRUMENTINDEX instr); + IMixPlugin* GetInstrumentPlugin(INSTRUMENTINDEX instr) const; #endif const CModSpecifications& GetModSpecifications() const {return *m_pModSpecs;} static const CModSpecifications& GetModSpecifications(const MODTYPE type); @@ -679,8 +716,12 @@ public: #ifdef MODPLUG_TRACKER void PatternTranstionChnSolo(const CHANNELINDEX chnIndex); void PatternTransitionChnUnmuteAll(); -#endif // MODPLUG_TRACKER +protected: + void HandlePatternTransitionEvents(); +#endif // MODPLUG_TRACKER + +public: double GetCurrentBPM() const; void DontLoopPattern(PATTERNINDEX nPat, ROWINDEX nRow = 0); CHANNELINDEX GetMixStat() const { return m_nMixStat; } @@ -809,7 +850,7 @@ public: 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 bool IsExtensionSupported(std::string_view ext); // UTF8, casing of ext is ignored static mpt::ustring ModContainerTypeToString(MODCONTAINERTYPE containertype); static mpt::ustring ModContainerTypeToTracker(MODCONTAINERTYPE containertype); @@ -847,7 +888,7 @@ public: void S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool compatibilityExport = false) const; void ModSaveCommand(uint8 &command, uint8 ¶m, const bool toXM, const bool compatibilityExport = false) const; static void ReadMODPatternEntry(FileReader &file, ModCommand &m); - static void ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m); + static void ReadMODPatternEntry(const std::array data, ModCommand &m); void SetupMODPanning(bool bForceSetup = false); // Setup LRRL panning, max channel volume @@ -959,6 +1000,7 @@ protected: void PortamentoFineMPT(ModChannel &chn, int); void PortamentoExtraFineMPT(ModChannel &chn, int); void NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const; + std::pair GetVolCmdTonePorta(const ModCommand &m, uint32 startTick) const; void TonePortamento(ModChannel &chn, uint32 param) const; void Vibrato(ModChannel &chn, uint32 param) const; void FineVibrato(ModChannel &chn, uint32 param) const; @@ -1057,7 +1099,8 @@ protected: bool ReadXISample(SAMPLEINDEX nSample, FileReader &file); bool ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewind = true); bool ReadITISample(SAMPLEINDEX nSample, FileReader &file); - bool ReadIFFSample(SAMPLEINDEX nInstr, FileReader &file); + bool ReadIFFSample(SAMPLEINDEX sample, FileReader &file); + bool ReadBRRSample(SAMPLEINDEX sample, FileReader& file); bool ReadFLACSample(SAMPLEINDEX sample, FileReader &file); bool ReadOpusSample(SAMPLEINDEX sample, FileReader &file); bool ReadVorbisSample(SAMPLEINDEX sample, FileReader &file); @@ -1120,8 +1163,6 @@ private: PLUGINDEX GetActiveInstrumentPlugin(CHANNELINDEX, PluginMutePriority respectMutes) const; IMixPlugin *GetChannelInstrumentPlugin(CHANNELINDEX chn) const; - void HandlePatternTransitionEvents(); - public: PLUGINDEX GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const; uint8 GetBestMidiChannel(CHANNELINDEX nChn) const; @@ -1130,7 +1171,7 @@ public: #ifndef NO_PLUGINS -inline IMixPlugin* CSoundFile::GetInstrumentPlugin(INSTRUMENTINDEX instr) +inline IMixPlugin* CSoundFile::GetInstrumentPlugin(INSTRUMENTINDEX instr) const { if(instr > 0 && instr <= GetNumInstruments() && Instruments[instr] && Instruments[instr]->nMixPlug && Instruments[instr]->nMixPlug <= MAX_MIXPLUGINS) return m_MixPlugins[Instruments[instr]->nMixPlug - 1].pMixPlugin; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp index 603e923e9..cbf988c29 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp @@ -14,7 +14,6 @@ #include "Sndfile.h" #include "MixerLoops.h" #include "MIDIEvents.h" -#include "tuning.h" #include "Tables.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" @@ -26,13 +25,10 @@ OPENMPT_NAMESPACE_BEGIN -// VU-Meter -#define VUMETER_DECAY 4 - // Log tables for pre-amp // Pre-amp (or more precisely: Pre-attenuation) depends on the number of channels, // Which this table takes care of. -static const uint8 PreAmpTable[16] = +static constexpr uint8 PreAmpTable[16] = { 0x60, 0x60, 0x60, 0x70, // 0-7 0x80, 0x88, 0x90, 0x98, // 8-15 @@ -41,7 +37,7 @@ static const uint8 PreAmpTable[16] = }; #ifndef NO_AGC -static const uint8 PreAmpAGCTable[16] = +static constexpr uint8 PreAmpAGCTable[16] = { 0x60, 0x60, 0x60, 0x64, 0x68, 0x70, 0x78, 0x80, @@ -87,8 +83,8 @@ void CSoundFile::InitPlayer(bool bReset) if(bReset) { ResetMixStat(); - gnDryLOfsVol = 0; - gnDryROfsVol = 0; + m_dryLOfsVol = m_dryROfsVol = 0; + m_surroundLOfsVol = m_surroundROfsVol = 0; InitAmigaResampler(); } m_Resampler.UpdateTables(); @@ -106,6 +102,9 @@ void CSoundFile::InitPlayer(bool bReset) #endif #ifndef NO_AGC m_AGC.Initialize(bReset, m_MixerSettings.gdwMixingFreq); +#endif +#ifndef NO_DSP + m_BitCrush.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif if(m_opl) { @@ -290,7 +289,7 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget MPT_ASSERT(m_PlayState.m_nBufferCount > 0); // assert that we have actually something to do - const samplecount_t countChunk = std::min({ MIXBUFFERSIZE, m_PlayState.m_nBufferCount, countToRender }); + const samplecount_t countChunk = std::min({ static_cast(MIXBUFFERSIZE), static_cast(m_PlayState.m_nBufferCount), static_cast(countToRender) }); if(m_MixerSettings.NumInputChannels > 0) { @@ -399,6 +398,14 @@ void CSoundFile::ProcessDSP(uint32 countChunk) m_AGC.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } #endif // NO_AGC + + #ifndef NO_DSP + if(m_MixerSettings.DSPMask & SNDDSP_BITCRUSH) + { + m_BitCrush.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); + } + #endif // NO_DSP + #if defined(NO_DSP) && defined(NO_EQ) && defined(NO_AGC) MPT_UNREFERENCED_PARAMETER(countChunk); #endif @@ -502,28 +509,33 @@ bool CSoundFile::ProcessRow() m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume; for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) { - m_PlayState.Chn[i].dwFlags.set(CHN_NOTEFADE | CHN_KEYOFF); - m_PlayState.Chn[i].nFadeOutVol = 0; - - if (i < m_nChannels) + auto &chn = m_PlayState.Chn[i]; + if(chn.dwFlags[CHN_ADLIB] && m_opl) { - m_PlayState.Chn[i].nGlobalVol = ChnSettings[i].nVolume; - m_PlayState.Chn[i].nVolume = ChnSettings[i].nVolume; - m_PlayState.Chn[i].nPan = ChnSettings[i].nPan; - m_PlayState.Chn[i].nPanSwing = m_PlayState.Chn[i].nVolSwing = 0; - m_PlayState.Chn[i].nCutSwing = m_PlayState.Chn[i].nResSwing = 0; - m_PlayState.Chn[i].nOldVolParam = 0; - m_PlayState.Chn[i].oldOffset = 0; - m_PlayState.Chn[i].nOldHiOffset = 0; - m_PlayState.Chn[i].nPortamentoDest = 0; + m_opl->NoteCut(i); + } + chn.dwFlags.set(CHN_NOTEFADE | CHN_KEYOFF); + chn.nFadeOutVol = 0; - if (!m_PlayState.Chn[i].nLength) + if(i < m_nChannels) + { + chn.nGlobalVol = ChnSettings[i].nVolume; + chn.nVolume = ChnSettings[i].nVolume; + chn.nPan = ChnSettings[i].nPan; + chn.nPanSwing = chn.nVolSwing = 0; + chn.nCutSwing = chn.nResSwing = 0; + chn.nOldVolParam = 0; + chn.oldOffset = 0; + chn.nOldHiOffset = 0; + chn.nPortamentoDest = 0; + + if(!chn.nLength) { - m_PlayState.Chn[i].dwFlags = ChnSettings[i].dwFlags; - m_PlayState.Chn[i].nLoopStart = 0; - m_PlayState.Chn[i].nLoopEnd = 0; - m_PlayState.Chn[i].pModInstrument = nullptr; - m_PlayState.Chn[i].pModSample = nullptr; + chn.dwFlags = ChnSettings[i].dwFlags; + chn.nLoopStart = 0; + chn.nLoopEnd = 0; + chn.pModInstrument = nullptr; + chn.pModSample = nullptr; } } } @@ -576,7 +588,9 @@ bool CSoundFile::ProcessRow() // the pattern loop (editor flag, not to be confused with the pattern loop effect) // flag is set - because in that case, the module would stop after the first pattern loop... const bool overrideLoopCheck = (m_nRepeatCount != -1) && m_SongFlags[SONG_PATTERNLOOP]; - if(!overrideLoopCheck && visitedSongRows.IsVisited(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, true)) + // If a row is jumped over due to a ProTracker quirk, don't add it to the visited rows (unless we notice that we are stuck in a loop by repeatedly playing that row) + const bool checkIgnoreRow = !ignoreRow || visitedSongRows.GetLastVisitedRow() == m_PlayState.m_nRow; + if(!overrideLoopCheck && checkIgnoreRow && visitedSongRows.IsVisited(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, true)) { if(m_nRepeatCount) { @@ -664,7 +678,7 @@ bool CSoundFile::ProcessRow() } // Reset channel values - ModCommand *m = Patterns[m_PlayState.m_nPattern].GetRow(m_PlayState.m_nRow); + ModCommand *m = Patterns[m_PlayState.m_nPattern].GetpModCommand(m_PlayState.m_nRow, 0); for (ModChannel *pChn = m_PlayState.Chn, *pEnd = pChn + m_nChannels; pChn != pEnd; pChn++, m++) { // First, handle some quirks that happen after the last tick of the previous row... @@ -733,7 +747,9 @@ bool CSoundFile::ProcessRow() if (m_PlayState.m_nTickCount) { m_SongFlags.reset(SONG_FIRSTTICK); - if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && m_PlayState.m_nTickCount < GetNumTicksOnCurrentRow()) + if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) + && (GetType() != MOD_TYPE_MOD || m_SongFlags[SONG_PT_MODE]) // Fix infinite loop in "GamerMan " by MrGamer, which was made with FT2 + && m_PlayState.m_nTickCount < GetNumTicksOnCurrentRow()) { // Emulate first tick behaviour if Row Delay is set. // Test cases: PatternDelaysRetrig.it, PatternDelaysRetrig.s3m, PatternDelaysRetrig.xm, PatternDelaysRetrig.mod @@ -779,7 +795,7 @@ int CSoundFile::GetVibratoDelta(int type, int position) const } else if(GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM)) { // Other waveforms are not supported. - static const int8 DBMSinus[] = + static constexpr int8 DBMSinus[] = { 33, 52, 69, 84, 96, 107, 116, 122, 125, 127, 125, 122, 116, 107, 96, 84, 69, 52, 33, 13, -8, -31, -54, -79, -104,-128, -104, -79, -54, -31, -8, 13, @@ -1136,7 +1152,7 @@ int CSoundFile::ProcessPitchFilterEnvelope(ModChannel &chn, int &period) const } else { // Pitch Envelope - if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { if(chn.nFineTune != envval) { @@ -1292,6 +1308,8 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel &chn, EnvelopeType envType void CSoundFile::IncrementEnvelopePositions(ModChannel &chn) const { + if (chn.isFirstTick && GetType() == MOD_TYPE_MED) + return; IncrementEnvelopePosition(chn, ENV_VOLUME); IncrementEnvelopePosition(chn, ENV_PANNING); IncrementEnvelopePosition(chn, ENV_PITCH); @@ -1393,7 +1411,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND { uint8 step = 0; const bool arpOnRow = (chn.rowCommand.command == CMD_ARPEGGIO); - const ModCommand::NOTE lastNote = ModCommand::IsNote(chn.nLastNote) ? pIns->NoteMap[chn.nLastNote - NOTE_MIN] : NOTE_NONE; + const ModCommand::NOTE lastNote = ModCommand::IsNote(chn.nLastNote) ? static_cast(pIns->NoteMap[chn.nLastNote - NOTE_MIN]) : static_cast(NOTE_NONE); if(arpOnRow) { switch(m_PlayState.m_nTickCount % 3) @@ -1432,7 +1450,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND if(chn.nCommand == CMD_ARPEGGIO) { - if((GetType() & MOD_TYPE_MPT) && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { switch(m_PlayState.m_nTickCount % 3) { @@ -1477,30 +1495,40 @@ 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, chn.nFineTune, chn.nC5Speed); + uint32 note = 0; // The fact that arpeggio behaves in a totally fucked up way at 16 ticks/row or more is that the arpeggio offset LUT only has 16 entries in FT2. // At more than 16 ticks/row, FT2 reads into the vibrato table, which is placed right after the arpeggio table. // Test case: Arpeggio.xm int arpPos = m_PlayState.m_nMusicSpeed - (m_PlayState.m_nTickCount % m_PlayState.m_nMusicSpeed); - if(arpPos > 16) arpPos = 2; - else if(arpPos == 16) arpPos = 0; - else arpPos %= 3; + if(arpPos > 16) + arpPos = 2; + else if(arpPos == 16) + arpPos = 0; + else + arpPos %= 3; switch(arpPos) { - case 1: note += (chn.nArpeggio >> 4); break; - case 2: note += (chn.nArpeggio & 0x0F); break; + case 1: note = (chn.nArpeggio >> 4); break; + case 2: note = (chn.nArpeggio & 0x0F); break; } - 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) + if(arpPos != 0) { - period = std::max(period, GetPeriodFromNote(108 + NOTE_MIN, 0, chn.nC5Speed)); - } + // Arpeggio is added on top of current note, but cannot do it the IT way because of + // the behaviour in ArpeggioClamp.xm. + // Test case: ArpSlide.xm + note += GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed); + period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); + + // FT2 compatibility: FT2 has a different note limit for Arpeggio. + // Test case: ArpeggioClamp.xm + if(note >= 108 + NOTE_MIN) + { + period = std::max(static_cast(period), GetPeriodFromNote(108 + NOTE_MIN, 0, chn.nC5Speed)); + } + } } } // Other trackers @@ -1558,6 +1586,8 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP if(chn.dwFlags[CHN_VIBRATO]) { + const bool advancePosition = !m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !(m_SongFlags[SONG_ITOLDEFFECTS])); + if(GetType() == MOD_TYPE_669) { if(chn.nVibratoPos % 2u) @@ -1569,12 +1599,12 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP } // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position - if(m_playBehaviour[kITVibratoTremoloPanbrello]) + if(advancePosition && m_playBehaviour[kITVibratoTremoloPanbrello]) chn.nVibratoPos += 4 * chn.nVibratoSpeed; int vdelta = GetVibratoDelta(chn.nVibratoType, chn.nVibratoPos); - if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { //Hack implementation: Scaling vibratofactor to [0.95; 1.05] //using figure from above tables and vibratodepth parameter @@ -1592,7 +1622,7 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP // ProTracker doesn't apply vibrato nor advance on the first tick. // Test case: VibratoReset.mod return; - } else if((GetType() & MOD_TYPE_XM) && (chn.nVibratoType & 0x03) == 1) + } else if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) && (chn.nVibratoType & 0x03) == 1) { // FT2 compatibility: Vibrato ramp down table is upside down. // Test case: VibratoWaveforms.xm @@ -1672,12 +1702,9 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP } // Advance vibrato position - IT updates on every tick, unless "old effects" are enabled (in this case it only updates on non-first ticks like other trackers) - 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 and pre-increments the vibrato position - if(!m_playBehaviour[kITVibratoTremoloPanbrello]) - chn.nVibratoPos += chn.nVibratoSpeed; - } + // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position + if(advancePosition && !m_playBehaviour[kITVibratoTremoloPanbrello]) + chn.nVibratoPos += chn.nVibratoSpeed; } else if(chn.dwOldFlags[CHN_VIBRATO]) { // Stop MIDI vibrato for plugins: @@ -1698,7 +1725,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: if(chn.pModSample != nullptr && chn.pModSample->nVibDepth) { const ModSample *pSmp = chn.pModSample; - const bool alternativeTuning = chn.pModInstrument && chn.pModInstrument->pTuning; + const bool hasTuning = chn.HasCustomTuning(); // 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. @@ -1709,7 +1736,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: const uint32 (&fineDownTable)[16] = useFreq ? FineLinearSlideDownTable : FineLinearSlideUpTable; // IT compatibility: Autovibrato is so much different in IT that I just put this in a separate code block, to get rid of a dozen IsCompatibilityMode() calls. - if(m_playBehaviour[kITVibratoTremoloPanbrello] && !alternativeTuning && GetType() != MOD_TYPE_MT2) + if(m_playBehaviour[kITVibratoTremoloPanbrello] && !hasTuning && GetType() != MOD_TYPE_MT2) { if(!pSmp->nVibRate) return; @@ -1755,7 +1782,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: } vdelta = (vdelta * adepth) / 64; - uint32 l = mpt::abs(vdelta); + uint32 l = std::abs(vdelta); LimitMax(period, Util::MaxValueOfType(period) / 256); period *= 256; if(vdelta < 0) @@ -1828,7 +1855,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: } int n = (vdelta * chn.nAutoVibDepth) / 256; - if(alternativeTuning) + if(hasTuning) { //Vib sweep is not taken into account here. vibratoFactor += 0.05F * pSmp->nVibDepth * vdelta / 4096.0f; //4096 == 64^2 @@ -1941,20 +1968,16 @@ void CSoundFile::ProcessRamping(ModChannel &chn) const SamplePosition CSoundFile::GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const { uint32 freq; - - const ModInstrument *pIns = chn.pModInstrument; - if(GetType() != MOD_TYPE_MPT || pIns == nullptr || pIns->pTuning == nullptr) - { + if(!chn.HasCustomTuning()) freq = GetFreqFromPeriod(period, chn.nC5Speed, periodFrac); - } else - { - freq = chn.m_Freq; - } + else + freq = chn.nPeriod; - // Applying Pitch/Tempo lock. - if(pIns && pIns->pitchToTempoLock.GetRaw()) + // Applying Pitch/Tempo lock + const ModInstrument *ins = chn.pModInstrument; + if(ins && ins->pitchToTempoLock.GetRaw()) { - freq = Util::muldivr(freq, m_PlayState.m_nMusicTempo.GetRaw(), pIns->pitchToTempoLock.GetRaw()); + freq = Util::muldivr(freq, m_PlayState.m_nMusicTempo.GetRaw(), ins->pitchToTempoLock.GetRaw()); } // Avoid increment to overflow and become negative with unrealisticly high frequencies. @@ -2236,6 +2259,19 @@ bool CSoundFile::ReadNote() period = m_nMinPeriod; } + const bool hasTuning = chn.HasCustomTuning(); + if(hasTuning) + { + if(chn.m_CalculateFreq || (chn.m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0)) + { + chn.RecalcTuningFreq(vibratoFactor, arpeggioSteps, *this); + if(!chn.m_CalculateFreq) + chn.m_ReCalculateFreqOnFirstTick = false; + else + chn.m_CalculateFreq = false; + } + } + if((chn.dwFlags & (CHN_ADLIB | CHN_MUTE | CHN_SYNCMUTE)) == CHN_ADLIB && m_opl) { const bool doProcess = m_playBehaviour[kOPLFlexibleNoteOff] || !chn.dwFlags[CHN_NOTEFADE] || GetType() == MOD_TYPE_S3M; @@ -2243,10 +2279,10 @@ bool CSoundFile::ReadNote() { // In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440. // Hence, we have to translate our "sample rate" into pitch. - const auto freq = GetFreqFromPeriod(period, chn.nC5Speed, nPeriodFrac); - const auto oplmilliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS); + const auto freq = hasTuning ? chn.nPeriod : GetFreqFromPeriod(period, chn.nC5Speed, nPeriodFrac); + const auto milliHertz = 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]); + m_opl->Frequency(nChn, milliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]); } if(doProcess) { @@ -2256,33 +2292,21 @@ bool CSoundFile::ReadNote() } // Deallocate OPL channels for notes that are most definitely never going to play again. - const auto *ins = chn.pModInstrument; - if(ins != nullptr + if(const auto *ins = chn.pModInstrument; ins != nullptr && (ins->VolEnv.dwFlags & (ENV_ENABLED | ENV_LOOP | ENV_SUSTAIN)) == ENV_ENABLED && !ins->VolEnv.empty() && chn.GetEnvelope(ENV_VOLUME).nEnvPosition >= ins->VolEnv.back().tick && ins->VolEnv.back().value == 0) { m_opl->NoteCut(nChn); + if(!m_playBehaviour[kOPLNoResetAtEnvelopeEnd]) + chn.dwFlags.reset(CHN_ADLIB); chn.dwFlags.set(CHN_NOTEFADE); chn.nFadeOutVol = 0; - } - } - - if(GetType() == MOD_TYPE_MPT && pIns != nullptr && pIns->pTuning != nullptr) - { - // In this case: GetType() == MOD_TYPE_MPT and using custom tunings. - if(chn.m_CalculateFreq || (chn.m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0)) + } else if(m_playBehaviour[kOPLFlexibleNoteOff] && chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0) { - 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]; - 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 - chn.m_CalculateFreq = false; + m_opl->NoteCut(nChn); + chn.dwFlags.reset(CHN_ADLIB); } } @@ -2308,8 +2332,9 @@ bool CSoundFile::ReadNote() // Volume ramping chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol | chn.leftVol) != 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; + constexpr uint8 VUMETER_DECAY = 4; + chn.nLeftVU = (chn.nLeftVU > VUMETER_DECAY) ? (chn.nLeftVU - VUMETER_DECAY) : 0; + chn.nRightVU = (chn.nRightVU > VUMETER_DECAY) ? (chn.nRightVU - VUMETER_DECAY) : 0; chn.newLeftVol = chn.newRightVol = 0; chn.pCurrentSample = (chn.pModSample && chn.pModSample->HasSampleData() && chn.nLength && chn.IsSamplePlaying()) ? chn.pModSample->samplev() : nullptr; @@ -2402,7 +2427,7 @@ bool CSoundFile::ReadNote() } else if(Resampling::IsKnownMode(m_nResampling)) { chn.resamplingMode = m_nResampling; - } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga) + } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off) { // Enforce Amiga resampler for Amiga modules chn.resamplingMode = SRCMODE_AMIGA; @@ -2443,6 +2468,9 @@ bool CSoundFile::ReadNote() { chn.rightVol = chn.leftVol = 0; chn.nLength = 0; + // Put the channel back into the mixer for end-of-sample pop reduction + if(chn.nLOfs || chn.nROfs) + m_PlayState.ChnMix[m_nMixChannels++] = nChn; } chn.dwOldFlags = chn.dwFlags; @@ -2575,8 +2603,8 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) else pPlugin->SetDryRatio(2 * defaultVolume); break; case PLUGIN_VOLUMEHANDLING_MIDI: - 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); + if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(uint8(127), static_cast(2 * vol)), nChn); + else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast(std::min(uint32(127), static_cast(2 * defaultVolume))), nChn); break; default: break; @@ -2599,20 +2627,20 @@ MPT_FORCEINLINE void ApplyGlobalVolumeWithRamping(int32 *SoundBuffer, int32 *Rea // Ramping required 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); 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); + if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); + if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); + if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); m_nSamplesToGlobalVolRampDest--; } else { SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_nGlobalVolume, MAX_GLOBAL_VOLUME); - 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); 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); + if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_nGlobalVolume, MAX_GLOBAL_VOLUME); + if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); + if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); m_lHighResRampingGlobalVolume = m_nGlobalVolume << VOLUMERAMPPRECISION; } SoundBuffer += isStereo ? 2 : 1; - MPT_CONSTANT_IF(hasRear) RearBuffer += 2; + if constexpr(hasRear) RearBuffer += 2; } } @@ -2656,8 +2684,8 @@ 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))); - while(mpt::abs(step) > maxStep) + int32 maxStep = std::max(int32(50), static_cast((10000 / (m_PlayState.m_nGlobalVolumeRampAmount + 1)))); + while(std::abs(step) > maxStep) { m_PlayState.m_nSamplesToGlobalVolRampDest += m_PlayState.m_nGlobalVolumeRampAmount; step = delta / static_cast(m_PlayState.m_nSamplesToGlobalVolRampDest); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp index fc0f3fd54..5398a4de4 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp @@ -24,13 +24,13 @@ OPENMPT_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////////////////////// // Note Name Tables -const MPT_UCHAR_TYPE NoteNamesSharp[12][4] = +const mpt::uchar NoteNamesSharp[12][4] = { UL_("C-"), UL_("C#"), UL_("D-"), UL_("D#"), UL_("E-"), UL_("F-"), UL_("F#"), UL_("G-"), UL_("G#"), UL_("A-"), UL_("A#"), UL_("B-") }; -const MPT_UCHAR_TYPE NoteNamesFlat[12][4] = +const mpt::uchar NoteNamesFlat[12][4] = { UL_("C-"), UL_("Db"), UL_("D-"), UL_("Eb"), UL_("E-"), UL_("F-"), UL_("Gb"), UL_("G-"), UL_("Ab"), UL_("A-"), UL_("Bb"), UL_("B-") @@ -43,7 +43,7 @@ const MPT_UCHAR_TYPE NoteNamesFlat[12][4] = struct ModFormatInfo { MODTYPE format; // MOD_TYPE_XXXX - const MPT_UCHAR_TYPE *name; // "ProTracker" + const mpt::uchar *name; // "ProTracker" const char *extension; // "mod" }; @@ -97,6 +97,7 @@ static constexpr ModFormatInfo modFormatInfo[] = // converted formats (no MODTYPE) { MOD_TYPE_NONE, UL_("General Digital Music"), "gdm" }, { MOD_TYPE_NONE, UL_("Un4seen MO3"), "mo3" }, + { MOD_TYPE_NONE, UL_("OggMod FastTracker 2"), "oxm" }, #ifndef NO_ARCHIVE_SUPPORT // Compressed modules { MOD_TYPE_MOD, UL_("Compressed ProTracker"), "mdz" }, @@ -112,7 +113,7 @@ static constexpr ModFormatInfo modFormatInfo[] = struct ModContainerInfo { MODCONTAINERTYPE format; // MOD_CONTAINERTYPE_XXXX - const MPT_UCHAR_TYPE *name; // "Unreal Music" + const mpt::uchar *name; // "Unreal Music" const char *extension; // "umx" }; @@ -175,21 +176,19 @@ std::vector CSoundFile::GetSupportedExtensions(bool otherFormats) } -static bool IsEqualExtension(const char *a, const char *b) +static bool IsEqualExtension(std::string_view a, std::string_view b) { - std::size_t lena = std::strlen(a); - std::size_t lenb = std::strlen(b); - if(lena != lenb) + if(a.length() != b.length()) { return false; } - return mpt::CompareNoCaseAscii(a, b, lena) == 0; + return mpt::CompareNoCaseAscii(a, b) == 0; } -bool CSoundFile::IsExtensionSupported(const char *ext) +bool CSoundFile::IsExtensionSupported(std::string_view ext) { - if(ext == nullptr || ext[0] == 0) + if(ext.length() == 0) { return false; } @@ -217,7 +216,7 @@ mpt::ustring CSoundFile::ModContainerTypeToString(MODCONTAINERTYPE containertype { if(containerInfo.format == containertype) { - return mpt::ToUnicode(mpt::CharsetUTF8, containerInfo.extension); + return mpt::ToUnicode(mpt::Charset::UTF8, containerInfo.extension); } } return mpt::ustring(); @@ -682,27 +681,29 @@ const int16 CResampler::FastSincTable[256*4] = // Compute Bessel function Izero(y) using a series approximation -static double izero(double y) +double Izero(double y) { - double s=1, ds=1, d=0; + double s = 1, ds = 1, d = 0; do { - d = d + 2; ds = ds * (y*y)/(d*d); + d = d + 2; + ds = ds * (y * y) / (d * d); s = s + ds; - } while (ds > 1E-7 * s); + } while(ds > 1E-7 * s); return s; } -static void getsinc(SINC_TYPE *psinc, double beta, double lowpass_factor) + +static void getsinc(SINC_TYPE *psinc, double beta, double cutoff) { - if(lowpass_factor >= 0.999) + if(cutoff >= 0.999) { // Avoid mixer overflows. // 1.0 itself does not make much sense. - lowpass_factor = 0.999; + cutoff = 0.999; } - const double izero_beta = izero(beta); - const double kPi = 4.0*atan(1.0)*lowpass_factor; + const double izeroBeta = Izero(beta); + const double kPi = 4.0 * std::atan(1.0) * cutoff; for (int isrc=0; isrc<8*SINC_PHASES; isrc++) { double fsinc; @@ -713,10 +714,11 @@ static void getsinc(SINC_TYPE *psinc, double beta, double lowpass_factor) fsinc = 1.0; } else { - double x = (double)(ix - (4*SINC_PHASES)) * (double)(1.0/SINC_PHASES); - fsinc = sin(x*kPi) * izero(beta*sqrt(1-x*x*(1.0/16.0))) / (izero_beta*x*kPi); // Kaiser window + const double x = (double)(ix - (4*SINC_PHASES)) * (double)(1.0/SINC_PHASES); + const double xPi = x * kPi; + fsinc = std::sin(xPi) * Izero(beta * std::sqrt(1 - x * x * (1.0 / 16.0))) / (izeroBeta * xPi); // Kaiser window } - double coeff = fsinc * lowpass_factor; + double coeff = fsinc * cutoff; #ifdef MPT_INTMIXER int n = (int)std::floor(coeff * (1< z) +{ + if(b == 0) + w[i] = z; + else + { + GenerateTwiddleFactors(i, b >> 1, z); + GenerateTwiddleFactors(i | b, b >> 1, z * w[b]); + } +} + + +TinyFFT::TinyFFT(const uint32 fftSize) + : w(std::size_t(1) << (fftSize - 1)) + , k(fftSize) +{ + const uint32 m = 1 << k; + constexpr double PI2_ = 6.28318530717958647692; + const double arg = -PI2_ / m; + for(uint32 i = 1, j = m / 4; j; i <<= 1, j >>= 1) + { + w[i] = std::exp(I * (arg * j)); + } + GenerateTwiddleFactors(0, m / 4, 1); +} + + +uint32 TinyFFT::Size() const noexcept +{ + return 1 << k; +} + + +// Computes in-place FFT of size 2^k of A, result is in bit-reversed order. +void TinyFFT::FFT(std::vector> &A) const +{ + MPT_ASSERT(A.size() == (std::size_t(1) << k)); + const uint32 m = 1 << k; + uint32 u = 1; + uint32 v = m / 4; + if(k & 1) + { + for(uint32 j = 0; j < m / 2; j++) + { + auto Ajv = A[j + (m / 2)]; + A[j + (m / 2)] = A[j] - Ajv; + A[j] += Ajv; + } + u <<= 1; + v >>= 1; + } + for(uint32 i = k & ~1; i > 0; i -= 2) + { + for(uint32 jh = 0; jh < u; jh++) + { + auto wj = w[jh << 1]; + auto wj2 = w[jh]; + auto wj3 = wj2 * wj; + for(uint32 j = jh << i, je = j + v; j < je; j++) + { + auto tmp0 = A[j]; + auto tmp1 = wj * A[j + v]; + auto tmp2 = wj2 * A[j + 2 * v]; + auto tmp3 = wj3 * A[j + 3 * v]; + + auto ttmp0 = tmp0 + tmp2; + auto ttmp2 = tmp0 - tmp2; + auto ttmp1 = tmp1 + tmp3; + auto ttmp3 = -I * (tmp1 - tmp3); + + A[j] = ttmp0 + ttmp1; + A[j + v] = ttmp0 - ttmp1; + A[j + 2 * v] = ttmp2 + ttmp3; + A[j + 3 * v] = ttmp2 - ttmp3; + } + } + u <<= 2; + v >>= 2; + } +} + + +// Computes in-place IFFT of size 2^k of A, input is expected to be in bit-reversed order. +void TinyFFT::IFFT(std::vector> &A) const +{ + MPT_ASSERT(A.size() == (std::size_t(1) << k)); + const uint32 m = 1 << k; + uint32 u = m / 4; + uint32 v = 1; + for(uint32 i = 2; i <= k; i += 2) + { + for(uint32 jh = 0; jh < u; jh++) + { + auto wj = std::conj(w[jh << 1]); + auto wj2 = std::conj(w[jh]); + auto wj3 = wj2 * wj; + for(uint32 j = jh << i, je = j + v; j < je; j++) + { + auto tmp0 = A[j]; + auto tmp1 = A[j + v]; + auto tmp2 = A[j + 2 * v]; + auto tmp3 = A[j + 3 * v]; + + auto ttmp0 = tmp0 + tmp1; + auto ttmp1 = tmp0 - tmp1; + auto ttmp2 = tmp2 + tmp3; + auto ttmp3 = I * (tmp2 - tmp3); + + A[j] = ttmp0 + ttmp2; + A[j + v] = wj * (ttmp1 + ttmp3); + A[j + 2 * v] = wj2 * (ttmp0 - ttmp2); + A[j + 3 * v] = wj3 * (ttmp1 - ttmp3); + } + } + u >>= 2; + v <<= 2; + } + if(k & 1) + { + for(uint32 j = 0; j < m / 2; j++) + { + auto Ajv = A[j + (m / 2)]; + A[j + (m / 2)] = A[j] - Ajv; + A[j] += Ajv; + } + } +} + + +void TinyFFT::Normalize(std::vector> &data) +{ + const double s = static_cast(data.size()); + for(auto &v : data) + v /= s; +} + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h b/Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h new file mode 100644 index 000000000..21229d0f9 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h @@ -0,0 +1,42 @@ +/* + * TinyFFT.h + * --------- + * Purpose: A simple FFT implementation for power-of-two FFTs + * Notes : This is a C++ adaption of Ryuhei Mori's BSD 2-clause licensed TinyFFT + * available from https://github.com/ryuhei-mori/tinyfft + * Authors: Ryuhei Mori + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" +#include + +OPENMPT_NAMESPACE_BEGIN + +class TinyFFT +{ + static constexpr std::complex I{0.0, 1.0}; + std::vector> w; // Pre-computed twiddle factors + const uint32 k; // log2 of FFT size + + void GenerateTwiddleFactors(uint32 i, uint32 b, std::complex z); + +public: + TinyFFT(const uint32 fftSize); + + uint32 Size() const noexcept; + + // Computes in-place FFT of size 2^k of A, result is in bit-reversed order. + void FFT(std::vector> &A) const; + + // Computes in-place IFFT of size 2^k of A, input is expected to be in bit-reversed order. + void IFFT(std::vector> &A) const; + + static void Normalize(std::vector> &data); +}; + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp index c614d583e..2b95d6182 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp @@ -43,7 +43,7 @@ struct UpgradePatternData { // Out-of-range global volume commands should be ignored in S3M. Fixed in OpenMPT 1.19 (r831). // So for tracks made with older versions of OpenMPT, we limit invalid global volume commands. - if(version < MAKE_VERSION_NUMERIC(1, 19, 00, 00) && m.command == CMD_GLOBALVOLUME) + if(version < MPT_V("1.19.00.00") && m.command == CMD_GLOBALVOLUME) { LimitMax(m.param, ModCommand::PARAM(64)); } @@ -51,8 +51,8 @@ struct UpgradePatternData else if(modType & (MOD_TYPE_IT | MOD_TYPE_MPT)) { - if(version < MAKE_VERSION_NUMERIC(1, 17, 03, 02) || - (!compatPlay && version < MAKE_VERSION_NUMERIC(1, 20, 00, 00))) + if(version < MPT_V("1.17.03.02") || + (!compatPlay && version < MPT_V("1.20.00.00"))) { if(m.command == CMD_GLOBALVOLUME) { @@ -80,14 +80,14 @@ struct UpgradePatternData // In the IT format, slide commands with both nibbles set should be ignored. // For note volume slides, OpenMPT 1.18 fixes this in compatible mode, OpenMPT 1.20 fixes this in normal mode as well. const bool noteVolSlide = - (version < MAKE_VERSION_NUMERIC(1, 18, 00, 00) || - (!compatPlay && version < MAKE_VERSION_NUMERIC(1, 20, 00, 00))) + (version < MPT_V("1.18.00.00") || + (!compatPlay && version < MPT_V("1.20.00.00"))) && (m.command == CMD_VOLUMESLIDE || m.command == CMD_VIBRATOVOL || m.command == CMD_TONEPORTAVOL || m.command == CMD_PANNINGSLIDE); // OpenMPT 1.20 also fixes this for global volume and channel volume slides. const bool chanVolSlide = - (version < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + (version < MPT_V("1.20.00.00")) && (m.command == CMD_GLOBALVOLSLIDE || m.command == CMD_CHANNELVOLSLIDE); @@ -95,12 +95,15 @@ struct UpgradePatternData { if((m.param & 0x0F) != 0x00 && (m.param & 0x0F) != 0x0F && (m.param & 0xF0) != 0x00 && (m.param & 0xF0) != 0xF0) { - m.param &= 0x0F; + if(m.command == CMD_GLOBALVOLSLIDE) + m.param &= 0xF0; + else + m.param &= 0x0F; } } - if(version < MAKE_VERSION_NUMERIC(1, 22, 01, 04) - && version != MAKE_VERSION_NUMERIC(1, 22, 00, 00)) // Ignore compatibility export + if(version < MPT_V("1.22.01.04") + && version != MPT_V("1.22.00.00")) // Ignore compatibility export { // OpenMPT 1.22.01.04 fixes illegal (out of range) instrument numbers; they should do nothing. In previous versions, they stopped the playing sample. if(sndFile.GetNumInstruments() && m.instr > sndFile.GetNumInstruments() && !compatPlay) @@ -115,16 +118,16 @@ struct UpgradePatternData { // Something made be believe that out-of-range global volume commands are ignored in XM // just like they are ignored in IT, but apparently they are not. Aaaaaargh! - if(((version >= MAKE_VERSION_NUMERIC(1, 17, 03, 02) && compatPlay) || (version >= MAKE_VERSION_NUMERIC(1, 20, 00, 00))) - && version < MAKE_VERSION_NUMERIC(1, 24, 02, 02) + if(((version >= MPT_V("1.17.03.02") && compatPlay) || (version >= MPT_V("1.20.00.00"))) + && version < MPT_V("1.24.02.02") && m.command == CMD_GLOBALVOLUME && m.param > 64) { m.command = CMD_NONE; } - if(version < MAKE_VERSION_NUMERIC(1, 19, 00, 00) - || (!compatPlay && version < MAKE_VERSION_NUMERIC(1, 20, 00, 00))) + if(version < MPT_V("1.19.00.00") + || (!compatPlay && version < MPT_V("1.20.00.00"))) { if(m.command == CMD_OFFSET && m.volcmd == VOLCMD_TONEPORTAMENTO) { @@ -134,7 +137,7 @@ struct UpgradePatternData } } - if(version < MAKE_VERSION_NUMERIC(1, 20, 01, 10) + if(version < MPT_V("1.20.01.10") && m.volcmd == VOLCMD_TONEPORTAMENTO && m.command == CMD_TONEPORTAMENTO && (m.vol != 0 || compatPlay) && m.param != 0) { @@ -145,7 +148,7 @@ struct UpgradePatternData m.param = mpt::saturate_cast(param); } - if(version < MAKE_VERSION_NUMERIC(1, 22, 07, 09) + if(version < MPT_V("1.22.07.09") && m.command == CMD_SPEED && m.param == 0) { // OpenMPT can emulate FT2's F00 behaviour now. @@ -153,7 +156,7 @@ struct UpgradePatternData } } - if(version < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + if(version < MPT_V("1.20.00.00")) { // Pattern Delay fixes @@ -161,7 +164,7 @@ struct UpgradePatternData // We also fix X6x commands in hacked XM files, since they are treated identically to the S6x command in IT/S3M files. // We don't treat them in files made with OpenMPT 1.18+ that have compatible play enabled, though, since they are ignored there anyway. const bool fixX6x = (m.command == CMD_XFINEPORTAUPDOWN && (m.param & 0xF0) == 0x60 - && (!(compatPlay && modType == MOD_TYPE_XM) || version < MAKE_VERSION_NUMERIC(1, 18, 00, 00))); + && (!(compatPlay && modType == MOD_TYPE_XM) || version < MPT_V("1.18.00.00"))); if(fixS6x || fixX6x) { @@ -192,8 +195,8 @@ struct UpgradePatternData } if(m.volcmd == VOLCMD_VIBRATODEPTH - && version < MAKE_VERSION_NUMERIC(1, 27, 00, 37) - && version != MAKE_VERSION_NUMERIC(1, 27, 00, 00)) + && version < MPT_V("1.27.00.37") + && version != MPT_V("1.27.00.00")) { // Fix handling of double vibrato commands - previously only one of them was applied at a time if(m.command == CMD_VIBRATOVOL && m.vol > 0) @@ -228,7 +231,7 @@ struct UpgradePatternData void CSoundFile::UpgradeModule() { - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 46) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.17.02.46") && m_dwLastSavedWithVersion != MPT_V("1.17.00.00")) { // Compatible playback mode didn't exist in earlier versions, so definitely disable it. m_playBehaviour.reset(MSF_COMPATIBLE_PLAY); @@ -237,22 +240,22 @@ void CSoundFile::UpgradeModule() const bool compatModeIT = m_playBehaviour[MSF_COMPATIBLE_PLAY] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)); const bool compatModeXM = m_playBehaviour[MSF_COMPATIBLE_PLAY] && GetType() == MOD_TYPE_XM; - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.20.00.00")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { ModInstrument *ins = Instruments[i]; // Previously, volume swing values ranged from 0 to 64. They should reach from 0 to 100 instead. - ins->nVolSwing = static_cast(std::min(ins->nVolSwing * 100 / 64, 100)); + ins->nVolSwing = static_cast(std::min(static_cast(ins->nVolSwing * 100 / 64), uint32(100))); - if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) + if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { // Previously, Pitch/Pan Separation was only half depth. // This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well. ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2; } - if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02)) + if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.17.03.02")) { // IT compatibility 24. Short envelope loops // Previously, the pitch / filter envelope loop handling was broken, the loop was shortened by a tick (like in XM). @@ -260,7 +263,7 @@ void CSoundFile::UpgradeModule() ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType()); } - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50)) + if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00") && m_dwLastSavedWithVersion < MPT_V("1.17.02.50")) { // If there are any plugins that can receive volume commands, enable volume bug emulation. if(ins->nMixPlug && ins->HasValidMIDIChannel()) @@ -269,7 +272,7 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50) && (ins->nVolSwing | ins->nPanSwing | ins->nCutSwing | ins->nResSwing)) + if(m_dwLastSavedWithVersion < MPT_V("1.17.02.50") && (ins->nVolSwing | ins->nPanSwing | ins->nCutSwing | ins->nResSwing)) { // If there are any instruments with random variation, enable the old random variation behaviour. m_playBehaviour.set(kMPTOldSwingBehaviour); @@ -277,7 +280,7 @@ void CSoundFile::UpgradeModule() } } - if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02) || !compatModeIT)) + if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_dwLastSavedWithVersion < MPT_V("1.17.03.02") || !compatModeIT)) { // In the IT format, a sweep value of 0 shouldn't apply vibrato at all. Previously, a value of 0 was treated as "no sweep". // In OpenMPT 1.17.03.02, this was corrected in compatible mode, in OpenMPT 1.20 it is corrected in normal mode as well, @@ -295,8 +298,8 @@ void CSoundFile::UpgradeModule() m_MidiCfg.UpgradeMacros(); } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 02, 10) - && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00) + if(m_dwLastSavedWithVersion < MPT_V("1.20.02.10") + && m_dwLastSavedWithVersion != MPT_V("1.20.00.00") && (GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT))) { bool instrPlugs = false; @@ -315,8 +318,8 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 03, 12) - && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 22, 00, 00) + if(m_dwLastSavedWithVersion < MPT_V("1.22.03.12") + && m_dwLastSavedWithVersion != MPT_V("1.22.00.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_playBehaviour[MSF_COMPATIBLE_PLAY] || m_playBehaviour[kMPTOldSwingBehaviour])) { @@ -332,17 +335,17 @@ void CSoundFile::UpgradeModule() } #ifndef NO_PLUGINS - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 01)) + if(m_dwLastSavedWithVersion < MPT_V("1.22.07.01")) { // Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name) for(auto &plugin : m_MixPlugins) { #if defined(MODPLUG_TRACKER) - const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, plugin.Info.szLibraryName); + const std::string name = mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, plugin.Info.szLibraryName); #else - const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetWindows1252, plugin.Info.szLibraryName); + const std::string name = mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Windows1252, plugin.Info.szLibraryName); #endif - mpt::String::Copy(plugin.Info.szLibraryName, name); + plugin.Info.szLibraryName = name; } } #endif // NO_PLUGINS @@ -351,15 +354,15 @@ void CSoundFile::UpgradeModule() // Starting from OpenMPT 1.23.01.04, FT2-style panning has its own mix mode instead. if(GetType() == MOD_TYPE_XM) { - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 22, 07, 19) - && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 23, 01, 04) + if(m_dwLastSavedWithVersion >= MPT_V("1.22.07.19") + && m_dwLastSavedWithVersion < MPT_V("1.23.01.04") && GetMixLevels() == mixLevelsCompatible) { SetMixLevels(mixLevelsCompatibleFT2); } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 25, 00, 07) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 25, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.25.00.07") && m_dwLastSavedWithVersion != MPT_V("1.25.00.00")) { // Instrument plugins can now receive random volume variation. // For old instruments, disable volume swing in case there was no sample associated. @@ -384,7 +387,7 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { @@ -394,14 +397,14 @@ void CSoundFile::UpgradeModule() // OpenMPT 1.18 fixed the depth of random pan in compatible mode. // OpenMPT 1.26 fixes it in normal mode too. - if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) + if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { ins->nPanSwing = (ins->nPanSwing + 3) / 4u; } } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 00, 12)) + if(m_dwLastSavedWithVersion < MPT_V("1.28.00.12")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { @@ -413,7 +416,7 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 03, 04)) + if(m_dwLastSavedWithVersion < MPT_V("1.28.03.04")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if (Instruments[i] != nullptr) { @@ -437,95 +440,95 @@ void CSoundFile::UpgradeModule() Version version; }; - if(compatModeIT && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + if(compatModeIT && m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { - { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITInstrWithoutNote, MAKE_VERSION_NUMERIC(1, 17, 02, 46) }, - { kITVolColFinePortamento, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITArpeggio, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITOutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPortaMemoryShare, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPatternLoopTargetReset, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPingPongNoReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, - { kITEnvelopeReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, - { kITClearOldNoteAfterCut, MAKE_VERSION_NUMERIC(1, 17, 02, 52) }, - { kITVibratoTremoloPanbrello, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITTremor, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITRetrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITMultiSampleBehaviour, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITPortaTargetReached, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITPatternLoopBreak, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITOffset, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITSwingBehaviour, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kITNNAReset, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kITSCxStopsSample, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, - { kITEnvelopePositionHandling, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, - { kITPortamentoInstrument, MAKE_VERSION_NUMERIC(1, 19, 00, 01) }, - { kITPingPongMode, MAKE_VERSION_NUMERIC(1, 19, 00, 21) }, - { kITRealNoteMapping, MAKE_VERSION_NUMERIC(1, 19, 00, 30) }, - { kITHighOffsetNoRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 14) }, - { kITFilterBehaviour, MAKE_VERSION_NUMERIC(1, 20, 00, 35) }, - { kITNoSurroundPan, MAKE_VERSION_NUMERIC(1, 20, 00, 53) }, - { kITShortSampleRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 54) }, - { kITPortaNoNote, MAKE_VERSION_NUMERIC(1, 20, 00, 56) }, - { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 76) }, - { kITDontResetNoteOffOnPorta, MAKE_VERSION_NUMERIC(1, 20, 02, 06) }, - { kITVolColMemory, MAKE_VERSION_NUMERIC(1, 21, 01, 16) }, - { kITPortamentoSwapResetsPos, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, - { kITEmptyNoteMapSlot, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, - { kITFirstTickHandling, MAKE_VERSION_NUMERIC(1, 22, 07, 09) }, - { kITSampleAndHoldPanbrello, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, - { kITClearPortaTarget, MAKE_VERSION_NUMERIC(1, 23, 04, 03) }, - { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, - { kITPanningReset, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, - { kITPatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 25, 00, 19) }, + { kTempoClamp, MPT_V("1.17.03.02") }, + { kPerChannelGlobalVolSlide, MPT_V("1.17.03.02") }, + { kPanOverride, MPT_V("1.17.03.02") }, + { kITInstrWithoutNote, MPT_V("1.17.02.46") }, + { kITVolColFinePortamento, MPT_V("1.17.02.49") }, + { kITArpeggio, MPT_V("1.17.02.49") }, + { kITOutOfRangeDelay, MPT_V("1.17.02.49") }, + { kITPortaMemoryShare, MPT_V("1.17.02.49") }, + { kITPatternLoopTargetReset, MPT_V("1.17.02.49") }, + { kITFT2PatternLoop, MPT_V("1.17.02.49") }, + { kITPingPongNoReset, MPT_V("1.17.02.51") }, + { kITEnvelopeReset, MPT_V("1.17.02.51") }, + { kITClearOldNoteAfterCut, MPT_V("1.17.02.52") }, + { kITVibratoTremoloPanbrello, MPT_V("1.17.03.02") }, + { kITTremor, MPT_V("1.17.03.02") }, + { kITRetrigger, MPT_V("1.17.03.02") }, + { kITMultiSampleBehaviour, MPT_V("1.17.03.02") }, + { kITPortaTargetReached, MPT_V("1.17.03.02") }, + { kITPatternLoopBreak, MPT_V("1.17.03.02") }, + { kITOffset, MPT_V("1.17.03.02") }, + { kITSwingBehaviour, MPT_V("1.18.00.00") }, + { kITNNAReset, MPT_V("1.18.00.00") }, + { kITSCxStopsSample, MPT_V("1.18.00.01") }, + { kITEnvelopePositionHandling, MPT_V("1.18.01.00") }, + { kITPortamentoInstrument, MPT_V("1.19.00.01") }, + { kITPingPongMode, MPT_V("1.19.00.21") }, + { kITRealNoteMapping, MPT_V("1.19.00.30") }, + { kITHighOffsetNoRetrig, MPT_V("1.20.00.14") }, + { kITFilterBehaviour, MPT_V("1.20.00.35") }, + { kITNoSurroundPan, MPT_V("1.20.00.53") }, + { kITShortSampleRetrig, MPT_V("1.20.00.54") }, + { kITPortaNoNote, MPT_V("1.20.00.56") }, + { kRowDelayWithNoteDelay, MPT_V("1.20.00.76") }, + { kITFT2DontResetNoteOffOnPorta, MPT_V("1.20.02.06") }, + { kITVolColMemory, MPT_V("1.21.01.16") }, + { kITPortamentoSwapResetsPos, MPT_V("1.21.01.25") }, + { kITEmptyNoteMapSlot, MPT_V("1.21.01.25") }, + { kITFirstTickHandling, MPT_V("1.22.07.09") }, + { kITSampleAndHoldPanbrello, MPT_V("1.22.07.19") }, + { kITClearPortaTarget, MPT_V("1.23.04.03") }, + { kITPanbrelloHold, MPT_V("1.24.01.06") }, + { kITPanningReset, MPT_V("1.24.01.06") }, + { kITPatternLoopWithJumpsOld, MPT_V("1.25.00.19") }, }; for(const auto &b : behaviours) { m_playBehaviour.set(b.behaviour, (m_dwLastSavedWithVersion >= b.version || m_dwLastSavedWithVersion == b.version.Masked(0xFFFF0000u))); } - } else if(compatModeXM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + } else if(compatModeXM && m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { - { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2Arpeggio, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2Retrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2VolColVibrato, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2PortaNoNote, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2KeyOff, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2PanSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2OffsetOutOfRange, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2RestrictXCommand, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2RetrigWithNoteDelay, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2SetPanEnvPos, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2PortaIgnoreInstr, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, - { kFT2VolColMemory, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, - { kFT2LoopE60Restart, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, - { kFT2ProcessSilentChannels, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, - { kFT2ReloadSampleSettings, MAKE_VERSION_NUMERIC(1, 20, 00, 36) }, - { kFT2PortaDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 40) }, - { kFT2Transpose, MAKE_VERSION_NUMERIC(1, 20, 00, 62) }, - { kFT2PatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, - { kFT2PortaTargetNoReset, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, - { kFT2EnvelopeEscape, MAKE_VERSION_NUMERIC(1, 20, 00, 77) }, - { kFT2Tremor, MAKE_VERSION_NUMERIC(1, 20, 01, 11) }, - { kFT2OutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 20, 02, 02) }, - { kFT2Periods, MAKE_VERSION_NUMERIC(1, 22, 03, 01) }, - { kFT2PanWithDelayedNoteOff, MAKE_VERSION_NUMERIC(1, 22, 03, 02) }, - { kFT2VolColDelay, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, - { kFT2FinetunePrecision, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, + { kTempoClamp, MPT_V("1.17.03.02") }, + { kPerChannelGlobalVolSlide, MPT_V("1.17.03.02") }, + { kPanOverride, MPT_V("1.17.03.02") }, + { kITFT2PatternLoop, MPT_V("1.17.03.02") }, + { kFT2Arpeggio, MPT_V("1.17.03.02") }, + { kFT2Retrigger, MPT_V("1.17.03.02") }, + { kFT2VolColVibrato, MPT_V("1.17.03.02") }, + { kFT2PortaNoNote, MPT_V("1.17.03.02") }, + { kFT2KeyOff, MPT_V("1.17.03.02") }, + { kFT2PanSlide, MPT_V("1.17.03.02") }, + { kFT2ST3OffsetOutOfRange, MPT_V("1.17.03.02") }, + { kFT2RestrictXCommand, MPT_V("1.18.00.00") }, + { kFT2RetrigWithNoteDelay, MPT_V("1.18.00.00") }, + { kFT2SetPanEnvPos, MPT_V("1.18.00.00") }, + { kFT2PortaIgnoreInstr, MPT_V("1.18.00.01") }, + { kFT2VolColMemory, MPT_V("1.18.01.00") }, + { kFT2LoopE60Restart, MPT_V("1.18.02.01") }, + { kFT2ProcessSilentChannels, MPT_V("1.18.02.01") }, + { kFT2ReloadSampleSettings, MPT_V("1.20.00.36") }, + { kFT2PortaDelay, MPT_V("1.20.00.40") }, + { kFT2Transpose, MPT_V("1.20.00.62") }, + { kFT2PatternLoopWithJumps, MPT_V("1.20.00.69") }, + { kFT2PortaTargetNoReset, MPT_V("1.20.00.69") }, + { kFT2EnvelopeEscape, MPT_V("1.20.00.77") }, + { kFT2Tremor, MPT_V("1.20.01.11") }, + { kFT2OutOfRangeDelay, MPT_V("1.20.02.02") }, + { kFT2Periods, MPT_V("1.22.03.01") }, + { kFT2PanWithDelayedNoteOff, MPT_V("1.22.03.02") }, + { kFT2VolColDelay, MPT_V("1.22.07.19") }, + { kFT2FinetunePrecision, MPT_V("1.22.07.19") }, }; for(const auto &b : behaviours) @@ -539,9 +542,12 @@ void CSoundFile::UpgradeModule() // The following behaviours were added in/after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { - { kITInstrWithNoteOff, MAKE_VERSION_NUMERIC(1, 26, 00, 01) }, - { kITMultiSampleInstrumentNumber, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, - { kITInstrWithNoteOffOldEffects, MAKE_VERSION_NUMERIC(1, 28, 02, 06) }, + { kITInstrWithNoteOff, MPT_V("1.26.00.01") }, + { kITMultiSampleInstrumentNumber, MPT_V("1.27.00.27") }, + { kITInstrWithNoteOffOldEffects, MPT_V("1.28.02.06") }, + { kITDoNotOverrideChannelPan, MPT_V("1.29.00.22") }, + { kITPatternLoopWithJumps, MPT_V("1.29.00.32") }, + { kITDCTBehaviour, MPT_V("1.29.00.57") }, }; for(const auto &b : behaviours) @@ -557,12 +563,13 @@ void CSoundFile::UpgradeModule() // The following behaviours were added after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { - { kFT2NoteOffFlags, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, - { 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) }, + { kFT2NoteOffFlags, MPT_V("1.27.00.27") }, + { kRowDelayWithNoteDelay, MPT_V("1.27.00.37") }, + { kFT2TremoloRampWaveform, MPT_V("1.27.00.37") }, + { kFT2PortaUpDownMemory, MPT_V("1.27.00.37") }, + { kFT2PanSustainRelease, MPT_V("1.28.00.09") }, + { kFT2NoteDelayWithoutInstr, MPT_V("1.28.00.44") }, + { kITFT2DontResetNoteOffOnPorta, MPT_V("1.29.00.34" )}, }; for(const auto &b : behaviours) @@ -575,14 +582,16 @@ 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) }, - { kST3OffsetWithoutInstrument, MAKE_VERSION_NUMERIC(1, 28, 00, 00) }, + { kST3NoMutedChannels, MPT_V("1.18.00.00") }, + { kST3EffectMemory, MPT_V("1.20.00.00") }, + { kRowDelayWithNoteDelay, MPT_V("1.20.00.00") }, + { kST3PortaSampleChange, MPT_V("1.22.00.00") }, + { kST3VibratoMemory, MPT_V("1.26.00.00") }, + { kITPanbrelloHold, MPT_V("1.26.00.00") }, + { KST3PortaAfterArpeggio, MPT_V("1.27.00.00") }, + { kST3OffsetWithoutInstrument, MPT_V("1.28.00.00") }, + { kST3RetrigAfterNoteCut, MPT_V("1.29.00.00") }, + { kFT2ST3OffsetOutOfRange, MPT_V("1.29.00.00") }, }; for(const auto &b : behaviours) @@ -592,13 +601,13 @@ void CSoundFile::UpgradeModule() } } - if(GetType() == MOD_TYPE_XM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00)) + if(GetType() == MOD_TYPE_XM && m_dwLastSavedWithVersion < MPT_V("1.19.00.00")) { // This bug was introduced sometime between 1.18.03.00 and 1.19.01.00 m_playBehaviour.set(kFT2NoteDelayWithoutInstr); } - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 27, 00, 27) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 27, 00, 49)) + if(m_dwLastSavedWithVersion >= MPT_V("1.27.00.27") && m_dwLastSavedWithVersion < MPT_V("1.27.00.49")) { // OpenMPT 1.27 inserted some IT/FT2 flags before the S3M flags that are never saved to files anyway, to keep the flag IDs a bit more compact. // However, it was overlooked that these flags would still be read by OpenMPT 1.26 and thus S3M-specific behaviour would be enabled in IT/XM files. @@ -611,28 +620,28 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.17.00.00")) { // MPT 1.16 has a maximum tempo of 255. m_playBehaviour.set(kTempoClamp); - } else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 20, 01, 03) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + } else if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00") && m_dwLastSavedWithVersion <= MPT_V("1.20.01.03") && m_dwLastSavedWithVersion != MPT_V("1.20.00.00")) { // OpenMPT introduced some "fixes" that execute regular portamentos also at speed 1. m_playBehaviour.set(kSlidesAtSpeed1); } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 24, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.24.00.00")) { // No frequency slides in Hz before OpenMPT 1.24 m_playBehaviour.reset(kHertzInLinearMode); - } else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 24, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00) && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) + } else if(m_dwLastSavedWithVersion >= MPT_V("1.24.00.00") && m_dwLastSavedWithVersion < MPT_V("1.26.00.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { // Frequency slides were always in Hz rather than periods in this version range. m_playBehaviour.set(kHertzInLinearMode); } if(m_playBehaviour[kITEnvelopePositionHandling] - && m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 23, 01, 02) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 00, 43)) + && m_dwLastSavedWithVersion >= MPT_V("1.23.01.02") && m_dwLastSavedWithVersion < MPT_V("1.28.00.43")) { // Bug that effectively clamped the release node to the sustain end for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) @@ -646,6 +655,18 @@ void CSoundFile::UpgradeModule() } } } + + if(GetType() == MOD_TYPE_MPT && GetNumInstruments() && m_dwLastSavedWithVersion >= MPT_V("1.28.00.20") && m_dwLastSavedWithVersion <= MPT_V("1.29.55.00")) + { + for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) + { + if(Samples[i].uFlags[CHN_ADLIB]) + { + m_playBehaviour.set(kOPLNoResetAtEnvelopeEnd); + break; + } + } + } } OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp index 9640159af..d8bf338e5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp @@ -163,30 +163,30 @@ uint16 WAVReader::GetFileCodePage(ChunkReader::ChunkList &chunks) 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)) + Version version = Version::Parse(mpt::ToUnicode(mpt::Charset::ISO8859_1, versionString)); + if(version && version < MPT_V("1.28.00.02")) { - return 1252; // mpt::CharsetWindows1252; // OpenMPT up to and including 1.28.00.01 wrote metadata in windows-1252 encoding + return 1252; // mpt::Charset::Windows1252; // OpenMPT up to and including 1.28.00.01 wrote metadata in windows-1252 encoding } else { - return 28591; // mpt::CharsetISO8859_1; // as per spec + return 28591; // mpt::Charset::ISO8859_1; // as per spec } } else { - return 28591; // mpt::CharsetISO8859_1; // as per spec + return 28591; // mpt::Charset::ISO8859_1; // as per spec } } if(!csetChunk.CanRead(2)) { // chunk not parsable - return 28591; // mpt::CharsetISO8859_1; + return 28591; // mpt::Charset::ISO8859_1; } uint16 codepage = csetChunk.ReadUint16LE(); return codepage; } -void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, char (&sampleName)[MAX_SAMPLENAME]) +void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, mpt::charbuf &sampleName) { // Read sample name FileReader textChunk = infoChunk.GetChunk(RIFFChunk::idINAM); @@ -194,12 +194,12 @@ void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharse { std::string sampleNameEncoded; textChunk.ReadString(sampleNameEncoded, textChunk.GetLength()); - mpt::String::Copy(sampleName, mpt::ToCharset(sampleCharset, mpt::ToUnicode(codePage, mpt::CharsetWindows1252, sampleNameEncoded))); + sampleName = mpt::ToCharset(sampleCharset, mpt::ToUnicode(codePage, mpt::Charset::Windows1252, sampleNameEncoded)); } if(isDLS) { // DLS sample -> sample filename - mpt::String::Copy(sample.filename, sampleName); + sample.filename = sampleName; } // Read software name @@ -264,9 +264,9 @@ void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharse { if(mptInfo.flags & WAVExtraChunk::setPanning) sample.uFlags.set(CHN_PANNING); - sample.nPan = std::min(mptInfo.defaultPan, 256); - sample.nVolume = std::min(mptInfo.defaultVolume, 256); - sample.nGlobalVol = std::min(mptInfo.globalVolume, 64); + sample.nPan = std::min(static_cast(mptInfo.defaultPan), uint16(256)); + sample.nVolume = std::min(static_cast(mptInfo.defaultVolume), uint16(256)); + sample.nGlobalVol = std::min(static_cast(mptInfo.globalVolume), uint16(64)); sample.nVibType = static_cast(mptInfo.vibratoType.get()); sample.nVibSweep = mptInfo.vibratoSweep; sample.nVibDepth = mptInfo.vibratoDepth; @@ -365,6 +365,7 @@ size_t WAVWriter::Finalize() FinalizeChunk(); RIFFHeader fileHeader; + Clear(fileHeader); fileHeader.magic = RIFFHeader::idRIFF; fileHeader.length = static_cast(totalSize - 8); fileHeader.type = RIFFHeader::idWAVE; @@ -433,7 +434,7 @@ void WAVWriter::Write(const void *data, size_t numBytes) { if(s != nullptr) { - s->write(static_cast(data), numBytes); + s->write(mpt::void_cast(data), numBytes); } else if(!memory.empty()) { if(position <= memory.size() && numBytes <= memory.size() - position) @@ -455,6 +456,7 @@ void WAVWriter::WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChanne { StartChunk(RIFFChunk::idfmt_); WAVFormatChunk wavFormat; + Clear(wavFormat); bool extensible = (numChannels > 2); @@ -470,6 +472,7 @@ void WAVWriter::WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChanne if(extensible) { WAVFormatChunkExtension extFormat; + Clear(extFormat); extFormat.size = sizeof(WAVFormatChunkExtension) - sizeof(uint16); extFormat.validBitsPerSample = bitDepth; switch(numChannels) @@ -525,13 +528,14 @@ 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::CharsetUTF8, utext); + std::string text = mpt::ToCharset(mpt::Charset::UTF8, utext); text = text.substr(0, uint32_max - 1u); if(!text.empty()) { const uint32 length = mpt::saturate_cast(text.length() + 1); RIFFChunk chunk; + Clear(chunk); chunk.id = static_cast(id); chunk.length = length; Write(chunk); @@ -567,6 +571,7 @@ void WAVWriter::WriteLoopInformation(const ModSample &sample) // Set up loops WAVSampleLoop loops[2]; + Clear(loops); if(sample.uFlags[CHN_SUSTAINLOOP]) { loops[info.numLoops++].ConvertToWAV(sample.nSustainStart, sample.nSustainEnd, sample.uFlags[CHN_PINGPONGSUSTAIN]); @@ -624,11 +629,11 @@ void WAVWriter::WriteExtraInformation(const ModSample &sample, MODTYPE modType, // also specify encoding. char name[MAX_SAMPLENAME]; - mpt::String::Write(name, sampleName, MAX_SAMPLENAME); + mpt::String::WriteBuf(mpt::String::nullTerminated, name) = sampleName; WriteArray(name); char filename[MAX_SAMPLEFILENAME]; - mpt::String::Write(filename, sample.filename); + mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = sample.filename; WriteArray(filename); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h index 8d3e6e4e0..97d44c04f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h @@ -322,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, mpt::Charset charset, char (&sampleCharset)[MAX_SAMPLENAME]); + void ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, mpt::charbuf &sampleName); }; @@ -373,12 +373,12 @@ public: template void Write(const T &data) { - MPT_STATIC_ASSERT((mpt::is_binary_safe::value)); + static_assert((mpt::is_binary_safe::value)); Write(&data, sizeof(T)); } // Write a buffer to the file. - void WriteBuffer(const char *data, size_t size) + void WriteBuffer(const std::byte *data, size_t size) { Write(data, size); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp index a9dbfd965..eb799bd04 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp @@ -22,7 +22,7 @@ OPENMPT_NAMESPACE_BEGIN // Convert OpenMPT's internal envelope representation to XM envelope data. void XMInstrument::ConvertEnvelopeToXM(const InstrumentEnvelope &mptEnv, uint8le &numPoints, uint8le &flags, uint8le &sustain, uint8le &loopStart, uint8le &loopEnd, EnvType env) { - numPoints = static_cast(std::min(12u, mptEnv.size())); + numPoints = static_cast(std::min(std::size_t(12), static_cast(mptEnv.size()))); // Envelope Data for(uint8 i = 0; i < numPoints; i++) @@ -125,7 +125,7 @@ std::vector XMInstrument::GetSampleList(const ModInstrument &mptIns // Convert XM envelope data to an OpenMPT's internal envelope representation. void XMInstrument::ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoints, uint8 flags, uint8 sustain, uint8 loopStart, uint8 loopEnd, EnvType env) const { - mptEnv.resize(std::min(numPoints, 12)); + mptEnv.resize(std::min(numPoints, uint8(12))); // Envelope Data for(uint32 i = 0; i < mptEnv.size(); i++) @@ -142,16 +142,15 @@ void XMInstrument::ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoi break; } - if(i > 0 && mptEnv[i].tick < mptEnv[i - 1].tick) + if(i > 0 && mptEnv[i].tick < mptEnv[i - 1].tick && !(mptEnv[i].tick & 0xFF00)) { // libmikmod code says: "Some broken XM editing program will only save the low byte of the position // value. Try to compensate by adding the missing high byte." - // Note: It appears that MPT 1.07's XI instrument saver omitted the high byte of envelope nodes. + // Note: MPT 1.07's XI instrument saver omitted the high byte of envelope nodes. // This might be the source for some broken envelopes in IT and XM files. - - mptEnv[i].tick &= 0xFF; - mptEnv[i].tick += mptEnv[i - 1].tick & 0xFF00; - if(mptEnv[i].tick < mptEnv[i - 1].tick) mptEnv[i].tick += 0x100; + mptEnv[i].tick |= mptEnv[i - 1].tick & 0xFF00; + if(mptEnv[i].tick < mptEnv[i - 1].tick) + mptEnv[i].tick += 0x100; } } @@ -194,7 +193,7 @@ void XMInstrument::ConvertToMPT(ModInstrument &mptIns) const { mptIns.nMidiChannel = midiChannel + MidiFirstChannel; Limit(mptIns.nMidiChannel, uint8(MidiFirstChannel), uint8(MidiLastChannel)); - mptIns.nMidiProgram = static_cast(std::min(midiProgram, 127) + 1); + mptIns.nMidiProgram = static_cast(std::min(static_cast(midiProgram), uint16(127)) + 1); } mptIns.midiPWD = static_cast(pitchWheelRange); } @@ -248,7 +247,7 @@ void XMInstrumentHeader::Finalise() void XMInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); - mpt::String::Write(name, mptIns.name); + mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; type = mptIns.nMidiProgram; // If FT2 writes crap here, we can do so, too! (we probably shouldn't, though. This is just for backwards compatibility with old MPT versions.) } @@ -271,7 +270,7 @@ void XMInstrumentHeader::ConvertToMPT(ModInstrument &mptIns) const } } - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); // Old MPT backwards compatibility if(!instrument.midiEnabled) @@ -287,11 +286,11 @@ void XIInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibi numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); memcpy(signature, "Extended Instrument: ", 21); - mpt::String::Write(name, mptIns.name); + mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; eof = 0x1A; - const std::string openMptTrackerName = mpt::ToCharset(mpt::CharsetCP437, Version::Current().GetOpenMPTVersionString()); - mpt::String::Write(trackerName, openMptTrackerName); + const std::string openMptTrackerName = mpt::ToCharset(mpt::Charset::CP437, Version::Current().GetOpenMPTVersionString()); + mpt::String::WriteBuf(mpt::String::spacePadded, trackerName) = openMptTrackerName; version = 0x102; } @@ -311,7 +310,7 @@ void XIInstrumentHeader::ConvertToMPT(ModInstrument &mptIns) const } } - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); } @@ -332,8 +331,8 @@ void XMSample::ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compa } else { int f2t = ModSample::FrequencyToTranspose(mptSmp.nC5Speed); - relnote = (int8)(f2t >> 7); - finetune = (int8)(f2t & 0x7F); + relnote = static_cast(f2t / 128); + finetune = static_cast(f2t & 0x7F); } flags = 0; @@ -401,7 +400,7 @@ void XMSample::ConvertToMPT(ModSample &mptSmp) const mptSmp.nLoopEnd /= 2; } - if((flags & (XMSample::sampleLoop | XMSample::sampleBidiLoop)) && mptSmp.nLoopStart < mptSmp.nLength && mptSmp.nLoopEnd > mptSmp.nLoopStart) + if((flags & (XMSample::sampleLoop | XMSample::sampleBidiLoop)) && mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); if((flags & XMSample::sampleBidiLoop)) @@ -410,7 +409,7 @@ void XMSample::ConvertToMPT(ModSample &mptSmp) const } } - strcpy(mptSmp.filename, ""); + mptSmp.filename = ""; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp index 0e923b59b..46f8b01d2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp @@ -22,14 +22,16 @@ #endif -//#define J2B_LOG +#ifdef MPT_ALL_LOGGING +#define J2B_LOG +#endif OPENMPT_NAMESPACE_BEGIN // First off, a nice vibrato translation LUT. -static const VibratoType j2bAutoVibratoTrans[] = +static constexpr VibratoType j2bAutoVibratoTrans[] = { VIB_SINE, VIB_SQUARE, VIB_RAMP_UP, VIB_RAMP_DOWN, VIB_RANDOM, }; @@ -203,10 +205,10 @@ struct AMFFInstrumentHeader // Convert instrument data to OpenMPT's internal format. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX baseSample) { - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name); - STATIC_ASSERT(CountOf(sampleMap) <= CountOf(mptIns.Keyboard)); - for(size_t i = 0; i < CountOf(sampleMap); i++) + static_assert(mpt::array_size::size <= mpt::array_size::size); + for(size_t i = 0; i < std::size(sampleMap); i++) { mptIns.Keyboard[i] = sampleMap[i] + baseSample + 1; } @@ -259,7 +261,7 @@ struct AMFFSampleHeader mptSmp.nLoopEnd = loopEnd; mptSmp.nC5Speed = sampleRate; - if(instrHeader.vibratoType < mpt::size(j2bAutoVibratoTrans)) + if(instrHeader.vibratoType < std::size(j2bAutoVibratoTrans)) mptSmp.nVibType = j2bAutoVibratoTrans[instrHeader.vibratoType]; mptSmp.nVibSweep = static_cast(instrHeader.vibratoSweep); mptSmp.nVibRate = static_cast(instrHeader.vibratoRate / 16); @@ -380,10 +382,10 @@ struct AMInstrumentHeader // Convert instrument data to OpenMPT's internal format. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX baseSample) { - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name); - STATIC_ASSERT(CountOf(sampleMap) <= CountOf(mptIns.Keyboard)); - for(uint8 i = 0; i < CountOf(sampleMap); i++) + static_assert(mpt::array_size::size <= mpt::array_size::size); + for(uint8 i = 0; i < std::size(sampleMap); i++) { mptIns.Keyboard[i] = sampleMap[i] + baseSample + 1; } @@ -422,8 +424,8 @@ struct AMSampleHeader void ConvertToMPT(AMInstrumentHeader &instrHeader, ModSample &mptSmp) const { mptSmp.Initialize(); - mptSmp.nPan = std::min(pan, 32767) * 256 / 32767; - mptSmp.nVolume = std::min(volume, 32767) * 256 / 32767; + mptSmp.nPan = std::min(pan.get(), uint16(32767)) * 256 / 32767; + mptSmp.nVolume = std::min(volume.get(), uint16(32767)) * 256 / 32767; mptSmp.nGlobalVol = 64; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; @@ -469,7 +471,7 @@ MPT_BINARY_STRUCT(AMSampleHeader, 60) static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSoundFile &sndFile) { // Effect translation LUT - static const EffectCommand amEffTrans[] = + static constexpr EffectCommand amEffTrans[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, @@ -516,7 +518,7 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou continue; } - ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min((flags & channelMask), channels - 1)); + ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min(static_cast(flags & channelMask), static_cast(channels - 1))); if(flags & dataFlag) { @@ -532,7 +534,7 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou } else { #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))); + MPT_LOG(LogDebug, "J2B", mpt::uformat(U_("J2B: Unknown command: 0x%1, param 0x%2"))(mpt::ufmt::HEX0<2>(command), mpt::ufmt::HEX0<2>(m.param))); #endif m.command = CMD_NONE; } @@ -560,7 +562,7 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou if (m.param & 0xF0) m.param &= 0xF0; break; case CMD_PANNING8: - if(m.param <= 0x80) m.param = MIN(m.param << 1, 0xFF); + if(m.param <= 0x80) m.param = mpt::saturate_cast(m.param * 2); else if(m.param == 0xA4) {m.command = CMD_S3MCMDEX; m.param = 0x91;} break; case CMD_PATTERNBREAK: @@ -589,8 +591,9 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou if (flags & noteFlag) // note + ins { - m.instr = chunk.ReadUint8(); - m.note = chunk.ReadUint8(); + const auto [instr, note] = chunk.ReadArray(); + m.instr = instr; + m.note = note; if(m.note == 0x80) m.note = NOTE_KEYOFF; else if(m.note > 0x80) m.note = NOTE_FADE; // I guess the support for IT "note fade" notes was not intended in mod2j2b, but hey, it works! :-D } @@ -731,16 +734,16 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX; m_SongFlags.set(SONG_LINEARSLIDES, !(mainChunk.flags & AMFFMainChunk::amigaSlides)); - m_nChannels = MIN(mainChunk.channels, MAX_BASECHANNELS); + m_nChannels = std::min(static_cast(mainChunk.channels), static_cast(MAX_BASECHANNELS)); m_nDefaultSpeed = mainChunk.speed; m_nDefaultTempo.Set(mainChunk.tempo); m_nDefaultGlobalVolume = mainChunk.globalvolume * 2; m_modFormat.formatName = isAM ? UL_("Galaxy Sound System (new version)") : UL_("Galaxy Sound System (old version)"); m_modFormat.type = U_("j2b"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, mainChunk.songname); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, mainChunk.songname); // It seems like there's no way to differentiate between // Muted and Surround channels (they're all 0xA0) - might @@ -834,7 +837,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) continue; } - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(instrHeader, Samples[smp]); if(loadFlags & loadSampleData) sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk); @@ -912,7 +915,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) break; } - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(instrHeader, Samples[smp]); @@ -1016,23 +1019,41 @@ bool CSoundFile::ReadJ2B(FileReader &file, ModLoadingFlags loadFlags) return true; } - FileReader::PinnedRawDataView filePackedView = file.GetPinnedRawDataView(fileHeader.packedLength); - -#ifndef MPT_BUILD_FUZZER - if(fileHeader.crc32 != crc32(0, mpt::byte_cast(filePackedView.data()), static_cast(filePackedView.size()))) - { - return false; - } -#endif - // Header is valid, now unpack the RIFF AM file using inflate - uLongf destSize = fileHeader.unpackedLength; - std::vector amFileData(destSize); - int retVal = uncompress(amFileData.data(), &destSize, mpt::byte_cast(filePackedView.data()), static_cast(filePackedView.size())); + z_stream strm{}; + if(inflateInit(&strm) != Z_OK) + return false; + + uint32 remainRead = fileHeader.packedLength, remainWrite = fileHeader.unpackedLength, totalWritten = 0; + uint32 crc = 0; + std::vector amFileData(remainWrite); + int retVal = Z_OK; + while(remainRead && remainWrite && retVal != Z_STREAM_END) + { + Bytef buffer[mpt::IO::BUFFERSIZE_TINY]; + uint32 readSize = std::min(static_cast(sizeof(buffer)), remainRead); + file.ReadRaw(buffer, readSize); + crc = crc32(crc, buffer, readSize); + + strm.avail_in = readSize; + strm.next_in = buffer; + do + { + strm.avail_out = remainWrite; + strm.next_out = amFileData.data() + totalWritten; + retVal = inflate(&strm, Z_NO_FLUSH); + uint32 written = remainWrite - strm.avail_out; + totalWritten += written; + remainWrite -= written; + } while(remainWrite && strm.avail_out == 0); + + remainRead -= readSize; + } + inflateEnd(&strm); bool result = false; #ifndef MPT_BUILD_FUZZER - if(destSize == fileHeader.unpackedLength && retVal == Z_OK) + if(fileHeader.crc32 == crc && !remainWrite && retVal == Z_STREAM_END) #endif { // Success, now load the RIFF AM(FF) module. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp index 1890a89ca..959f165e9 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp @@ -24,7 +24,7 @@ namespace ModSpecs #define SongFlag(x) (FlagSet::store_type(0) | x) -MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = +constexpr CModSpecifications mptm_ = { /* TODO: Proper, less arbitrarily chosen values here. @@ -41,7 +41,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = 1, // Channel min 127, // Channel max 32, // Min tempo - 512, // Max tempo + 1000, // Max tempo 1, // Min Speed 255, // Max Speed 1, // Min pattern rows @@ -72,14 +72,14 @@ MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = true, // Has artist name true, // Has default resampling true, // Fixed point tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\:#???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\:#????????", // Supported Effects " vpcdabuh??gfe?o", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications mod_ = +constexpr CModSpecifications mod_ = { MOD_TYPE_MOD, // Internal MODTYPE value "mod", // File extension @@ -122,12 +122,12 @@ MPT_CONSTEXPR11_VAR CModSpecifications mod_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCD?FF?E??????????????????????", // Supported Effects + " 0123456789ABCD?FF?E???????????????????????", // Supported Effects " ???????????????", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications xm_ = +constexpr CModSpecifications xm_ = { MOD_TYPE_XM, // Internal MODTYPE value "xm", // File extension @@ -139,7 +139,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications xm_ = 1, // Channel min 32, // Channel max 32, // Min tempo - 512, // Max tempo + 1000, // Max tempo 1, // Min Speed 31, // Max Speed 1, // Min pattern rows @@ -170,12 +170,12 @@ MPT_CONSTEXPR11_VAR CModSpecifications xm_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCDRFFTE???GHK??XPL???????????", // Supported Effects + " 0123456789ABCDRFFTE???GHK??XPL????????????", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; // XM with MPT extensions -MPT_CONSTEXPR11_VAR CModSpecifications xmEx_ = +constexpr CModSpecifications xmEx_ = { MOD_TYPE_XM, // Internal MODTYPE value "xm", // File extension @@ -187,7 +187,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications xmEx_ = 1, // Channel min 127, // Channel max 32, // Min tempo - 512, // Max tempo + 1000, // Max tempo 1, // Min Speed 31, // Max Speed 1, // Min pattern rows @@ -218,11 +218,11 @@ MPT_CONSTEXPR11_VAR CModSpecifications xmEx_ = true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCDRFFTE???GHK?YXPLZ\\?#???????", // Supported Effects + " 0123456789ABCDRFFTE???GHK?YXPLZ\\?#????????", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications s3m_ = +constexpr CModSpecifications s3m_ = { MOD_TYPE_S3M, // Internal MODTYPE value "s3m", // File extension @@ -265,12 +265,12 @@ MPT_CONSTEXPR11_VAR CModSpecifications s3m_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?U???????????????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?U??????????????? ", // Supported Effects " vp?????????????", // Supported Volume Column commands }; // S3M with MPT extensions -MPT_CONSTEXPR11_VAR CModSpecifications s3mEx_ = +constexpr CModSpecifications s3mEx_ = { MOD_TYPE_S3M, // Internal MODTYPE value "s3m", // File extension @@ -313,11 +313,11 @@ MPT_CONSTEXPR11_VAR CModSpecifications s3mEx_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z??????????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z?????????? ", // Supported Effects " vp?????????????", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications it_ = +constexpr CModSpecifications it_ = { MOD_TYPE_IT, // Internal MODTYPE value "it", // File extension @@ -360,11 +360,11 @@ MPT_CONSTEXPR11_VAR CModSpecifications it_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z??????????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z?????????? ", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications itEx_ = +constexpr CModSpecifications itEx_ = { MOD_TYPE_IT, // Internal MODTYPE value "it", // File extension @@ -407,7 +407,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications itEx_ = true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\?#???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\?#??????? ", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp index 99a4fc253..80caed2dd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp @@ -1,5 +1,5 @@ /* - * ModCommand.cpp + * modcommand.cpp * -------------- * Purpose: Various functions for writing effects to patterns, converting ModCommands, etc. * Notes : (currently none) @@ -29,10 +29,10 @@ const EffectType effectTypes[] = EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, - EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, + EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, }; -STATIC_ASSERT(CountOf(effectTypes) == MAX_EFFECTS); +static_assert(std::size(effectTypes) == MAX_EFFECTS); const EffectType volumeEffectTypes[] = @@ -43,30 +43,24 @@ const EffectType volumeEffectTypes[] = EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, }; -STATIC_ASSERT(CountOf(volumeEffectTypes) == MAX_VOLCMDS); +static_assert(std::size(volumeEffectTypes) == MAX_VOLCMDS); EffectType ModCommand::GetEffectType(COMMAND cmd) { - if(cmd < CountOf(effectTypes)) - { + if(cmd < std::size(effectTypes)) return effectTypes[cmd]; - } else - { + else return EFFECT_TYPE_NORMAL; - } } EffectType ModCommand::GetVolumeEffectType(VOLCMD volcmd) { - if(volcmd < CountOf(volumeEffectTypes)) - { + if(volcmd < std::size(volumeEffectTypes)) return volumeEffectTypes[volcmd]; - } else - { + else return EFFECT_TYPE_NORMAL; - } } @@ -206,7 +200,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd } } - param = (PARAM)(std::min(maxColumnValue, GetValueEffectCol()) * 0x7F / maxColumnValue); + param = static_cast(std::min(static_cast(maxColumnValue), GetValueEffectCol()) * 0x7F / maxColumnValue); command = newCmd; // might be removed later volcmd = VOLCMD_NONE; note = NOTE_NONE; @@ -303,10 +297,10 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd // swap L/R, convert to fine slide if(param & 0xF0) { - param = 0xF0 | std::min(0x0E, (param >> 4)); + param = 0xF0 | std::min(PARAM(0x0E), static_cast(param >> 4)); } else { - param = 0x0F | (std::min(0x0E, param & 0x0F) << 4); + param = 0x0F | (std::min(PARAM(0x0E), static_cast(param & 0x0F)) << 4); } break; @@ -357,7 +351,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd vol = 0; } - MPT_FALLTHROUGH; + [[fallthrough]]; case CMD_VOLUMESLIDE: if((param & 0xF0) && ((param & 0x0F) == 0x0F)) { @@ -481,7 +475,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd break; case CMD_GLOBALVOLUME: - param = (std::min(0x80, param) + 1) / 2u; + param = (std::min(PARAM(0x80), param) + 1) / 2u; break; default: @@ -500,7 +494,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd param = (param & 0xF0) | (((param & 0x0F) + 1) / 2u); break; case CMD_GLOBALVOLUME: - param = (std::min(0x80, param) + 1) / 2u; + param = (std::min(PARAM(0x80), param) + 1) / 2u; break; } } // End if(oldTypeIsIT_MPT && newTypeIsXM) @@ -513,10 +507,10 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd { case CMD_VIBRATO: // With linear slides, strength is roughly halved. - param = (param & 0xF0) | std::min((param & 0x0F) * 2u, 15); + param = (param & 0xF0) | std::min(static_cast((param & 0x0F) * 2u), PARAM(15)); break; case CMD_GLOBALVOLUME: - param = std::min(0x40, param) * 2u; + param = std::min(PARAM(0x40), param) * 2u; break; } } // End if(oldTypeIsIT_MPT && newTypeIsXM) @@ -528,11 +522,11 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd switch(command) { case CMD_SPEED: - param = std::min(param, 0x1F); + param = std::min(param, PARAM(0x1F)); break; break; case CMD_TEMPO: - param = std::max(param, 0x20); + param = std::max(param, PARAM(0x20)); break; } } @@ -816,7 +810,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd case VOLCMD_VIBRATODEPTH: // OpenMPT-specific commands case VOLCMD_OFFSET: - vol = std::min(vol, 9); + vol = std::min(vol, VOL(9)); break; } } // End if(newTypeIsIT_MPT) @@ -853,7 +847,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd } -bool ModCommand::IsGlobalCommand() const +bool ModCommand::IsGlobalCommand(COMMAND command, PARAM param) { switch(command) { @@ -900,9 +894,10 @@ bool ModCommand::IsGlobalCommand() const size_t ModCommand::GetEffectWeight(COMMAND cmd) { // Effect weights, sorted from lowest to highest weight. - static const COMMAND weights[] = + static constexpr COMMAND weights[] = { CMD_NONE, + CMD_DUMMY, CMD_XPARAM, CMD_SETENVPOSITION, CMD_KEYOFF, @@ -945,9 +940,9 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd) CMD_POSITIONJUMP, CMD_PATTERNBREAK, }; - STATIC_ASSERT(CountOf(weights) == MAX_EFFECTS); + static_assert(std::size(weights) == MAX_EFFECTS); - for(size_t i = 0; i < CountOf(weights); i++) + for(size_t i = 0; i < std::size(weights); i++) { if(weights[i] == cmd) { @@ -972,7 +967,7 @@ bool ModCommand::ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force) return true; case CMD_VOLUME: effect = VOLCMD_VOLUME; - param = std::min(param, 64); + param = std::min(param, PARAM(64)); break; case CMD_PORTAMENTOUP: // if not force, reject when dividing causes loss of data in LSB, or if the final value is too @@ -1010,7 +1005,7 @@ bool ModCommand::ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force) return false; case CMD_VIBRATO: if(force) - param = std::min(param & 0x0F, 9); + param = std::min(static_cast(param & 0x0F), PARAM(9)); else if((param & 0x0F) > 9 || (param & 0xF0) != 0) return false; param &= 0x0F; @@ -1125,13 +1120,13 @@ bool ModCommand::CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 & } -bool ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2) +std::pair ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2) { for(uint8 n = 0; n < 4; n++) { if(ModCommand::ConvertVolEffect(effect1, param1, (n > 1))) { - return true; + return {CMD_NONE, ModCommand::PARAM(0)}; } std::swap(effect1, effect2); std::swap(param1, param2); @@ -1143,9 +1138,10 @@ bool ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &e std::swap(effect1, effect2); std::swap(param1, param2); } + std::pair lostCommand = {static_cast(effect1), param1}; effect1 = VOLCMD_NONE; param1 = 0; - return false; + return lostCommand; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h index da5f14a6f..096c40977 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h @@ -1,5 +1,5 @@ /* - * ModCommand.h + * modcommand.h * ------------ * Purpose: ModCommand declarations and helpers. One ModCommand corresponds to one pattern cell. * Notes : (currently none) @@ -20,88 +20,92 @@ OPENMPT_NAMESPACE_BEGIN class CSoundFile; // Note definitions -#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)) // === (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 +enum : uint8 // ModCommand::NOTE +{ + NOTE_NONE = 0, // Empty note cell + NOTE_MIN = 1, // Minimum note value + NOTE_MAX = 120, // Maximum note value + NOTE_MIDDLEC = (5 * 12 + NOTE_MIN), + NOTE_KEYOFF = 0xFF, // === (Note Off, releases envelope / fades samples, stops plugin note) + NOTE_NOTECUT = 0xFE, // ^^^ (Cuts sample / stops all plugin notes) + NOTE_FADE = 0xFD, // ~~~ (Fades samples, stops plugin note) + NOTE_PC = 0xFC, // Param Control 'note'. Changes param value on first tick. + NOTE_PCS = 0xFB, // Param Control (Smooth) 'note'. Interpolates param value during the whole row. + NOTE_MIN_SPECIAL = NOTE_PCS, + NOTE_MAX_SPECIAL = NOTE_KEYOFF, +}; // Volume Column commands enum VolumeCommand : uint8 { - VOLCMD_NONE = 0, - VOLCMD_VOLUME = 1, - VOLCMD_PANNING = 2, - VOLCMD_VOLSLIDEUP = 3, - VOLCMD_VOLSLIDEDOWN = 4, - VOLCMD_FINEVOLUP = 5, - VOLCMD_FINEVOLDOWN = 6, - VOLCMD_VIBRATOSPEED = 7, - VOLCMD_VIBRATODEPTH = 8, - VOLCMD_PANSLIDELEFT = 9, - VOLCMD_PANSLIDERIGHT = 10, - VOLCMD_TONEPORTAMENTO = 11, - VOLCMD_PORTAUP = 12, - VOLCMD_PORTADOWN = 13, - VOLCMD_DELAYCUT = 14, //currently unused - VOLCMD_OFFSET = 15, - MAX_VOLCMDS = 16 + VOLCMD_NONE = 0, + VOLCMD_VOLUME = 1, + VOLCMD_PANNING = 2, + VOLCMD_VOLSLIDEUP = 3, + VOLCMD_VOLSLIDEDOWN = 4, + VOLCMD_FINEVOLUP = 5, + VOLCMD_FINEVOLDOWN = 6, + VOLCMD_VIBRATOSPEED = 7, + VOLCMD_VIBRATODEPTH = 8, + VOLCMD_PANSLIDELEFT = 9, + VOLCMD_PANSLIDERIGHT = 10, + VOLCMD_TONEPORTAMENTO = 11, + VOLCMD_PORTAUP = 12, + VOLCMD_PORTADOWN = 13, + VOLCMD_DELAYCUT = 14, //currently unused + VOLCMD_OFFSET = 15, + MAX_VOLCMDS }; // Effect column commands enum EffectCommand : uint8 { - CMD_NONE = 0, - CMD_ARPEGGIO = 1, - CMD_PORTAMENTOUP = 2, - CMD_PORTAMENTODOWN = 3, - CMD_TONEPORTAMENTO = 4, - CMD_VIBRATO = 5, - CMD_TONEPORTAVOL = 6, - CMD_VIBRATOVOL = 7, - CMD_TREMOLO = 8, - CMD_PANNING8 = 9, - CMD_OFFSET = 10, - CMD_VOLUMESLIDE = 11, - CMD_POSITIONJUMP = 12, - CMD_VOLUME = 13, - CMD_PATTERNBREAK = 14, - CMD_RETRIG = 15, - CMD_SPEED = 16, - CMD_TEMPO = 17, - CMD_TREMOR = 18, - CMD_MODCMDEX = 19, - CMD_S3MCMDEX = 20, - CMD_CHANNELVOLUME = 21, - CMD_CHANNELVOLSLIDE = 22, - CMD_GLOBALVOLUME = 23, - CMD_GLOBALVOLSLIDE = 24, - CMD_KEYOFF = 25, - CMD_FINEVIBRATO = 26, - CMD_PANBRELLO = 27, - CMD_XFINEPORTAUPDOWN = 28, - CMD_PANNINGSLIDE = 29, - CMD_SETENVPOSITION = 30, - CMD_MIDI = 31, - CMD_SMOOTHMIDI = 32, - CMD_DELAYCUT = 33, - CMD_XPARAM = 34, - CMD_NOTESLIDEUP = 35, // IMF Gxy / PTM Jxy (Slide y notes up every x ticks) - CMD_NOTESLIDEDOWN = 36, // IMF Hxy / PTM Kxy (Slide y notes down every x ticks) - CMD_NOTESLIDEUPRETRIG = 37, // PTM Lxy (Slide y notes up every x ticks + retrigger note) - CMD_NOTESLIDEDOWNRETRIG = 38, // PTM Mxy (Slide y notes down every x ticks + retrigger note) - CMD_REVERSEOFFSET = 39, // PTM Nxx Revert sample + offset - CMD_DBMECHO = 40, // DBM enable/disable echo - CMD_OFFSETPERCENTAGE = 41, // PLM Percentage Offset - MAX_EFFECTS = 42 + CMD_NONE = 0, + CMD_ARPEGGIO = 1, + CMD_PORTAMENTOUP = 2, + CMD_PORTAMENTODOWN = 3, + CMD_TONEPORTAMENTO = 4, + CMD_VIBRATO = 5, + CMD_TONEPORTAVOL = 6, + CMD_VIBRATOVOL = 7, + CMD_TREMOLO = 8, + CMD_PANNING8 = 9, + CMD_OFFSET = 10, + CMD_VOLUMESLIDE = 11, + CMD_POSITIONJUMP = 12, + CMD_VOLUME = 13, + CMD_PATTERNBREAK = 14, + CMD_RETRIG = 15, + CMD_SPEED = 16, + CMD_TEMPO = 17, + CMD_TREMOR = 18, + CMD_MODCMDEX = 19, + CMD_S3MCMDEX = 20, + CMD_CHANNELVOLUME = 21, + CMD_CHANNELVOLSLIDE = 22, + CMD_GLOBALVOLUME = 23, + CMD_GLOBALVOLSLIDE = 24, + CMD_KEYOFF = 25, + CMD_FINEVIBRATO = 26, + CMD_PANBRELLO = 27, + CMD_XFINEPORTAUPDOWN = 28, + CMD_PANNINGSLIDE = 29, + CMD_SETENVPOSITION = 30, + CMD_MIDI = 31, + CMD_SMOOTHMIDI = 32, + CMD_DELAYCUT = 33, + CMD_XPARAM = 34, + CMD_NOTESLIDEUP = 35, // IMF Gxy / PTM Jxy (Slide y notes up every x ticks) + CMD_NOTESLIDEDOWN = 36, // IMF Hxy / PTM Kxy (Slide y notes down every x ticks) + CMD_NOTESLIDEUPRETRIG = 37, // PTM Lxy (Slide y notes up every x ticks + retrigger note) + CMD_NOTESLIDEDOWNRETRIG = 38, // PTM Mxy (Slide y notes down every x ticks + retrigger note) + CMD_REVERSEOFFSET = 39, // PTM Nxx Revert sample + offset + CMD_DBMECHO = 40, // DBM enable/disable echo + CMD_OFFSETPERCENTAGE = 41, // PLM Percentage Offset + CMD_DUMMY = 42, + MAX_EFFECTS }; @@ -128,7 +132,7 @@ 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]. - enum : int { maxColumnValue = 999 }; + static constexpr int maxColumnValue = 999; // Returns empty modcommand. static ModCommand Empty() { return ModCommand(); } @@ -182,7 +186,8 @@ public: // Returns true if any of the commands in this cell trigger a tone portamento. bool IsPortamento() const { return command == CMD_TONEPORTAMENTO || command == CMD_TONEPORTAVOL || volcmd == VOLCMD_TONEPORTAMENTO; } // Returns true if the cell contains an effect command that may affect the global state of the module. - bool IsGlobalCommand() const; + bool IsGlobalCommand() const { return IsGlobalCommand(command, param); } + static bool IsGlobalCommand(COMMAND command, PARAM param); // Returns true if the note is inside the Amiga frequency range bool IsAmigaNote() const { return IsAmigaNote(note); } @@ -205,8 +210,8 @@ public: static size_t GetEffectWeight(COMMAND cmd); // Try to convert a an effect into a volume column effect. Returns true on success. static bool ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force); - // Takes two "normal" effect commands and converts them to volume column + effect column commands. Returns true on success, false (if one effect was lost) otherwise. - static bool TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2); + // Takes two "normal" effect commands and converts them to volume column + effect column commands. Returns the dropped command + param (CMD_NONE if nothing had to be dropped). + static std::pair TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2); // Try to combine two commands into one. Returns true on success and the combined command is placed in eff1 / param1. static bool CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 ¶m2); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp index 2d61e1a6d..27760f6fe 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp @@ -1,8 +1,8 @@ /* * modsmp_ctrl.cpp * --------------- - * Purpose: Basic sample editing code (resizing, adding silence, normalizing, ...). - * 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. + * Purpose: Basic sample editing code. + * Notes : This is a legacy namespace. Some of this stuff is not required in libopenmpt (but stuff in soundlib/ still depends on it). The rest could be merged into struct ModSample. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -40,286 +40,6 @@ void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, } -SmpLength InsertSilence(ModSample &smp, const SmpLength silenceLength, const SmpLength startFrom, CSoundFile &sndFile) -{ - if(silenceLength == 0 || silenceLength > MAX_SAMPLE_LENGTH || smp.nLength > MAX_SAMPLE_LENGTH - silenceLength || startFrom > smp.nLength) - return smp.nLength; - - const bool wasEmpty = !smp.HasSampleData(); - const SmpLength newLength = smp.nLength + silenceLength; - - char *pNewSmp = nullptr; - - pNewSmp = static_cast(ModSample::AllocateSample(newLength, smp.GetBytesPerSample())); - if(pNewSmp == nullptr) - return smp.nLength; //Sample allocation failed. - - if(!wasEmpty) - { - // Copy over old sample - const SmpLength silenceOffset = startFrom * smp.GetBytesPerSample(); - const SmpLength silenceBytes = silenceLength * smp.GetBytesPerSample(); - if(startFrom > 0) - { - memcpy(pNewSmp, smp.samplev(), silenceOffset); - } - if(startFrom < smp.nLength) - { - memcpy(pNewSmp + silenceOffset + silenceBytes, smp.sampleb() + silenceOffset, smp.GetSampleSizeInBytes() - silenceOffset); - } - - // Update loop points if necessary. - 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(cue >= startFrom) cue += silenceLength; - } - } else - { - // Set loop points automatically - smp.nLoopStart = 0; - smp.nLoopEnd = newLength; - smp.uFlags.set(CHN_LOOP); - } - - ReplaceSample(smp, pNewSmp, newLength, sndFile); - PrecomputeLoops(smp, sndFile, true); - - return smp.nLength; -} - - -namespace -{ - // Update loop points and cues after deleting a sample selection - static void AdjustLoopPoints(SmpLength selStart, SmpLength selEnd, SmpLength &loopStart, SmpLength &loopEnd, SmpLength length) - { - Util::DeleteRange(selStart, selEnd - 1, loopStart, loopEnd); - - LimitMax(loopEnd, length); - if(loopStart + 2 >= loopEnd) - { - loopStart = loopEnd = 0; - } - } -} - -SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSoundFile &sndFile) -{ - LimitMax(selEnd, smp.nLength); - if(selEnd <= selStart) - { - return smp.nLength; - } - const uint8 bps = smp.GetBytesPerSample(); - 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? - AdjustLoopPoints(selStart, selEnd, smp.nLoopStart, smp.nLoopEnd, smp.nLength); - AdjustLoopPoints(selStart, selEnd, smp.nSustainStart, smp.nSustainEnd, smp.nLength); - - if(smp.nLoopEnd == 0) smp.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); - if(smp.nSustainEnd == 0) smp.uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); - - for(auto &cue : smp.cues) - { - Util::DeleteItem(selStart, selEnd - 1, cue); - } - - smp.PrecomputeLoops(sndFile); - return smp.nLength; -} - - -SmpLength ResizeSample(ModSample &smp, const SmpLength newLength, CSoundFile &sndFile) -{ - // Invalid sample size - 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(newLength > smp.nLength) - return InsertSilence(smp, newLength - smp.nLength, smp.nLength, sndFile); - - // Else: Shrink sample - - const SmpLength newSmpBytes = newLength * 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.sampleb(), newSmpBytes); - ReplaceSample(smp, pNewSmp, newLength, sndFile); - - // Adjust loops - if(smp.nLoopStart > newLength) - { - smp.nLoopStart = smp.nLoopEnd = 0; - smp.uFlags.reset(CHN_LOOP); - } - if(smp.nLoopEnd > newLength) smp.nLoopEnd = newLength; - if(smp.nSustainStart > newLength) - { - smp.nSustainStart = smp.nSustainEnd = 0; - smp.uFlags.reset(CHN_SUSTAINLOOP); - } - if(smp.nSustainEnd > newLength) smp.nSustainEnd = newLength; - - PrecomputeLoops(smp, sndFile); - - return smp.nLength; -} - -namespace // Unnamed namespace for local implementation functions. -{ - - -template -class PrecomputeLoop -{ -protected: - T *target; - const T *sampleData; - SmpLength loopEnd; - int numChannels; - bool pingpong; - bool ITPingPongMode; - -public: - PrecomputeLoop(T *target, const T *sampleData, SmpLength loopEnd, int numChannels, bool pingpong, bool ITPingPongMode) - : target(target), sampleData(sampleData), loopEnd(loopEnd), numChannels(numChannels), pingpong(pingpong), ITPingPongMode(ITPingPongMode) - { - if(loopEnd > 0) - { - CopyLoop(true); - CopyLoop(false); - } - } - - void CopyLoop(bool direction) const - { - // Direction: true = start reading and writing forward, false = start reading and writing backward (write direction never changes) - const int numSamples = 2 * InterpolationMaxLookahead + (direction ? 1 : 0); // Loop point is included in forward loop expansion - T *dest = target + numChannels * (2 * InterpolationMaxLookahead - 1); // Write buffer offset - SmpLength readPosition = loopEnd - 1; - const int writeIncrement = direction ? 1 : -1; - int readIncrement = writeIncrement; - - for(int i = 0; i < numSamples; i++) - { - // Copy sample over to lookahead buffer - for(int c = 0; c < numChannels; c++) - { - dest[c] = sampleData[readPosition * numChannels + c]; - } - dest += writeIncrement * numChannels; - - if(readPosition == loopEnd - 1 && readIncrement > 0) - { - // Reached end of loop while going forward - if(pingpong) - { - readIncrement = -1; - if(ITPingPongMode && readPosition > 0) - { - readPosition--; - } - } else - { - readPosition = 0; - } - } else if(readPosition == 0 && readIncrement < 0) - { - // Reached start of loop while going backward - if(pingpong) - { - readIncrement = 1; - } else - { - readPosition = loopEnd - 1; - } - } else - { - readPosition += readIncrement; - } - } - } -}; - - -template -void PrecomputeLoopsImpl(ModSample &smp, const CSoundFile &sndFile) -{ - const int numChannels = smp.GetNumChannels(); - const int copySamples = numChannels * InterpolationMaxLookahead; - - T *sampleData = reinterpret_cast(smp.samplev()); - T *afterSampleStart = sampleData + smp.nLength * numChannels; - T *loopLookAheadStart = afterSampleStart + copySamples; - T *sustainLookAheadStart = loopLookAheadStart + 4 * copySamples; - - // Hold sample on the same level as the last sampling point at the end to prevent extra pops with interpolation. - // Do the same at the sample start, too. - for(int i = 0; i < (int)InterpolationMaxLookahead; i++) - { - for(int c = 0; c < numChannels; c++) - { - afterSampleStart[i * numChannels + c] = afterSampleStart[-numChannels + c]; - sampleData[-(i + 1) * numChannels + c] = sampleData[c]; - } - } - - if(smp.uFlags[CHN_LOOP]) - { - PrecomputeLoop(loopLookAheadStart, - sampleData + smp.nLoopStart * numChannels, - smp.nLoopEnd - smp.nLoopStart, - numChannels, - smp.uFlags[CHN_PINGPONGLOOP], - sndFile.m_playBehaviour[kITPingPongMode]); - } - if(smp.uFlags[CHN_SUSTAINLOOP]) - { - PrecomputeLoop(sustainLookAheadStart, - sampleData + smp.nSustainStart * numChannels, - smp.nSustainEnd - smp.nSustainStart, - numChannels, - smp.uFlags[CHN_PINGPONGSUSTAIN], - sndFile.m_playBehaviour[kITPingPongMode]); - } -} - -} // unnamed namespace. - - -bool PrecomputeLoops(ModSample &smp, CSoundFile &sndFile, bool updateChannels) -{ - if(!smp.HasSampleData()) - return false; - - smp.SanitizeLoops(); - - // Update channels with possibly changed loop values - if(updateChannels) - { - UpdateLoopPoints(smp, sndFile); - } - - if(smp.GetElementarySampleSize() == 2) - PrecomputeLoopsImpl(smp, sndFile); - else if(smp.GetElementarySampleSize() == 1) - PrecomputeLoopsImpl(smp, sndFile); - - return true; -} - - // Propagate loop point changes to player bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile) { @@ -372,247 +92,6 @@ bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile) } -void ResetSamples(CSoundFile &sndFile, ResetFlag resetflag, SAMPLEINDEX minSample, SAMPLEINDEX maxSample) -{ - if(minSample == SAMPLEINDEX_INVALID) - { - minSample = 1; - } - if(maxSample == SAMPLEINDEX_INVALID) - { - maxSample = sndFile.GetNumSamples(); - } - Limit(minSample, SAMPLEINDEX(1), SAMPLEINDEX(MAX_SAMPLES - 1)); - Limit(maxSample, SAMPLEINDEX(1), SAMPLEINDEX(MAX_SAMPLES - 1)); - - if(minSample > maxSample) - { - std::swap(minSample, maxSample); - } - - for(SAMPLEINDEX i = minSample; i <= maxSample; i++) - { - ModSample &sample = sndFile.GetSample(i); - switch(resetflag) - { - case SmpResetInit: - strcpy(sndFile.m_szNames[i], ""); - strcpy(sample.filename, ""); - sample.nC5Speed = 8363; - // note: break is left out intentionally. keep this order or c&p the stuff from below if you change anything! - MPT_FALLTHROUGH; - case SmpResetCompo: - sample.nPan = 128; - sample.nGlobalVol = 64; - sample.nVolume = 256; - sample.nVibDepth = 0; - sample.nVibRate = 0; - sample.nVibSweep = 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 = VIB_SINE; - break; - default: - break; - } - } -} - - -namespace -{ - struct OffsetData - { - double max = 0.0, min = 0.0, offset = 0.0; - }; - - // Returns maximum sample amplitude for given sample type (int8/int16). - template - double GetMaxAmplitude() {return 1.0 + (std::numeric_limits::max)();} - - // 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 length) - { - OffsetData offsetVals; - - if(length < 1) - return offsetVals; - - const double maxAmplitude = GetMaxAmplitude(); - double max = -1, min = 1, sum = 0; - - const T *p = pStart; - for(SmpLength i = 0; i < length; i++, p++) - { - const double val = double(*p) / maxAmplitude; - sum += val; - if(val > max) max = val; - if(val < min) min = val; - } - - offsetVals.max = max; - offsetVals.min = min; - offsetVals.offset = (-sum / (double)(length)); - return offsetVals; - } - - template - void RemoveOffsetAndNormalize(T *pStart, const SmpLength length, const double offset, const double amplify) - { - T *p = pStart; - for(SmpLength i = 0; i < length; i++, p++) - { - double var = (*p) * amplify + offset; - *p = mpt::saturate_round(var); - } - } -} - - -// Remove DC offset -double RemoveDCOffset(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - if(!smp.HasSampleData()) - return 0; - - if(end > smp.nLength) end = smp.nLength; - if(start > end) start = end; - if(start == end) - { - start = 0; - end = smp.nLength; - } - - start *= smp.GetNumChannels(); - end *= smp.GetNumChannels(); - - const double maxAmplitude = (smp.GetElementarySampleSize() == 2) ? GetMaxAmplitude() : GetMaxAmplitude(); - - // step 1: Calculate offset. - OffsetData oData; - if(smp.GetElementarySampleSize() == 2) - oData = CalculateOffset(smp.sample16() + start, end - start); - else if(smp.GetElementarySampleSize() == 1) - oData = CalculateOffset(smp.sample8() + start, end - start); - else - return 0; - - double offset = oData.offset; - - if((int)(offset * maxAmplitude) == 0) - return 0; - - // those will be changed... - oData.max += offset; - oData.min += offset; - - // ... and that might cause distortion, so we will normalize this. - const double amplify = 1 / std::max(oData.max, -oData.min); - - // step 2: centralize + normalize sample - offset *= maxAmplitude * amplify; - if(smp.GetElementarySampleSize() == 2) - RemoveOffsetAndNormalize(smp.sample16() + start, end - start, offset, amplify); - else if(smp.GetElementarySampleSize() == 1) - RemoveOffsetAndNormalize(smp.sample8() + start, end - start, offset, amplify); - - // step 3: adjust global vol (if available) - if((sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (start == 0) && (end == smp.nLength * smp.GetNumChannels())) - { - CriticalSection cs; - - smp.nGlobalVol = std::min(mpt::saturate_round(smp.nGlobalVol / amplify), uint16(64)); - for(auto &chn : sndFile.m_PlayState.Chn) - { - if(chn.pModSample == &smp) - { - chn.UpdateInstrumentVolume(&smp, chn.pModInstrument); - } - } - } - - PrecomputeLoops(smp, sndFile, false); - - return oData.offset; -} - - -template -static void ReverseSampleImpl(T *pStart, const SmpLength nLength) -{ - for(SmpLength i = 0; i < nLength / 2; i++) - { - std::swap(pStart[i], pStart[nLength - 1 - i]); - } -} - -// Reverse sample data -bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - if(!smp.HasSampleData()) return false; - if(end == 0 || start > smp.nLength || end > smp.nLength) - { - start = 0; - end = smp.nLength; - } - - if(end - start < 2) return false; - - STATIC_ASSERT(MaxSamplingPointSize <= 4); - if(smp.GetBytesPerSample() == 4) // 16 bit stereo - ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); - else if(smp.GetBytesPerSample() == 2) // 16 bit mono / 8 bit stereo - ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); - else if(smp.GetBytesPerSample() == 1) // 8 bit mono - ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); - else - return false; - - PrecomputeLoops(smp, sndFile, false); - return true; -} - - -template -static void UnsignSampleImpl(T *pStart, const SmpLength length) -{ - const T offset = (std::numeric_limits::min)(); - for(SmpLength i = 0; i < length; i++) - { - pStart[i] += offset; - } -} - -// Virtually unsign sample data -bool UnsignSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - if(!smp.HasSampleData()) return false; - if(end == 0 || start > smp.nLength || end > smp.nLength) - { - start = 0; - end = smp.nLength; - } - start *= smp.GetNumChannels(); - end *= smp.GetNumChannels(); - if(smp.GetElementarySampleSize() == 2) - UnsignSampleImpl(smp.sample16() + start, end - start); - else if(smp.GetElementarySampleSize() == 1) - UnsignSampleImpl(smp.sample8() + start, end - start); - else - return false; - - PrecomputeLoops(smp, sndFile, false); - return true; -} - - template static void InvertSampleImpl(T *pStart, const SmpLength length) { @@ -640,7 +119,7 @@ bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sn else return false; - PrecomputeLoops(smp, sndFile, false); + smp.PrecomputeLoops(sndFile, false); return true; } @@ -691,83 +170,7 @@ bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterlo } else return false; - PrecomputeLoops(smp, sndFile, true); - return true; -} - - -template -static void SilenceSampleImpl(T *p, SmpLength length, SmpLength inc, bool fromStart, bool toEnd) -{ - const int dest = toEnd ? 0 : p[(length - 1) * inc]; - const int base = fromStart ? 0 :p[0]; - const int delta = dest - base; - const int64 len_m1 = length - 1; - for(SmpLength i = 0; i < length; i++) - { - int n = base + static_cast((static_cast(delta) * static_cast(i)) / len_m1); - *p = static_cast(n); - p += inc; - } -} - -// X-Fade sample data to create smooth loop transitions -bool SilenceSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - LimitMax(end, smp.nLength); - if(!smp.HasSampleData() || start >= end) return false; - - const SmpLength length = end - start; - const bool fromStart = start == 0; - const bool toEnd = end == smp.nLength; - const uint8 numChn = smp.GetNumChannels(); - - for(uint8 chn = 0; chn < numChn; chn++) - { - if(smp.GetElementarySampleSize() == 2) - SilenceSampleImpl(smp.sample16() + start * numChn + chn, length, numChn, fromStart, toEnd); - else if(smp.GetElementarySampleSize() == 1) - SilenceSampleImpl(smp.sample8() + start * numChn + chn, length, numChn, fromStart, toEnd); - else - return false; - } - - PrecomputeLoops(smp, sndFile, false); - return true; -} - - -template -static void StereoSepSampleImpl(T *p, SmpLength length, int32 separation) -{ - const int32 fac1 = static_cast(32768 + separation / 2), fac2 = static_cast(32768 - separation / 2); - while(length--) - { - const int32 l = p[0], r = p[1]; - p[0] = mpt::saturate_cast((Util::mul32to64(l, fac1) + Util::mul32to64(r, fac2)) >> 16); - p[1] = mpt::saturate_cast((Util::mul32to64(l, fac2) + Util::mul32to64(r, fac1)) >> 16); - p += 2; - } -} - -// X-Fade sample data to create smooth loop transitions -bool StereoSepSample(ModSample &smp, SmpLength start, SmpLength end, double separation, CSoundFile &sndFile) -{ - LimitMax(end, smp.nLength); - if(!smp.HasSampleData() || start >= end || smp.GetNumChannels() != 2) return false; - - const SmpLength length = end - start; - const uint8 numChn = smp.GetNumChannels(); - const int32 sep32 = mpt::saturate_round(separation * (65536.0 / 100.0)); - - if(smp.GetElementarySampleSize() == 2) - StereoSepSampleImpl(smp.sample16() + start * numChn, length, sep32); - else if(smp.GetElementarySampleSize() == 1) - StereoSepSampleImpl(smp.sample8() + start * numChn, length, sep32); - else - return false; - - PrecomputeLoops(smp, sndFile, false); + smp.PrecomputeLoops(sndFile, true); return true; } @@ -832,7 +235,7 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers } } - PrecomputeLoops(smp, sndFile, false); + smp.PrecomputeLoops(sndFile, false); return true; } @@ -872,43 +275,6 @@ bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile) smp.uFlags.set(CHN_STEREO); ReplaceSample(smp, newSample, smp.nLength, sndFile); - PrecomputeLoops(smp, sndFile, false); - return true; -} - - -// Convert 16-bit sample to 8-bit -bool ConvertTo8Bit(ModSample &smp, CSoundFile &sndFile) -{ - if(!smp.HasSampleData() || smp.GetElementarySampleSize() != 2) - return false; - - 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) - chn.dwFlags.reset(CHN_16BIT); - } - - smp.PrecomputeLoops(sndFile, false); - return true; -} - - -// Convert 8-bit sample to 16-bit -bool ConvertTo16Bit(ModSample &smp, CSoundFile &sndFile) -{ - if(!smp.HasSampleData() || smp.GetElementarySampleSize() != 1) - return false; - - int16 *newSample = static_cast(ModSample::AllocateSample(smp.nLength, 2 * smp.GetNumChannels())); - if(newSample == nullptr) - return false; - - 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); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h index 28df8030d..366aa20e3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h @@ -1,7 +1,7 @@ /* * modsmp_ctrl.h * ------------- - * Purpose: Basic sample editing code (resizing, adding silence, normalizing, ...). + * Purpose: Basic sample editing code * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -12,78 +12,29 @@ #include "BuildSettings.h" -OPENMPT_NAMESPACE_BEGIN -class CSoundFile; -struct ModSample; -struct ModChannel; -OPENMPT_NAMESPACE_END - #include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN +class CSoundFile; +struct ModSample; +struct ModChannel; + namespace ctrlSmp { -enum ResetFlag -{ - SmpResetCompo = 1, - SmpResetInit, - SmpResetVibrato, -}; - -// 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 silenceLength, const SmpLength startFrom, CSoundFile &sndFile); - -// Remove part of a sample [selStart, selEnd[. -// Note: Removed memory is not freed. -// Return: Length of the new sample. -SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSoundFile &sndFile); - -// 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 newLength, CSoundFile &sndFile); - // Replaces sample in 'smp' with given sample and frees the old sample. 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); - // Propagate loop point changes to player bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile); -// Resets samples. -void ResetSamples(CSoundFile &sndFile, ResetFlag resetflag, SAMPLEINDEX minSample = SAMPLEINDEX_INVALID, SAMPLEINDEX maxSample = SAMPLEINDEX_INVALID); - -// Remove DC offset and normalize. -// Return: If DC offset was removed, returns original offset value, zero otherwise. -double RemoveDCOffset(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - -// Amplify / fade sample data -bool AmplifySample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile, double amplifyStart, double amplifyEnd); - -// Reverse sample data -bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - -// Virtually unsign sample data -bool UnsignSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - // Invert sample data (flip by 180 degrees) bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Crossfade sample data to create smooth loops bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterloopFade, bool useSustainLoop, CSoundFile &sndFile); -// Silence parts of the sample data -bool SilenceSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - -// Modify stereo separation of the sample data. separation is in range [-200, 200] -bool StereoSepSample(ModSample &smp, SmpLength start, SmpLength end, double separation, CSoundFile &sndFile); - enum StereoToMonoMode { mixChannels, @@ -98,12 +49,6 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers // Convert a mono sample to stereo bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile); -// Convert 16-bit sample to 8-bit -bool ConvertTo8Bit(ModSample &smp, CSoundFile &sndFile); - -// Convert 8-bit sample to 16-bit -bool ConvertTo16Bit(ModSample &smp, CSoundFile &sndFile); - } // Namespace ctrlSmp namespace ctrlChn diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h index ab10f7f47..7ac894508 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h @@ -1,6 +1,7 @@ // This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker). // It was released by Shayde/Reality into the public domain. // Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied. +// Additional fixes by JP Cimalando. /* @@ -144,6 +145,7 @@ class Opal { uint16_t GetOctave() const { return Octave; } uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } uint16_t GetModulationType() const { return ModulationType; } + Channel * GetChannelPair() const { return ChannelPair; } void ComputeKeyScaleNumber(); @@ -307,6 +309,7 @@ void Opal::Init(int sample_rate) { Clock = 0; TremoloClock = 0; + TremoloLevel = 0; VibratoTick = 0; VibratoClock = 0; NoteSel = false; @@ -375,7 +378,7 @@ void Opal::SetSampleRate(int sample_rate) { //================================================================================================== void Opal::Port(uint16_t reg_num, uint8_t val) { - static const int8_t op_lookup[] = { + static constexpr int8_t op_lookup[] = { // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F @@ -450,20 +453,28 @@ void Opal::Port(uint16_t reg_num, uint8_t val) { Channel &chan = Chan[chan_num]; + // Registers Ax and Bx affect both channels + Channel *chans[2] = {&chan, chan.GetChannelPair()}; + int numchans = chans[1] ? 2 : 1; + // Do specific registers switch (reg_num & 0xF0) { // Frequency low case 0xA0: { - chan.SetFrequencyLow(val); + for (int i = 0; i < numchans; i++) { + chans[i]->SetFrequencyLow(val); + } break; } // Key-on / Octave / Frequency High case 0xB0: { - chan.SetKeyOn((val & 0x20) != 0); - chan.SetOctave(val >> 2 & 7); - chan.SetFrequencyHigh(val & 3); + for (int i = 0; i < numchans; i++) { + chans[i]->SetKeyOn((val & 0x20) != 0); + chans[i]->SetOctave(val >> 2 & 7); + chans[i]->SetFrequencyHigh(val & 3); + } break; } @@ -900,11 +911,11 @@ int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, in // 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; + if (AttackRate == 0) + add = 0; + if (AttackMask && (Master->Clock & AttackMask)) + add = 0; EnvelopeLevel += add; if (EnvelopeLevel <= 0) { EnvelopeLevel = 0; @@ -915,12 +926,12 @@ int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, in // Decay stage case EnvDec: { + uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; + if (DecayRate == 0) + add = 0; if (DecayMask && (Master->Clock & DecayMask)) - break; - if (DecayRate != 0) { - uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; - EnvelopeLevel += add; - } + add = 0; + EnvelopeLevel += add; if (EnvelopeLevel >= SustainLevel) { EnvelopeLevel = SustainLevel; EnvelopeStage = EnvSus; @@ -935,16 +946,16 @@ int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, in break; // Note: fall-through! - MPT_FALLTHROUGH; + [[fallthrough]]; } // Release stage case EnvRel: { - if (ReleaseRate == 0) - break; - if (ReleaseMask && (Master->Clock & ReleaseMask)) - break; uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; + if (ReleaseRate == 0) + add = 0; + if (ReleaseMask && (Master->Clock & ReleaseMask)) + add = 0; EnvelopeLevel += add; if (EnvelopeLevel >= 0x1FF) { EnvelopeLevel = 0x1FF; @@ -1185,7 +1196,7 @@ void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { //================================================================================================== void Opal::Operator::SetKeyScale(uint16_t scale) { - static const uint8_t kslShift[4] = { 15, 1, 2, 0 }; + static constexpr uint8_t kslShift[4] = { 8, 1, 2, 0 }; KeyScaleShift = kslShift[scale]; ComputeKeyScaleLevel(); } @@ -1314,7 +1325,7 @@ void Opal::Operator::ComputeRates() { //================================================================================================== void Opal::Operator::ComputeKeyScaleLevel() { - static const uint8_t levtab[] = { + static constexpr uint8_t levtab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32, 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp index 4d419ec8a..5df1fcb78 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp @@ -240,7 +240,7 @@ bool CPattern::Shrink() } } } - m_ModCommands.resize(m_ModCommands.size() / 2); + m_ModCommands.resize(m_Rows * nChns); return true; } @@ -262,7 +262,7 @@ bool CPattern::SetName(const char *newName, size_t maxChars) { return false; } - m_PatternName.assign(newName, mpt::strnlen(newName, maxChars)); + m_PatternName = mpt::truncate(newName, maxChars); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp index 06d8d62e0..c14b6e923 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp @@ -62,9 +62,9 @@ void DigiBoosterEcho::Process(float *pOutL, float *pOutR, uint32 numFrames) ar += lDelay * m_PCrossPBack; // Prevent denormals - if(mpt::abs(al) < 1e-24f) + if(std::abs(al) < 1e-24f) al = 0.0f; - if(mpt::abs(ar) < 1e-24f) + if(std::abs(ar) < 1e-24f) ar = 0.0f; m_delayLine[m_writePos * 2] = al; @@ -199,7 +199,7 @@ CString DigiBoosterEcho::GetParamDisplay(PlugParamIndex param) IMixPlugin::ChunkData DigiBoosterEcho::GetChunk(bool) { - auto data = reinterpret_cast(&m_chunk); + auto data = reinterpret_cast(&m_chunk); return ChunkData(data, sizeof(m_chunk)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h index 61e9e9cad..60fb1a9ab 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h @@ -14,7 +14,7 @@ OPENMPT_NAMESPACE_BEGIN -class DigiBoosterEcho : public IMixPlugin +class DigiBoosterEcho final : public IMixPlugin { public: enum Parameters @@ -34,7 +34,7 @@ public: static PluginChunk Create(uint8 delay, uint8 feedback, uint8 mix, uint8 cross) { - STATIC_ASSERT(sizeof(PluginChunk) == 8); + static_assert(sizeof(PluginChunk) == 8); PluginChunk result; memcpy(result.id, "Echo", 4); result.param[kEchoDelay] = delay; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp index cb36c4fad..6e789fe4f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp @@ -87,7 +87,7 @@ void LFOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames) value = std::sin(m_phase * 2.0 * M_PI); break; case kTriangle: - value = 1.0 - 4.0 * mpt::abs(m_phase - 0.5); + value = 1.0 - 4.0 * std::abs(m_phase - 0.5); break; case kSaw: value = 2.0 * m_phase - 1.0; @@ -415,7 +415,7 @@ CString LFOPlugin::GetParamDisplay(PlugParamIndex param) return m_bypassed ? _T("Yes") : _T("No"); } else if(param == kWaveform) { - static const TCHAR *waveforms[] = { _T("Sine"), _T("Triangle"), _T("Saw"), _T("Square"), _T("Noise"), _T("Smoothed Noise") }; + static constexpr const TCHAR * const waveforms[] = { _T("Sine"), _T("Triangle"), _T("Saw"), _T("Square"), _T("Noise"), _T("Smoothed Noise") }; if(m_waveForm < MPT_ARRAY_COUNT(waveforms)) return waveforms[m_waveForm]; } else if(param == kLoopMode) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h index 73010709f..030753554 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h @@ -19,7 +19,7 @@ OPENMPT_NAMESPACE_BEGIN -class LFOPlugin : public IMixPlugin +class LFOPlugin final : public IMixPlugin { friend class LFOPluginEditor; @@ -49,7 +49,7 @@ protected: kNumWaveforms }; - std::vector m_chunkData; + std::vector m_chunkData; // LFO parameters float m_amplitude, m_offset, m_frequency; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h index c4db88829..401feefb5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h @@ -15,7 +15,7 @@ OPENMPT_NAMESPACE_BEGIN #ifndef NO_VST -static const char *VstOpCodes[] = +static constexpr const char *VstOpCodes[] = { "effOpen", "effClose", diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp index 8b82a0ab2..981fb213a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp @@ -102,10 +102,10 @@ CString IMixPlugin::GetFormattedParamName(PlugParamIndex param) CString name; if(paramName.IsEmpty()) { - name.Format(_T("%02u: Parameter %02u"), param, param); + name = mpt::cformat(_T("%1: Parameter %2"))(mpt::cfmt::dec0<2>(param), mpt::cfmt::dec0<2>(param)); } else { - name.Format(_T("%02u: %s"), param, paramName.GetString()); + name = mpt::cformat(_T("%1: %2"))(mpt::cfmt::dec0<2>(param), paramName); } return name; } @@ -476,7 +476,7 @@ void IMixPlugin::SaveAllParameters() m_pMixStruct->defaultProgram = -1; // Default implementation: Save all parameter values - PlugParamIndex numParams = std::min(GetNumParameters(), (std::numeric_limits::max() - sizeof(uint32)) / sizeof(IEEE754binary32LE)); + PlugParamIndex numParams = std::min(GetNumParameters(), static_cast((std::numeric_limits::max() - sizeof(uint32)) / sizeof(IEEE754binary32LE))); uint32 nLen = numParams * sizeof(IEEE754binary32LE); if (!nLen) return; nLen += sizeof(uint32); @@ -486,10 +486,12 @@ void IMixPlugin::SaveAllParameters() m_pMixStruct->pluginData.resize(nLen); auto memFile = std::make_pair(mpt::as_span(m_pMixStruct->pluginData), mpt::IO::Offset(0)); mpt::IO::WriteIntLE(memFile, 0); // Plugin data type + BeginGetProgram(); for(PlugParamIndex i = 0; i < numParams; i++) { mpt::IO::Write(memFile, IEEE754binary32LE(GetParameter(i))); } + EndGetProgram(); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { m_pMixStruct->pluginData.clear(); @@ -509,7 +511,7 @@ void IMixPlugin::RestoreAllParameters(int32 /*program*/) const uint32 numParams = GetNumParameters(); if((m_pMixStruct->pluginData.size() - sizeof(uint32)) >= (numParams * sizeof(IEEE754binary32LE))) { - BeginSetProgram(-1); + BeginSetProgram(); for(uint32 i = 0; i < numParams; i++) { SetParameter(i, memFile.ReadFloatLE()); @@ -588,28 +590,26 @@ void IMixPlugin::AutomateParameter(PlugParamIndex param) modDoc->RecordParamChange(GetSlot(), param); } - modDoc->PostMessageToAllViews(WM_MOD_PLUGPARAMAUTOMATE, m_nSlot, param); - // TODO: This should rather be posted to the GUI thread! - CAbstractVstEditor *pVstEditor = GetEditor(); + modDoc->SendNotifyMessageToAllViews(WM_MOD_PLUGPARAMAUTOMATE, m_nSlot, param); - if(pVstEditor && pVstEditor->m_hWnd) + if(auto *vstEditor = GetEditor(); vstEditor && vstEditor->m_hWnd) { // Mark track modified if GUI is open and format supports plugins SetModified(); - if (CMainFrame::GetInputHandler()->ShiftPressed() && TrackerSettings::Instance().midiMappingInPluginEditor) + // Do not use InputHandler in case we are coming from a bridged plugin editor + if((GetAsyncKeyState(VK_SHIFT) & 0x8000) && TrackerSettings::Instance().midiMappingInPluginEditor) { // Shift pressed -> Open MIDI mapping dialog - CMainFrame::GetInputHandler()->SetModifierMask(ModNone); // Make sure that the dialog will open only once. CMainFrame::GetMainFrame()->PostMessage(WM_MOD_MIDIMAPPING, m_nSlot, param); } // Learn macro - int macroToLearn = pVstEditor->GetLearnMacro(); + int macroToLearn = vstEditor->GetLearnMacro(); if (macroToLearn > -1) { modDoc->LearnMacro(macroToLearn, param); - pVstEditor->SetLearnMacro(-1); + vstEditor->SetLearnMacro(-1); } } } @@ -628,7 +628,7 @@ void IMixPlugin::SetModified() bool IMixPlugin::SaveProgram() { mpt::PathString defaultDir = TrackerSettings::Instance().PathPluginPresets.GetWorkingDir(); - bool useDefaultDir = !defaultDir.empty(); + const bool useDefaultDir = !defaultDir.empty(); if(!useDefaultDir && m_Factory.dllPath.IsFile()) { defaultDir = m_Factory.dllPath.GetPath(); @@ -650,19 +650,21 @@ bool IMixPlugin::SaveProgram() TrackerSettings::Instance().PathPluginPresets.SetWorkingDir(dlg.GetWorkingDirectory()); } - bool bank = (dlg.GetExtension() == P_("fxb")); + const bool isBank = (dlg.GetExtension() == P_("fxb")); - 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)) + try { - return true; - } else + mpt::SafeOutputFile sf(dlg.GetFirstFile(), std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave)); + mpt::ofstream &f = sf; + f.exceptions(f.exceptions() | std::ios::badbit | std::ios::failbit); + if(f.good() && VSTPresets::SaveFile(f, *this, isBank)) + return true; + } catch(const std::exception &) { - Reporting::Error("Error saving preset.", m_pEditor); - return false; + } - + Reporting::Error("Error saving preset.", m_pEditor); + return false; } @@ -694,7 +696,7 @@ bool IMixPlugin::LoadProgram(mpt::PathString fileName) } const char *errorStr = nullptr; - InputFile f(fileName); + InputFile f(fileName, SettingCacheCompleteFileBeforeLoading()); if(f.IsValid()) { FileReader file = GetFileReader(f); @@ -922,8 +924,17 @@ void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vo // Problem: if a note dies out naturally and we never send a note off, this counter // will block at max until note off. Is this a problem? // Safe to assume we won't need more than 16 note offs max on a given note? +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:6385) // false-positive: Reading invalid data from 'channel.noteOnMap': the readable size is '32768' bytes, but 'note' bytes may be read. +#endif // MPT_COMPILER_MSVC if(channel.noteOnMap[note][trackChannel] < uint8_max) +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + { channel.noteOnMap[note][trackChannel]++; + } MidiSend(MIDIEvents::NoteOn(midiCh, static_cast(note), volume)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h index bb5531264..d8e6ac188 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h @@ -209,16 +209,18 @@ public: void SetModified(); #endif - virtual void BeginSetProgram(int32 /*program*/ = -1) { } - virtual void EndSetProgram() { } - virtual int GetNumInputChannels() const = 0; virtual int GetNumOutputChannels() const = 0; - typedef mpt::const_byte_span ChunkData; + using ChunkData = mpt::const_byte_span; virtual bool ProgramsAreChunks() const { return false; } virtual ChunkData GetChunk(bool /*isBank*/) { return ChunkData(); } virtual void SetChunk(const ChunkData &/*chunk*/, bool /*isBank*/) { } + + virtual void BeginSetProgram(int32 /*program*/ = -1) {} + virtual void EndSetProgram() {} + virtual void BeginGetProgram(int32 /*program*/ = -1) {} + virtual void EndGetProgram() {} }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp index 786bb7590..ad515561e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp @@ -44,11 +44,11 @@ #include "../../pluginBridge/BridgeWrapper.h" #endif // NO_VST -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) #include #include #include -#endif // NO_DMO +#endif // MPT_WITH_DMO #ifdef MODPLUG_TRACKER #include "../../mptrack/Mptrack.h" @@ -60,31 +60,159 @@ OPENMPT_NAMESPACE_BEGIN -//#define VST_LOG -//#define DMO_LOG +#ifdef MPT_ALL_LOGGING +#define VST_LOG +#define DMO_LOG +#endif #ifdef MODPLUG_TRACKER -static const MPT_UCHAR_TYPE *const cacheSection = UL_("PluginCache"); +static constexpr const mpt::uchar *cacheSection = UL_("PluginCache"); #endif // MODPLUG_TRACKER -uint8 VSTPluginLib::GetDllBits(bool fromCache) const +#ifndef NO_VST + + +uint8 VSTPluginLib::GetNativePluginArch() +{ + uint8 result = 0; + switch(mpt::Windows::GetProcessArchitecture()) + { + case mpt::Windows::Architecture::x86: + result = PluginArch_x86; + break; + case mpt::Windows::Architecture::amd64: + result = PluginArch_amd64; + break; + case mpt::Windows::Architecture::arm: + result = PluginArch_arm; + break; + case mpt::Windows::Architecture::arm64: + result = PluginArch_arm64; + break; + default: + result = 0; + break; + } + return result; +} + + +mpt::ustring VSTPluginLib::GetPluginArchName(uint8 arch) +{ + mpt::ustring result; + switch(arch) + { + case PluginArch_x86: + result = U_("x86"); + break; + case PluginArch_amd64: + result = U_("amd64"); + break; + case PluginArch_arm: + result = U_("arm"); + break; + case PluginArch_arm64: + result = U_("arm64"); + break; + default: + result = U_(""); + break; + } + return result; +} + + +mpt::ustring VSTPluginLib::GetPluginArchNameUser(uint8 arch) +{ + mpt::ustring result; + #if defined(MPT_WITH_WINDOWS10) + switch(arch) + { + case PluginArch_x86: + result = U_("x86 (32bit)"); + break; + case PluginArch_amd64: + result = U_("amd64 (64bit)"); + break; + case PluginArch_arm: + result = U_("arm (32bit)"); + break; + case PluginArch_arm64: + result = U_("arm64 (64bit)"); + break; + default: + result = U_(""); + break; + } + #else // !MPT_WITH_WINDOWS10 + switch(arch) + { + case PluginArch_x86: + result = U_("32-Bit"); + break; + case PluginArch_amd64: + result = U_("64-Bit"); + break; + case PluginArch_arm: + result = U_("32-Bit"); + break; + case PluginArch_arm64: + result = U_("64-Bit"); + break; + default: + result = U_(""); + break; + } + #endif // MPT_WITH_WINDOWS10 + return result; +} + + +uint8 VSTPluginLib::GetDllArch(bool fromCache) const { // Built-in plugins are always native. if(dllPath.empty()) - return mpt::arch_bits; + return GetNativePluginArch(); #ifndef NO_VST - if(!dllBits || !fromCache) + if(!dllArch || !fromCache) { - dllBits = static_cast(BridgeWrapper::GetPluginBinaryType(dllPath)); + dllArch = static_cast(BridgeWrapper::GetPluginBinaryType(dllPath)); } #else MPT_UNREFERENCED_PARAMETER(fromCache); #endif // NO_VST - return dllBits; + return dllArch; } +mpt::ustring VSTPluginLib::GetDllArchName(bool fromCache) const +{ + return GetPluginArchName(GetDllArch(fromCache)); +} + + +mpt::ustring VSTPluginLib::GetDllArchNameUser(bool fromCache) const +{ + return GetPluginArchNameUser(GetDllArch(fromCache)); +} + + +bool VSTPluginLib::IsNative(bool fromCache) const +{ + return GetDllArch(fromCache) == GetNativePluginArch(); +} + + +bool VSTPluginLib::IsNativeFromCache() const +{ + return dllArch == GetNativePluginArch() || dllArch == 0; +} + + +#endif // !NO_VST + + // PluginCache format: // FullDllPath = (hex-encoded) // .Flags = Plugin Flags (see VSTPluginLib::DecodeCacheFlags). @@ -96,13 +224,13 @@ void VSTPluginLib::WriteToCache() const SettingsContainer &cacheFile = theApp.GetPluginCache(); const std::string crcName = dllPath.ToUTF8(); - const uint32 crc = mpt::crc32(crcName); - const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc); + const mpt::crc32 crc(crcName); + const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc.result()); mpt::PathString writePath = dllPath; if(theApp.IsPortableMode()) { - writePath = theApp.AbsolutePathToRelative(writePath); + writePath = theApp.PathAbsoluteToInstallRelative(writePath); } cacheFile.Write(cacheSection, writePath.ToUnicode(), IDs); @@ -124,7 +252,7 @@ bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile) #else if(!sndFile.m_PluginManager) { - sndFile.m_PluginManager = mpt::make_unique(); + sndFile.m_PluginManager = std::make_unique(); } return sndFile.m_PluginManager->CreateMixPlugin(mixPlugin, sndFile); #endif // MODPLUG_TRACKER @@ -133,7 +261,7 @@ bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile) CVstPluginManager::CVstPluginManager() { -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) HRESULT COMinit = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(COMinit == S_OK || COMinit == S_FALSE) { @@ -170,7 +298,7 @@ CVstPluginManager::CVstPluginManager() #endif // MODPLUG_TRACKER }; - pluginList.reserve(mpt::size(BuiltInPlugins)); + pluginList.reserve(std::size(BuiltInPlugins)); for(const auto &plugin : BuiltInPlugins) { VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(plugin.createProc, true, mpt::PathString::FromUTF8(plugin.filename), mpt::PathString::FromUTF8(plugin.name)); @@ -205,7 +333,7 @@ CVstPluginManager::~CVstPluginManager() } delete plug; } -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) if(MustUnInitilizeCOM) { CoUninitialize(); @@ -223,14 +351,8 @@ bool CVstPluginManager::IsValidPlugin(const VSTPluginLib *pLib) const void CVstPluginManager::EnumerateDirectXDMOs() { -#ifndef NO_DMO -#if MPT_MSVC_BEFORE(2017,0) - // VS2015 crashes if knownDMOs is constexpr. - const -#else - constexpr -#endif - mpt::UUID knownDMOs[] = +#if defined(MPT_WITH_DMO) + constexpr mpt::UUID knownDMOs[] = { "745057C7-F353-4F2D-A7EE-58434477730E"_uuid, // AEC (Acoustic echo cancellation, not usable) "EFE6629C-81F7-4281-BD91-C9D604A95AF6"_uuid, // Chorus @@ -287,7 +409,7 @@ void CVstPluginManager::EnumerateDirectXDMOs() delete plug; } #ifdef DMO_LOG - Log(mpt::format(U_("Found \"%1\" clsid=%2\n"))(plug->libraryName, plug->dllPath)); + MPT_LOG(LogDebug, "DMO", mpt::format(U_("Found \"%1\" clsid=%2\n"))(plug->libraryName, plug->dllPath)); #endif } } @@ -299,7 +421,7 @@ void CVstPluginManager::EnumerateDirectXDMOs() index++; } if (hkEnum) RegCloseKey(hkEnum); -#endif // NO_DMO +#endif // MPT_WITH_DMO } @@ -322,7 +444,7 @@ static void GetPluginInformation(Vst::AEffect *effect, VSTPluginLib &library) #ifdef MODPLUG_TRACKER std::vector s(256, 0); CVstPlugin::DispatchSEH(effect, Vst::effGetVendorString, 0, 0, s.data(), 0, exception); - library.vendor = mpt::ToCString(mpt::CharsetLocale, s.data()); + library.vendor = mpt::ToCString(mpt::Charset::Locale, s.data()); #endif // MODPLUG_TRACKER } #endif // NO_VST @@ -354,7 +476,7 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const if(IDs.length() < 16) { // If that didn't work out, find relative path - mpt::PathString relPath = theApp.AbsolutePathToRelative(dllPath); + mpt::PathString relPath = theApp.PathAbsoluteToInstallRelative(dllPath); IDs = cacheFile.Read(cacheSection, relPath.ToUnicode(), U_("")); } @@ -387,13 +509,13 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const plug->vendor = cacheFile.Read(cacheSection, IDs + U_(".Vendor"), CString()); #ifdef VST_LOG - Log("Plugin \"%s\" found in PluginCache\n", plug->libraryName.ToLocale().c_str()); + MPT_LOG(LogDebug, "VST", mpt::format(U_("Plugin \"%1\" found in PluginCache"))(plug->libraryName)); #endif // VST_LOG return plug; } else { #ifdef VST_LOG - Log("Plugin \"%s\" mismatch in PluginCache: \"%s\" [%s]=\"%s\"\n", s, dllPath, (LPCTSTR)IDs, (LPCTSTR)strFullPath); + MPT_LOG(LogDebug, "VST", mpt::format(U_("Plugin mismatch in PluginCache: \"%1\" [%2]"))(dllPath, IDs)); #endif // VST_LOG } } @@ -425,13 +547,13 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const GetPluginInformation(pEffect, *plug); #ifdef VST_LOG - int nver = CVstPlugin::DispatchSEH(pEffect, effGetVstVersion, 0,0, nullptr, 0, exception); + intptr_t nver = CVstPlugin::DispatchSEH(pEffect, Vst::effGetVstVersion, 0,0, nullptr, 0, exception); if (!nver) nver = pEffect->version; - Log("%-20s: v%d.0, %d in, %d out, %2d programs, %2d params, flags=0x%04X realQ=%d offQ=%d\n", - plug->libraryName.ToLocale().c_str(), nver, + MPT_LOG(LogDebug, "VST", mpt::format(U_("%1: v%2.0, %3 in, %4 out, %5 programs, %6 params, flags=0x%7 realQ=%8 offQ=%9"))( + plug->libraryName, nver, pEffect->numInputs, pEffect->numOutputs, - pEffect->numPrograms, pEffect->numParams, - pEffect->flags, pEffect->realQualities, pEffect->offQualities); + mpt::ufmt::dec0<2>(pEffect->numPrograms), mpt::ufmt::dec0<2>(pEffect->numParams), + mpt::ufmt::HEX0<4>(static_cast(pEffect->flags)), pEffect->realQualities, pEffect->offQualities)); #endif // VST_LOG CVstPlugin::DispatchSEH(pEffect, Vst::effClose, 0, 0, 0, 0, exception); @@ -497,7 +619,15 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd #endif // MODPLUG_TRACKER // Find plugin in library - int8 match = 0; // "Match quality" of found plugin. Higher value = better match. + enum PlugMatchQuality + { + kNoMatch, + kMatchName, + kMatchId, + kMatchNameAndId, + }; + + PlugMatchQuality match = kNoMatch; // "Match quality" of found plugin. Higher value = better match. #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT const mpt::PathString libraryName = mpt::PathString::FromUTF8(mixPlugin.GetLibraryName()); #else @@ -516,20 +646,22 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd if(matchID && matchName) { pFound = plug; +#ifndef NO_VST if(plug->IsNative(false)) { break; } +#endif //!NO_VST // If the plugin isn't native, first check if a native version can be found. - match = 3; - } else if(matchID && match < 2) + match = kMatchNameAndId; + } else if(matchID && match < kMatchId) { pFound = plug; - match = 2; - } else if(matchName && match < 1) + match = kMatchId; + } else if(matchName && match < kMatchName) { pFound = plug; - match = 1; + match = kMatchName; } } @@ -546,7 +678,7 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd mpt::PathString fullPath = TrackerSettings::Instance().PathPlugins.GetDefaultDir(); if(fullPath.empty()) { - fullPath = theApp.GetAppDirPath() + P_("Plugins\\"); + fullPath = theApp.GetInstallPath() + P_("Plugins\\"); } fullPath += mpt::PathString::FromUTF8(mixPlugin.GetLibraryName()) + P_(".dll"); @@ -555,13 +687,13 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd { // Try plugin cache (search for library name) SettingsContainer &cacheFile = theApp.GetPluginCache(); - mpt::ustring IDs = cacheFile.Read(cacheSection, mpt::ToUnicode(mpt::CharsetUTF8, mixPlugin.GetLibraryName()), U_("")); + mpt::ustring IDs = cacheFile.Read(cacheSection, mpt::ToUnicode(mpt::Charset::UTF8, mixPlugin.GetLibraryName()), U_("")); if(IDs.length() >= 16) { fullPath = cacheFile.Read(cacheSection, IDs, P_("")); if(!fullPath.empty()) { - fullPath = theApp.RelativePathToAbsolute(fullPath); + fullPath = theApp.PathInstallRelativeToAbsolute(fullPath); if(fullPath.IsFile()) { pFound = AddPlugin(fullPath); @@ -606,7 +738,7 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd { // "plug not found" notification code MOVED to CSoundFile::Create #ifdef VST_LOG - Log("Unknown plugin\n"); + MPT_LOG(LogDebug, "VST", U_("Unknown plugin")); #endif } #endif // NO_VST @@ -645,7 +777,7 @@ void CVstPluginManager::ReportPlugException(const mpt::ustring &msg) { Reporting::Notification(msg); #ifdef VST_LOG - Log(mpt::ToUnicode(msg)); + MPT_LOG(LogDebug, "VST", mpt::ToUnicode(msg)); #endif } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h index 45c3e5c71..df2bcd8b3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h @@ -23,6 +23,7 @@ OPENMPT_NAMESPACE_BEGIN class CSoundFile; class IMixPlugin; struct SNDMIXPLUGIN; +enum PluginArch : int; struct VSTPluginLib { @@ -66,7 +67,7 @@ public: bool isInstrument : 1; bool useBridge : 1, shareBridgeInstance : 1; protected: - mutable uint8 dllBits = 0; + mutable uint8 dllArch = 0; public: VSTPluginLib(CreateProc factoryProc, bool isBuiltIn, const mpt::PathString &dllPath, const mpt::PathString &libraryName @@ -86,23 +87,34 @@ public: { } +#ifndef NO_VST + + // Get native phost process arch encoded as plugin arch + static uint8 GetNativePluginArch(); + static mpt::ustring GetPluginArchName(uint8 arch); + static mpt::ustring GetPluginArchNameUser(uint8 arch); + // Check whether a plugin can be hosted inside OpenMPT or requires bridging - uint8 GetDllBits(bool fromCache = true) const; - bool IsNative(bool fromCache = true) const { return GetDllBits(fromCache) == mpt::arch_bits; } + uint8 GetDllArch(bool fromCache = true) const; + mpt::ustring GetDllArchName(bool fromCache = true) const; + mpt::ustring GetDllArchNameUser(bool fromCache = true) const; + bool IsNative(bool fromCache = true) const; // Check if a plugin is native, and if it is currently unknown, assume that it is native. Use this function only for performance reasons // (e.g. if tons of unscanned plugins would slow down generation of the plugin selection dialog) - bool IsNativeFromCache() const { return dllBits == mpt::arch_bits || dllBits == 0; } + bool IsNativeFromCache() const; + +#endif // !NO_VST void WriteToCache() const; uint32 EncodeCacheFlags() const { - // Format: 00000000.00000000.DDDDDDSB.CCCCCCCI + // Format: 00000000.00000000.AAAAAASB.CCCCCCCI return (isInstrument ? 1 : 0) | (category << 1) | (useBridge ? 0x100 : 0) | (shareBridgeInstance ? 0x200 : 0) - | ((dllBits / 8) << 10); + | ((dllArch / 8) << 10); } void DecodeCacheFlags(uint32 flags) @@ -119,7 +131,7 @@ public: } useBridge = (flags & 0x100) != 0; shareBridgeInstance = (flags & 0x200) != 0; - dllBits = ((flags >> 10) & 0x3F) * 8; + dllArch = ((flags >> 10) & 0x3F) * 8; } }; @@ -128,7 +140,7 @@ class CVstPluginManager { #ifndef NO_PLUGINS protected: -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) bool MustUnInitilizeCOM = false; #endif std::vector pluginList; @@ -137,14 +149,15 @@ public: CVstPluginManager(); ~CVstPluginManager(); - typedef std::vector::iterator iterator; - typedef std::vector::const_iterator const_iterator; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; iterator begin() { return pluginList.begin(); } const_iterator begin() const { return pluginList.begin(); } iterator end() { return pluginList.end(); } const_iterator end() const { return pluginList.end(); } void reserve(size_t num) { pluginList.reserve(num); } + size_t size() const { return pluginList.size(); } bool IsValidPlugin(const VSTPluginLib *pLib) const; VSTPluginLib *AddPlugin(const mpt::PathString &dllPath, const mpt::ustring &tags = mpt::ustring(), bool fromCache = true, bool *fileFound = nullptr); @@ -161,6 +174,7 @@ public: const VSTPluginLib **begin() const { return nullptr; } const VSTPluginLib **end() const { return nullptr; } void reserve(size_t) { } + size_t size() const { return 0; } void OnIdle() {} #endif // NO_PLUGINS diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h index 4e18524c9..da5989984 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h @@ -12,6 +12,9 @@ #include "BuildSettings.h" +#include +#include + #include "../../common/mptAlloc.h" @@ -24,27 +27,26 @@ OPENMPT_NAMESPACE_BEGIN template class PluginMixBuffer { -protected: - std::vector inputs; // Pointers to input buffers - std::vector outputs; // Pointers to output buffers +private: + #if defined(MPT_ENABLE_ALIGNED_ALLOC) - mpt::aligned_buffer alignedBuffer; // Aligned buffer pointed into -#else - std::vector alignedBuffer; + static constexpr std::align_val_t alignment = std::align_val_t{16}; + static_assert(sizeof(mpt::aligned_array) == sizeof(std::array)); + static_assert(alignof(mpt::aligned_array) == static_cast(alignment)); #endif - // Return pointer to an aligned buffer - 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]; - } +protected: + +#if defined(MPT_ENABLE_ALIGNED_ALLOC) + std::vector> inputs; + std::vector> outputs; +#else + std::vector> inputs; + std::vector> outputs; +#endif + std::vector inputsarray; + std::vector outputsarray; public: @@ -61,14 +63,8 @@ public: { inputs.resize(numInputs); outputs.resize(numOutputs); - - // Create inputs + outputs buffers - #if defined(MPT_ENABLE_ALIGNED_ALLOC) - alignedBuffer.destructive_resize(bufferSize * (numInputs + numOutputs)); - #else - alignedBuffer.resize(bufferSize * (numInputs + numOutputs)); - #endif - + inputsarray.resize(numInputs); + outputsarray.resize(numOutputs); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); @@ -76,22 +72,21 @@ public: inputs.shrink_to_fit(); outputs.clear(); outputs.shrink_to_fit(); - #if defined(MPT_ENABLE_ALIGNED_ALLOC) - alignedBuffer.destructive_resize(0); - #else - alignedBuffer.resize(0); - #endif + inputsarray.clear(); + inputsarray.shrink_to_fit(); + outputsarray.clear(); + outputsarray.shrink_to_fit(); return false; } for(uint32 i = 0; i < numInputs; i++) { - inputs[i] = GetInputBuffer(i); + inputsarray[i] = inputs[i].data(); } for(uint32 i = 0; i < numOutputs; i++) { - outputs[i] = GetOutputBuffer(i); + outputsarray[i] = outputs[i].data(); } return true; @@ -103,7 +98,7 @@ public: MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < inputs.size(); i++) { - std::memset(inputs[i], 0, numSamples * sizeof(buffer_t)); + std::fill(inputs[i].data(), inputs[i].data() + numSamples, buffer_t{0}); } } @@ -113,7 +108,7 @@ public: MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < outputs.size(); i++) { - std::memset(outputs[i], 0, numSamples * sizeof(buffer_t)); + std::fill(outputs[i].data(), outputs[i].data() + numSamples, buffer_t{0}); } } @@ -123,16 +118,17 @@ public: } // Return pointer to a given input or output buffer - 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); } + const buffer_t *GetInputBuffer(uint32 index) const { return inputs[index].data(); } + const buffer_t *GetOutputBuffer(uint32 index) const { return outputs[index].data(); } + buffer_t *GetInputBuffer(uint32 index) { return inputs[index].data(); } + buffer_t *GetOutputBuffer(uint32 index) { return outputs[index].data(); } // Return pointer array to all input or output buffers - buffer_t **GetInputBufferArray() { return inputs.empty() ? nullptr : inputs.data(); } - buffer_t **GetOutputBufferArray() { return outputs.empty() ? nullptr : outputs.data(); } + buffer_t **GetInputBufferArray() { return inputs.empty() ? nullptr : inputsarray.data(); } + buffer_t **GetOutputBufferArray() { return outputs.empty() ? nullptr : outputsarray.data(); } + + bool Ok() const { return (inputs.size() + outputs.size()) > 0; } - 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 03223fe6f..947d761eb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h @@ -52,8 +52,8 @@ struct SNDMIXPLUGININFO uint8le reserved; uint32le dwOutputRouting; // 0 = send to master 0x80 + x = send to plugin x uint32le dwReserved[4]; // Reserved for routing info - char szName[32]; // User-chosen plugin display name - this is locale ANSI! - char szLibraryName[64]; // original DLL name - this is UTF-8! + mpt::charbuf<32, mpt::String::nullTerminated> szName; // User-chosen plugin display name - this is locale ANSI! + mpt::charbuf<64, mpt::String::nullTerminated> szLibraryName; // original DLL name - this is UTF-8! // Should only be called from SNDMIXPLUGIN::SetBypass() and IMixPlugin::Bypass() void SetBypass(bool bypass = true) { if(bypass) routingFlags |= irBypass; else routingFlags &= uint8(~irBypass); } @@ -65,7 +65,7 @@ MPT_BINARY_STRUCT(SNDMIXPLUGININFO, 128) // this is directly written to files, s struct SNDMIXPLUGIN { IMixPlugin *pMixPlugin; - std::vector pluginData; + std::vector pluginData; SNDMIXPLUGININFO Info; float fDryRatio; int32 defaultProgram; @@ -81,9 +81,9 @@ struct SNDMIXPLUGIN } const char *GetName() const - { return Info.szName; } + { return Info.szName.buf; } const char *GetLibraryName() const - { return Info.szLibraryName; } + { return Info.szLibraryName.buf; } // 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; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp index f128551a7..a254a6660 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp @@ -86,8 +86,8 @@ void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) m_waveShapeMin -= 2; if(m_waveShapeMax > 1) m_waveShapeMax -= 2; - waveMin = mpt::abs(m_waveShapeMin) * 2 - 1; - waveMax = mpt::abs(m_waveShapeMax) * 2 - 1; + waveMin = std::abs(m_waveShapeMin) * 2 - 1; + waveMax = std::abs(m_waveShapeMax) * 2 - 1; } else { m_waveShapeMin = m_waveShapeMax * m_waveShapeVal + m_waveShapeMin; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp index 757e74f69..9549b2929 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp @@ -64,11 +64,11 @@ void Compressor::Process(float *pOutL, float *pOutR, uint32 numFrames) m_buffer[m_bufPos * 2] = leftIn; m_buffer[m_bufPos * 2 + 1] = rightIn; - leftIn = mpt::abs(leftIn); - rightIn = mpt::abs(rightIn); + leftIn = std::abs(leftIn); + rightIn = std::abs(rightIn); float mono = (leftIn + rightIn) * (0.5f * 32768.0f * 32768.0f); - float monoLog = mpt::abs(logGain(mono, 31, 5)) * (1.0f / float(1u << 31)); + float monoLog = std::abs(logGain(mono, 31, 5)) * (1.0f / float(1u << 31)); float newPeak = monoLog + (m_peak - monoLog) * ((m_peak <= monoLog) ? m_attack : m_release); m_peak = newPeak; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h index 56fb3cead..2fae55a20 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Compressor : public IMixPlugin +class Compressor final : public IMixPlugin { protected: enum Parameters @@ -106,4 +106,4 @@ protected: OPENMPT_NAMESPACE_END -#endif // !NO_PLUGINS && NO_DMO +#endif // !NO_PLUGINS diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp index 83e50641e..8b4806242 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp @@ -12,7 +12,7 @@ #include "stdafx.h" -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) #include "../../Sndfile.h" #include "../../../common/mptUUID.h" #include "DMOPlugin.h" @@ -20,15 +20,20 @@ #include #include #include -#endif // !NO_DMO +#endif // MPT_WITH_DMO OPENMPT_NAMESPACE_BEGIN -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) +#ifdef MPT_ALL_LOGGING #define DMO_LOG +#else +#define DMO_LOG +#endif + IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { @@ -51,11 +56,11 @@ IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIX return p; } #ifdef DMO_LOG - Log(factory.libraryName.ToUnicode() + U_(": Unable to use this DMO")); + MPT_LOG(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Unable to use this DMO")); #endif } #ifdef DMO_LOG - else Log(factory.libraryName.ToUnicode() + U_(": Failed to get IMediaObject & IMediaObjectInPlace interfaces")); + else MPT_LOG(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Failed to get IMediaObject & IMediaObjectInPlace interfaces")); #endif if (pMO) pMO->Release(); if (pMOIP) pMOIP->Release(); @@ -121,8 +126,8 @@ uint32 DMOPlugin::GetLatency() const } -static const float _f2si = 32768.0f; -static const float _si2f = 1.0f / 32768.0f; +static constexpr float _f2si = 32768.0f; +static constexpr float _si2f = 1.0f / 32768.0f; static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, float * MPT_RESTRICT output, uint32 numFrames) @@ -131,7 +136,7 @@ static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MP 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 - STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); + static_assert((MIXBUFFERSIZE & 7) == 0); __m128 factor = _mm_set_ps1(_f2si); numFrames = (numFrames + 3) / 4; do @@ -165,7 +170,7 @@ static void DeinterleaveStereo(const float * MPT_RESTRICT input, float * MPT_RES 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 - STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); + static_assert((MIXBUFFERSIZE & 7) == 0); __m128 factor = _mm_set_ps1(_si2f); numFrames = (numFrames + 3) / 4; do @@ -202,7 +207,7 @@ static void InterleaveFloatToInt16(const float * MPT_RESTRICT inputL, const floa 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); + static_assert((MIXBUFFERSIZE & 7) == 0); __m64 *out = reinterpret_cast<__m64 *>(output); __m128 factor = _mm_set_ps1(_f2si); numFrames = (numFrames + 3) / 4; @@ -254,7 +259,7 @@ static void DeinterleaveInt16ToFloat(const int16 * MPT_RESTRICT input, float * M 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); + static_assert((MIXBUFFERSIZE & 7) == 0); const __m128i *in = reinterpret_cast(input); __m128 factor = _mm_set_ps1(_si2f); numFrames = (numFrames + 3) / 4; @@ -433,7 +438,7 @@ void DMOPlugin::Resume() || FAILED(m_pMediaObject->SetOutputType(0, &mt, 0))) { #ifdef DMO_LOG - Log(U_("DMO: Failed to set I/O media type")); + MPT_LOG(LogDebug, "DMO", U_("DMO: Failed to set I/O media type")); #endif } } @@ -552,11 +557,11 @@ CString DMOPlugin::GetParamDisplay(PlugParamIndex param) #endif // MODPLUG_TRACKER -#else // NO_DMO +#else // !MPT_WITH_DMO MPT_MSVC_WORKAROUND_LNK4221(DMOPlugin) -#endif // !NO_DMO +#endif // MPT_WITH_DMO OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h index 0fbb86341..9d0c737f8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h @@ -8,7 +8,7 @@ */ -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) #include "../PlugInterface.h" #include @@ -21,7 +21,7 @@ typedef interface IMediaParams IMediaParams; OPENMPT_NAMESPACE_BEGIN -class DMOPlugin : public IMixPlugin +class DMOPlugin final : public IMixPlugin { protected: IMediaObject *m_pMediaObject; @@ -96,5 +96,5 @@ public: OPENMPT_NAMESPACE_END -#endif // NO_DMO +#endif // MPT_WITH_DMO diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp index eb15b0f03..fb9da9b89 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp @@ -222,7 +222,7 @@ void Distortion::RecalculateDistortionParams() shift = 5; m_shift = shift; - static const float LogNorm[32] = + static constexpr float LogNorm[32] = { 1.00f, 1.00f, 1.50f, 1.00f, 1.75f, 1.40f, 1.17f, 1.00f, 1.88f, 1.76f, 1.50f, 1.36f, 1.25f, 1.15f, 1.07f, 1.00f, diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h index 9ce1ff158..39a700917 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Distortion : public IMixPlugin +class Distortion final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp index f51756432..14d6c0a47 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp @@ -71,7 +71,7 @@ void Echo::Process(float *pOutL, float *pOutR, uint32 numFrames) chnOutput += chnDelay * m_param[kEchoFeedback]; // Prevent denormals - if(mpt::abs(chnOutput) < 1e-24f) + if(std::abs(chnOutput) < 1e-24f) chnOutput = 0.0f; m_delayLine[m_writePos * 2 + channel] = chnOutput; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h index 5d4e6322b..b56d6c95b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Echo : public IMixPlugin +class Echo final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h index 549921c8f..8b964ed14 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Flanger : public Chorus +class Flanger final : public Chorus { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h index a05960bfa..6fa6b20f7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Gargle : public IMixPlugin +class Gargle final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp index e339fe38c..658767830 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp @@ -13,6 +13,9 @@ #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "I3DL2Reverb.h" +#ifdef MODPLUG_TRACKER +#include "../../../sounddsp/Reverb.h" +#endif // MODPLUG_TRACKER #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN @@ -76,7 +79,6 @@ IMixPlugin* I3DL2Reverb::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDM I3DL2Reverb::I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) - , m_recalcParams(true) { m_param[kI3DL2ReverbRoom] = 0.9f; m_param[kI3DL2ReverbRoomHF] = 0.99f; @@ -92,6 +94,8 @@ I3DL2Reverb::I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGI m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f; m_param[kI3DL2ReverbQuality] = 2.0f / 3.0f; + SetCurrentProgram(m_program); + m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } @@ -266,8 +270,8 @@ void I3DL2Reverb::Process(float *pOutL, float *pOutR, uint32 numFrames) float outL = earlyRefOutL + lateRevOutL; float outR = earlyRefOutR + lateRevOutR; - for(std::size_t d = 0; d < mpt::size(m_delayLines); d++) - m_delayLines[d].Advance(); + for(auto &line : m_delayLines) + line.Advance(); if(!(m_quality & kFullSampleRate)) { @@ -292,6 +296,42 @@ void I3DL2Reverb::Process(float *pOutL, float *pOutR, uint32 numFrames) } +int32 I3DL2Reverb::GetNumPrograms() const +{ +#ifdef MODPLUG_TRACKER + return NUM_REVERBTYPES; +#else + return 0; +#endif +} + +void I3DL2Reverb::SetCurrentProgram(int32 program) +{ +#ifdef MODPLUG_TRACKER + if(program < NUM_REVERBTYPES) + { + m_program = program; + const auto &preset = *GetReverbPreset(m_program); + m_param[kI3DL2ReverbRoom] = (preset.lRoom + 10000) / 10000.0f; + m_param[kI3DL2ReverbRoomHF] = (preset.lRoomHF + 10000) / 10000.0f; + m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f; + m_param[kI3DL2ReverbDecayTime] = (preset.flDecayTime - 0.1f) / 19.9f; + m_param[kI3DL2ReverbDecayHFRatio] = (preset.flDecayHFRatio - 0.1f) / 1.9f; + m_param[kI3DL2ReverbReflections] = (preset.lReflections + 10000) / 11000.0f; + m_param[kI3DL2ReverbReflectionsDelay] = preset.flReflectionsDelay / 0.3f; + m_param[kI3DL2ReverbReverb] = (preset.lReverb + 10000) / 12000.0f; + m_param[kI3DL2ReverbReverbDelay] = preset.flReverbDelay / 0.1f; + m_param[kI3DL2ReverbDiffusion] = preset.flDiffusion / 100.0f; + m_param[kI3DL2ReverbDensity] = preset.flDensity / 100.0f; + m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f; + RecalculateI3DL2ReverbParams(); + } +#else + MPT_UNUSED_VARIABLE(program); +#endif +} + + PlugParamValue I3DL2Reverb::GetParameter(PlugParamIndex index) { if(index < kI3DL2ReverbNumParameters) @@ -410,7 +450,7 @@ CString I3DL2Reverb::GetParamLabel(PlugParamIndex param) CString I3DL2Reverb::GetParamDisplay(PlugParamIndex param) { - static const TCHAR *modes[] = { _T("LQ"), _T("LQ+"), _T("HQ"), _T("HQ+") }; + static constexpr const TCHAR * const modes[] = { _T("LQ"), _T("LQ+"), _T("HQ"), _T("HQ+") }; float value = m_param[param]; switch(param) { @@ -433,6 +473,18 @@ CString I3DL2Reverb::GetParamDisplay(PlugParamIndex param) return s; } + +CString I3DL2Reverb::GetCurrentProgramName() +{ + return GetProgramName(m_program); +} + + +CString I3DL2Reverb::GetProgramName(int32 program) +{ + return mpt::ToCString(GetReverbPresetName(program)); +} + #endif // MODPLUG_TRACKER @@ -468,7 +520,7 @@ void I3DL2Reverb::RecalculateI3DL2ReverbParams() void I3DL2Reverb::SetDelayTaps() { // Early reflections - static const float delays[] = + static constexpr float delays[] = { 1.0000f, 1.0000f, 0.0000f, 0.1078f, 0.1768f, 0.2727f, 0.3953f, 0.5386f, 0.6899f, 0.8306f, 0.9400f, 0.9800f, @@ -490,7 +542,7 @@ void I3DL2Reverb::SetDelayTaps() for(int i = 0, power = 0; i < 6; i++) { power += i; - float factor = std::pow(0.93f, power); + float factor = std::pow(0.93f, static_cast(power)); m_delayTaps[i + 0] = static_cast(delayL * factor); m_delayTaps[i + 6] = static_cast(delayR * factor); } @@ -499,7 +551,7 @@ void I3DL2Reverb::SetDelayTaps() m_delayTaps[13] = static_cast(3.25f / 1000.0f * sampleRate); m_delayTaps[14] = static_cast(3.53f / 1000.0f * sampleRate); - for(std::size_t d = 0; d < mpt::size(m_delayTaps); d++) + for(std::size_t d = 0; d < std::size(m_delayTaps); d++) m_delayLines[d].SetDelayTap(m_delayTaps[d]); } @@ -569,7 +621,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.0f) + if(std::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 29e9dfdd4..e172224f8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class I3DL2Reverb : public IMixPlugin +class I3DL2Reverb final : public IMixPlugin { protected: enum Parameters @@ -64,6 +64,7 @@ protected: }; float m_param[kI3DL2ReverbNumParameters]; + int32 m_program = 0; // Calculated parameters uint32 m_quality; @@ -85,9 +86,9 @@ protected: // Remaining frame for downsampled reverb float m_prevL; float m_prevR; - bool m_remain; + bool m_remain = false; - bool m_ok, m_recalcParams; + bool m_ok = false, m_recalcParams = true; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); @@ -103,9 +104,10 @@ public: float RenderSilence(uint32) override { return 0.0f; } - int32 GetNumPrograms() const override { return 0; } - int32 GetCurrentProgram() override { return 0; } - void SetCurrentProgram(int32) override { } + int32 GetNumPrograms() const override; + int32 GetCurrentProgram() override { return m_program; } + // cppcheck-suppress virtualCallInConstructor + void SetCurrentProgram(int32) override; PlugParamIndex GetNumParameters() const override { return kI3DL2ReverbNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; @@ -125,9 +127,9 @@ public: CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; - CString GetCurrentProgramName() override { return CString(); } + CString GetCurrentProgramName() override; void SetCurrentProgramName(const CString &) override { } - CString GetProgramName(int32) override { return CString(); } + CString GetProgramName(int32 program) override; bool HasEditor() const override { return false; } #endif diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h index 16c59dafc..4326a09b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class ParamEq : public IMixPlugin +class ParamEq final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h index a732d1c21..f6ef95518 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class WavesReverb : public IMixPlugin +class WavesReverb final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp index 84593272e..3f60d8ae1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp @@ -26,12 +26,12 @@ namespace Tuning { namespace CTuningS11n { - void ReadStr(std::istream& iStrm, std::string& str, const size_t); - void ReadNoteMap(std::istream& iStrm, std::map& m, const size_t); + void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset); + void ReadNoteMap(std::istream &iStrm, std::map &m, const std::size_t dummy, mpt::Charset charset); void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t); - void WriteNoteMap(std::ostream& oStrm, const std::map& m); - void WriteStr(std::ostream& oStrm, const std::string& str); + void WriteNoteMap(std::ostream &oStrm, const std::map &m); + void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr); struct RatioWriter { @@ -46,6 +46,12 @@ namespace CTuningS11n using namespace CTuningS11n; +/* +Version history: + 4->5: Lots of changes, finestep interpretation revamp, fileformat revamp. + 3->4: Changed sizetypes in serialisation from size_t(uint32) to + smaller types (uint8, USTEPTYPE) (March 2007) +*/ /* Version changes: 3->4: Finetune related internal structure and serialization revamp. @@ -54,85 +60,128 @@ Version changes: */ -MPT_STATIC_ASSERT(CTuningRTI::s_RatioTableFineSizeMaxDefault < static_cast(FINESTEPCOUNT_MAX)); +static_assert(CTuning::s_RatioTableFineSizeMaxDefault < static_cast(FINESTEPCOUNT_MAX)); -CTuningRTI::CTuningRTI() - : m_TuningType(TT_GENERAL) +CTuning::CTuning() + : m_TuningType(Type::GENERAL) , m_FineStepCount(0) { - { - m_RatioTable.clear(); - m_StepMin = s_StepMinDefault; - m_RatioTable.resize(s_RatioTableSizeDefault, 1); - m_GroupSize = 0; - m_GroupRatio = 0; - m_RatioTableFine.clear(); - } + m_RatioTable.clear(); + m_NoteMin = s_NoteMinDefault; + m_RatioTable.resize(s_RatioTableSizeDefault, 1); + m_GroupSize = 0; + m_GroupRatio = 0; + m_RatioTableFine.clear(); } -bool CTuningRTI::ProCreateGroupGeometric(const std::vector& v, const RATIOTYPE& r, const VRPAIR& vr, const NOTEINDEXTYPE& ratiostartpos) +bool CTuning::CreateGroupGeometric(const NOTEINDEXTYPE &s, const RATIOTYPE &r, const NOTEINDEXTYPE &startindex) { - if(v.size() == 0 - || r <= 0 - || vr.second < vr.first - || ratiostartpos < vr.first) + if(s < 1 || !IsValidRatio(r) || startindex < GetNoteRange().first) { - return true; + return false; } + std::vector v; + v.reserve(s); + for(NOTEINDEXTYPE i = startindex; i < startindex + s; i++) + { + v.push_back(GetRatio(i)); + } + return CreateGroupGeometric(v, r, GetNoteRange(), startindex); +} - m_StepMin = vr.first; + +bool CTuning::CreateGroupGeometric(const std::vector &v, const RATIOTYPE &r, const NoteRange &range, const NOTEINDEXTYPE &ratiostartpos) +{ + if(range.first > range.last || v.size() == 0) + { + return false; + } + if(ratiostartpos < range.first || range.last < ratiostartpos || static_cast(range.last - ratiostartpos) < static_cast(v.size() - 1)) + { + return false; + } + if(GetFineStepCount() > FINESTEPCOUNT_MAX) + { + return false; + } + for(size_t i = 0; i < v.size(); i++) + { + if(v[i] < 0) + { + return false; + } + } + if(r <= 0) + { + return false; + } + m_TuningType = Type::GROUPGEOMETRIC; + m_NoteMin = range.first; m_GroupSize = mpt::saturate_cast(v.size()); m_GroupRatio = std::fabs(r); - - m_RatioTable.resize(vr.second-vr.first+1); - std::copy(v.begin(), v.end(), m_RatioTable.begin() + (ratiostartpos - vr.first)); - - for(int32 i = ratiostartpos-1; i>=m_StepMin && ratiostartpos > NOTEINDEXTYPE_MIN; i--) + m_RatioTable.resize(range.last - range.first + 1); + std::copy(v.begin(), v.end(), m_RatioTable.begin() + (ratiostartpos - range.first)); + for(int32 i = ratiostartpos - 1; i >= m_NoteMin && ratiostartpos > NOTEINDEXTYPE_MIN; i--) { - m_RatioTable[i-m_StepMin] = m_RatioTable[i - m_StepMin + m_GroupSize] / m_GroupRatio; + m_RatioTable[i - m_NoteMin] = m_RatioTable[i - m_NoteMin + m_GroupSize] / m_GroupRatio; } - for(int32 i = ratiostartpos+m_GroupSize; i<=vr.second && ratiostartpos <= (NOTEINDEXTYPE_MAX - m_GroupSize); i++) + for(int32 i = ratiostartpos + m_GroupSize; i <= range.last && ratiostartpos <= (NOTEINDEXTYPE_MAX - m_GroupSize); i++) { - m_RatioTable[i-m_StepMin] = m_GroupRatio * m_RatioTable[i - m_StepMin - m_GroupSize]; + m_RatioTable[i - m_NoteMin] = m_GroupRatio * m_RatioTable[i - m_NoteMin - m_GroupSize]; } - - return false; + UpdateFineStepTable(); + return true; } -bool CTuningRTI::ProCreateGeometric(const UNOTEINDEXTYPE& s, const RATIOTYPE& r, const VRPAIR& vr) +bool CTuning::CreateGeometric(const UNOTEINDEXTYPE &p, const RATIOTYPE &r) { - if(vr.second - vr.first + 1 > NOTEINDEXTYPE_MAX) return true; - //Note: Setting finestep is handled by base class when CreateGeometric is called. + return CreateGeometric(p, r, GetNoteRange()); +} + + +bool CTuning::CreateGeometric(const UNOTEINDEXTYPE &s, const RATIOTYPE &r, const NoteRange &range) +{ + if(range.first > range.last) { - m_RatioTable.clear(); - m_StepMin = s_StepMinDefault; - m_RatioTable.resize(s_RatioTableSizeDefault, static_cast(1.0)); - m_GroupSize = 0; - m_GroupRatio = 0; - m_RatioTableFine.clear(); + return false; } - m_StepMin = vr.first; - + if(s < 1 || !IsValidRatio(r)) + { + return false; + } + if(range.last - range.first + 1 > NOTEINDEXTYPE_MAX) + { + return false; + } + m_TuningType = Type::GEOMETRIC; + m_RatioTable.clear(); + m_NoteMin = s_NoteMinDefault; + m_RatioTable.resize(s_RatioTableSizeDefault, static_cast(1.0)); + m_GroupSize = 0; + m_GroupRatio = 0; + m_RatioTableFine.clear(); + m_NoteMin = range.first; m_GroupSize = mpt::saturate_cast(s); m_GroupRatio = std::fabs(r); - const RATIOTYPE stepRatio = std::pow(m_GroupRatio, static_cast(1.0)/ static_cast(m_GroupSize)); - - m_RatioTable.resize(vr.second - vr.first + 1); - for(int32 i = vr.first; i<=vr.second; i++) + const RATIOTYPE stepRatio = std::pow(m_GroupRatio, static_cast(1.0) / static_cast(m_GroupSize)); + m_RatioTable.resize(range.last - range.first + 1); + for(int32 i = range.first; i <= range.last; i++) { - m_RatioTable[i-m_StepMin] = std::pow(stepRatio, static_cast(i)); + m_RatioTable[i - m_NoteMin] = std::pow(stepRatio, static_cast(i)); } - return false; + UpdateFineStepTable(); + return true; } -std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) const + +mpt::ustring CTuning::GetNoteName(const NOTEINDEXTYPE &x, bool addOctave) const { if(!IsValidNote(x)) { - return std::string(); + return mpt::ustring(); } if(GetGroupSize() < 1) { @@ -140,20 +189,20 @@ std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) cons if(i != m_NoteNameMap.end()) return i->second; else - return mpt::fmt::val(x); + return mpt::ufmt::val(x); } else { const NOTEINDEXTYPE pos = static_cast(mpt::wrapping_modulo(x, m_GroupSize)); const NOTEINDEXTYPE middlePeriodNumber = 5; - std::string rValue; + mpt::ustring rValue; const auto nmi = m_NoteNameMap.find(pos); if(nmi != m_NoteNameMap.end()) { rValue = nmi->second; if(addOctave) { - rValue += mpt::fmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); + rValue += mpt::ufmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); } } else @@ -163,19 +212,19 @@ std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) cons //C:5, D:3, R:7 if(m_GroupSize <= 26) { - rValue = std::string(1, static_cast(pos + 'A')); - rValue += ":"; + rValue = mpt::ToUnicode(mpt::Charset::UTF8, std::string(1, static_cast(pos + 'A'))); + rValue += UL_(":"); } else { - rValue = mpt::fmt::HEX0<1>(pos % 16) + mpt::fmt::HEX0<1>((pos / 16) % 16); + rValue = mpt::ufmt::HEX0<1>(pos % 16) + mpt::ufmt::HEX0<1>((pos / 16) % 16); if(pos > 0xff) { - rValue = mpt::ToLowerCaseAscii(rValue); + rValue = mpt::ToUnicode(mpt::Charset::UTF8, mpt::ToLowerCaseAscii(mpt::ToCharset(mpt::Charset::UTF8, rValue))); } } if(addOctave) { - rValue += mpt::fmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); + rValue += mpt::ufmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); } } return rValue; @@ -183,86 +232,76 @@ std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) cons } -const RATIOTYPE CTuningRTI::s_DefaultFallbackRatio = 1.0f; - - -//Without finetune -RATIOTYPE CTuningRTI::GetRatio(const NOTEINDEXTYPE& stepsFromCentre) const +void CTuning::SetNoteName(const NOTEINDEXTYPE &n, const mpt::ustring &str) { - if(stepsFromCentre < m_StepMin) return s_DefaultFallbackRatio; - if(stepsFromCentre >= m_StepMin + static_cast(m_RatioTable.size())) return s_DefaultFallbackRatio; - return m_RatioTable[stepsFromCentre - m_StepMin]; -} - - -//With finetune -RATIOTYPE CTuningRTI::GetRatio(const NOTEINDEXTYPE& baseNote, const STEPINDEXTYPE& baseStepDiff) const -{ - const STEPINDEXTYPE fsCount = static_cast(GetFineStepCount()); - if(fsCount < 0 || fsCount > FINESTEPCOUNT_MAX) + if(!str.empty()) { - return s_DefaultFallbackRatio; - } - if(fsCount == 0 || baseStepDiff == 0) + m_NoteNameMap[n] = str; + } else { - return GetRatio(static_cast(baseNote + baseStepDiff)); - } - - //If baseStepDiff is more than the number of finesteps between notes, - //note is increased. So first figuring out what step and fineStep values to - //actually use. Interpreting finestep -1 on note x so that it is the same as - //finestep GetFineStepCount() on note x-1. - //Note: If finestepcount is n, n+1 steps are needed to get to - //next note. - NOTEINDEXTYPE note; - STEPINDEXTYPE fineStep; - note = static_cast(baseNote + mpt::wrapping_divide(baseStepDiff, (fsCount+1))); - fineStep = mpt::wrapping_modulo(baseStepDiff, (fsCount+1)); - - if(note < m_StepMin) return s_DefaultFallbackRatio; - if(note >= m_StepMin + static_cast(m_RatioTable.size())) return s_DefaultFallbackRatio; - - if(fineStep) return m_RatioTable[note - m_StepMin] * GetRatioFine(note, fineStep); - else return m_RatioTable[note - m_StepMin]; -} - - -RATIOTYPE CTuningRTI::GetRatioFine(const NOTEINDEXTYPE& note, USTEPINDEXTYPE sd) const -{ - if(GetFineStepCount() <= 0 || GetFineStepCount() > static_cast(FINESTEPCOUNT_MAX)) - { - return s_DefaultFallbackRatio; - } - - //Neither of these should happen. - if(sd <= 0) sd = 1; - if(sd > GetFineStepCount()) sd = GetFineStepCount(); - - if(GetType() != TT_GENERAL && m_RatioTableFine.size() > 0) //Taking fineratio from table - { - if(GetType() == TT_GEOMETRIC) + const auto iter = m_NoteNameMap.find(n); + if(iter != m_NoteNameMap.end()) { - return m_RatioTableFine[sd-1]; + m_NoteNameMap.erase(iter); } - if(GetType() == TT_GROUPGEOMETRIC) - return m_RatioTableFine[GetRefNote(note) * GetFineStepCount() + sd - 1]; - - MPT_ASSERT_NOTREACHED(); - return m_RatioTableFine[0]; //Shouldn't happen. } - else //Calculating ratio 'on the fly'. - { - //'Geometric finestepping'. - return std::pow(GetRatio(note+1) / GetRatio(note), static_cast(sd)/(GetFineStepCount()+1)); - - } - } -bool CTuningRTI::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) +// Without finetune +RATIOTYPE CTuning::GetRatio(const NOTEINDEXTYPE note) const { - if(GetType() != TT_GROUPGEOMETRIC && GetType() != TT_GENERAL) + if(!IsValidNote(note)) + { + return s_DefaultFallbackRatio; + } + return m_RatioTable[note - m_NoteMin]; +} + + +// With finetune +RATIOTYPE CTuning::GetRatio(const NOTEINDEXTYPE baseNote, const STEPINDEXTYPE baseFineSteps) const +{ + const STEPINDEXTYPE fineStepCount = static_cast(GetFineStepCount()); + if(fineStepCount == 0 || baseFineSteps == 0) + { + return GetRatio(static_cast(baseNote + baseFineSteps)); + } + + // If baseFineSteps is more than the number of finesteps between notes, note is increased. + // So first figuring out what note and fineStep values to actually use. + // Interpreting finestep==-1 on note x so that it is the same as finestep==fineStepCount on note x-1. + // Note: If fineStepCount is n, n+1 steps are needed to get to next note. + const NOTEINDEXTYPE note = static_cast(baseNote + mpt::wrapping_divide(baseFineSteps, (fineStepCount + 1))); + const STEPINDEXTYPE fineStep = mpt::wrapping_modulo(baseFineSteps, (fineStepCount + 1)); + if(!IsValidNote(note)) + { + return s_DefaultFallbackRatio; + } + if(fineStep == 0) + { + return m_RatioTable[note - m_NoteMin]; + } + + RATIOTYPE fineRatio = static_cast(1.0); + if(GetType() == Type::GEOMETRIC && m_RatioTableFine.size() > 0) + { + fineRatio = m_RatioTableFine[fineStep - 1]; + } else if(GetType() == Type::GROUPGEOMETRIC && m_RatioTableFine.size() > 0) + { + fineRatio = m_RatioTableFine[GetRefNote(note) * fineStepCount + fineStep - 1]; + } else + { + // Geometric finestepping + fineRatio = std::pow(GetRatio(note + 1) / GetRatio(note), static_cast(fineStep) / (fineStepCount + 1)); + } + return m_RatioTable[note - m_NoteMin] * fineRatio; +} + + +bool CTuning::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) +{ + if(GetType() != Type::GROUPGEOMETRIC && GetType() != Type::GENERAL) { return false; } @@ -270,23 +309,23 @@ bool CTuningRTI::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) if(m_RatioTable.empty()) { m_RatioTable.assign(s_RatioTableSizeDefault, 1); - m_StepMin = s_StepMinDefault; + m_NoteMin = s_NoteMinDefault; } - if(!IsNoteInTable(s)) + if(!IsValidNote(s)) { return false; } - m_RatioTable[s - m_StepMin] = std::fabs(r); - if(GetType() == TT_GROUPGEOMETRIC) + m_RatioTable[s - m_NoteMin] = std::fabs(r); + if(GetType() == Type::GROUPGEOMETRIC) { // update other groups - for(NOTEINDEXTYPE n = m_StepMin; n < m_StepMin + static_cast(m_RatioTable.size()); ++n) + for(NOTEINDEXTYPE n = m_NoteMin; n < m_NoteMin + static_cast(m_RatioTable.size()); ++n) { if(n == s) { // nothing - } else if(mpt::abs(n - s) % m_GroupSize == 0) + } else if(std::abs(n - s) % m_GroupSize == 0) { - m_RatioTable[n - m_StepMin] = std::pow(m_GroupRatio, static_cast(n - s) / static_cast(m_GroupSize)) * m_RatioTable[s - m_StepMin]; + m_RatioTable[n - m_NoteMin] = std::pow(m_GroupRatio, static_cast(n - s) / static_cast(m_GroupSize)) * m_RatioTable[s - m_NoteMin]; } } UpdateFineStepTable(); @@ -295,21 +334,21 @@ bool CTuningRTI::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) } -void CTuningRTI::SetFineStepCount(const USTEPINDEXTYPE& fs) +void CTuning::SetFineStepCount(const USTEPINDEXTYPE& fs) { - m_FineStepCount = mpt::clamp(mpt::saturate_cast(fs), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); + m_FineStepCount = std::clamp(mpt::saturate_cast(fs), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); } -void CTuningRTI::UpdateFineStepTable() +void CTuning::UpdateFineStepTable() { if(m_FineStepCount <= 0) { m_RatioTableFine.clear(); return; } - if(GetType() == TT_GEOMETRIC) + if(GetType() == Type::GEOMETRIC) { if(m_FineStepCount > s_RatioTableFineSizeMaxDefault) { @@ -317,13 +356,13 @@ void CTuningRTI::UpdateFineStepTable() return; } m_RatioTableFine.resize(m_FineStepCount); - const RATIOTYPE q = GetRatio(GetValidityRange().first + 1) / GetRatio(GetValidityRange().first); + const RATIOTYPE q = GetRatio(GetNoteRange().first + 1) / GetRatio(GetNoteRange().first); const RATIOTYPE rFineStep = std::pow(q, static_cast(1)/(m_FineStepCount+1)); for(USTEPINDEXTYPE i = 1; i<=m_FineStepCount; i++) m_RatioTableFine[i-1] = std::pow(rFineStep, static_cast(i)); return; } - if(GetType() == TT_GROUPGEOMETRIC) + if(GetType() == Type::GROUPGEOMETRIC) { const UNOTEINDEXTYPE p = GetGroupSize(); if(p > s_RatioTableFineSizeMaxDefault / m_FineStepCount) @@ -337,7 +376,7 @@ void CTuningRTI::UpdateFineStepTable() { //Creating 'geometric' finestepping between notes. m_RatioTableFine.resize(p * m_FineStepCount); - const NOTEINDEXTYPE startnote = GetRefNote(GetValidityRange().first); + const NOTEINDEXTYPE startnote = GetRefNote(GetNoteRange().first); for(UNOTEINDEXTYPE i = 0; i(mpt::wrapping_modulo(note, GetGroupSize())); + if(!IsValidRatio(r)) + { + return false; + } + for(auto & ratio : m_RatioTable) + { + ratio *= r; + } + return true; } -SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) +bool CTuning::ChangeGroupsize(const NOTEINDEXTYPE& s) +{ + if(s < 1) + return false; + + if(m_TuningType == Type::GROUPGEOMETRIC) + return CreateGroupGeometric(s, GetGroupRatio(), 0); + + if(m_TuningType == Type::GEOMETRIC) + return CreateGeometric(s, GetGroupRatio()); + + return false; +} + + +bool CTuning::ChangeGroupRatio(const RATIOTYPE& r) +{ + if(!IsValidRatio(r)) + return false; + + if(m_TuningType == Type::GROUPGEOMETRIC) + return CreateGroupGeometric(GetGroupSize(), r, 0); + + if(m_TuningType == Type::GEOMETRIC) + return CreateGeometric(GetGroupSize(), r); + + return false; +} + + +SerializationResult CTuning::InitDeserialize(std::istream &iStrm, mpt::Charset defaultCharset) { // Note: OpenMPT since at least r323 writes version number (4<<24)+4 while it // reads version number (5<<24)+4 or earlier. @@ -382,29 +458,34 @@ SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) srlztn::SsbRead ssb(iStrm); ssb.BeginRead("CTB244RTI", (5 << 24) + 4); // version - ssb.ReadItem(m_TuningName, "0", ReadStr); + int8 use_utf8 = 0; + ssb.ReadItem(use_utf8, "UTF8"); + const mpt::Charset charset = use_utf8 ? mpt::Charset::UTF8 : defaultCharset; + ssb.ReadItem(m_TuningName, "0", [charset](std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy){ return ReadStr(iStrm, ustr, dummy, charset); }); uint16 dummyEditMask = 0xffff; ssb.ReadItem(dummyEditMask, "1"); - ssb.ReadItem(m_TuningType, "2"); - ssb.ReadItem(m_NoteNameMap, "3", ReadNoteMap); + std::underlying_type::type type = 0; + ssb.ReadItem(type, "2"); + m_TuningType = static_cast(type); + ssb.ReadItem(m_NoteNameMap, "3", [charset](std::istream &iStrm, std::map &m, const std::size_t dummy){ return ReadNoteMap(iStrm, m, dummy, charset); }); ssb.ReadItem(m_FineStepCount, "4"); // RTI entries. ssb.ReadItem(m_RatioTable, "RTI0", ReadRatioTable); - ssb.ReadItem(m_StepMin, "RTI1"); + ssb.ReadItem(m_NoteMin, "RTI1"); ssb.ReadItem(m_GroupSize, "RTI2"); ssb.ReadItem(m_GroupRatio, "RTI3"); UNOTEINDEXTYPE ratiotableSize = 0; ssb.ReadItem(ratiotableSize, "RTI4"); - // If reader status is ok and m_StepMin is somewhat reasonable, process data. - if(!((ssb.GetStatus() & srlztn::SNT_FAILURE) == 0 && m_StepMin >= -300 && m_StepMin <= 300)) + // If reader status is ok and m_NoteMin is somewhat reasonable, process data. + if(!((ssb.GetStatus() & srlztn::SNT_FAILURE) == 0 && m_NoteMin >= -300 && m_NoteMin <= 300)) { return SerializationResult::Failure; } // reject unknown types - if(m_TuningType != TT_GENERAL && m_TuningType != TT_GROUPGEOMETRIC && m_TuningType != TT_GEOMETRIC) + if(m_TuningType != Type::GENERAL && m_TuningType != Type::GROUPGEOMETRIC && m_TuningType != Type::GEOMETRIC) { return SerializationResult::Failure; } @@ -412,26 +493,26 @@ SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) { return SerializationResult::Failure; } - m_FineStepCount = mpt::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); + m_FineStepCount = std::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); if(m_RatioTable.size() > static_cast(NOTEINDEXTYPE_MAX)) { return SerializationResult::Failure; } - if((GetType() == TT_GROUPGEOMETRIC) || (GetType() == TT_GEOMETRIC)) + if((GetType() == Type::GROUPGEOMETRIC) || (GetType() == Type::GEOMETRIC)) { if(ratiotableSize < 1 || ratiotableSize > NOTEINDEXTYPE_MAX) { return SerializationResult::Failure; } - if(GetType() == TT_GEOMETRIC) + if(GetType() == Type::GEOMETRIC) { - if(CreateGeometric(GetGroupSize(), GetGroupRatio(), VRPAIR(m_StepMin, static_cast(m_StepMin + ratiotableSize - 1))) != false) + if(!CreateGeometric(GetGroupSize(), GetGroupRatio(), NoteRange{m_NoteMin, static_cast(m_NoteMin + ratiotableSize - 1)})) { return SerializationResult::Failure; } } else { - if(CreateGroupGeometric(m_RatioTable, GetGroupRatio(), VRPAIR(m_StepMin, static_cast(m_StepMin+ratiotableSize-1)), m_StepMin) != false) + if(!CreateGroupGeometric(m_RatioTable, GetGroupRatio(), NoteRange{m_NoteMin, static_cast(m_NoteMin + ratiotableSize - 1)}, m_NoteMin)) { return SerializationResult::Failure; } @@ -447,13 +528,14 @@ SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) template static bool VectorFromBinaryStream(std::istream& inStrm, std::vector& v, const SIZETYPE maxSize = (std::numeric_limits::max)()) { - if(!inStrm.good()) return true; + if(!inStrm.good()) + return false; SIZETYPE size = 0; mpt::IO::ReadIntLE(inStrm, size); if(size > maxSize) - return true; + return false; v.resize(size); for(std::size_t i = 0; i& v, c mpt::IO::Read(inStrm, tmp); v[i] = tmp; } - if(inStrm.good()) - return false; - else - return true; + + return inStrm.good(); } -SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) +SerializationResult CTuning::InitDeserializeOLD(std::istream &inStrm, mpt::Charset defaultCharset) { if(!inStrm.good()) return SerializationResult::Failure; @@ -511,16 +591,20 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Tuning name if(version2 <= 3) { - if(!mpt::IO::ReadSizedStringLE(inStrm, m_TuningName, 0xffff)) + std::string tmpName; + if(!mpt::IO::ReadSizedStringLE(inStrm, tmpName, 0xffff)) { return SerializationResult::Failure; } + m_TuningName = mpt::ToUnicode(defaultCharset, tmpName); } else { - if(!mpt::IO::ReadSizedStringLE(inStrm, m_TuningName)) + std::string tmpName; + if(!mpt::IO::ReadSizedStringLE(inStrm, tmpName)) { return SerializationResult::Failure; } + m_TuningName = mpt::ToUnicode(defaultCharset, tmpName); } //Const mask @@ -530,7 +614,7 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Tuning type int16 tt = 0; mpt::IO::ReadIntLE(inStrm, tt); - m_TuningType = tt; + m_TuningType = static_cast(tt); //Notemap uint16 size = 0; @@ -565,7 +649,7 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) return SerializationResult::Failure; } } - m_NoteNameMap[n] = str; + m_NoteNameMap[n] = mpt::ToUnicode(defaultCharset, str); } //End marker @@ -578,7 +662,7 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) } // reject unknown types - if(m_TuningType != TT_GENERAL && m_TuningType != TT_GROUPGEOMETRIC && m_TuningType != TT_GEOMETRIC) + if(m_TuningType != Type::GENERAL && m_TuningType != Type::GROUPGEOMETRIC && m_TuningType != Type::GEOMETRIC) { return SerializationResult::Failure; } @@ -586,13 +670,13 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Ratiotable if(version <= 2) { - if(VectorFromBinaryStream(inStrm, m_RatioTable, 0xffff)) + if(!VectorFromBinaryStream(inStrm, m_RatioTable, 0xffff)) { return SerializationResult::Failure; } } else { - if(VectorFromBinaryStream(inStrm, m_RatioTable)) + if(!VectorFromBinaryStream(inStrm, m_RatioTable)) { return SerializationResult::Failure; } @@ -601,24 +685,24 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Fineratios if(version <= 2) { - if(VectorFromBinaryStream(inStrm, m_RatioTableFine, 0xffff)) + if(!VectorFromBinaryStream(inStrm, m_RatioTableFine, 0xffff)) { return SerializationResult::Failure; } } else { - if(VectorFromBinaryStream(inStrm, m_RatioTableFine)) + if(!VectorFromBinaryStream(inStrm, m_RatioTableFine)) { return SerializationResult::Failure; } } m_FineStepCount = mpt::saturate_cast(m_RatioTableFine.size()); - //m_StepMin - int16 stepmin = 0; - mpt::IO::ReadIntLE(inStrm, stepmin); - m_StepMin = stepmin; - if(m_StepMin < -200 || m_StepMin > 200) + // m_NoteMin + int16 notemin = 0; + mpt::IO::ReadIntLE(inStrm, notemin); + m_NoteMin = notemin; + if(m_NoteMin < -200 || m_NoteMin > 200) { return SerializationResult::Failure; } @@ -654,11 +738,11 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) { return SerializationResult::Failure; } - if((m_GroupSize <= 0 || m_GroupRatio <= 0) && m_TuningType != TT_GENERAL) + if((m_GroupSize <= 0 || m_GroupRatio <= 0) && m_TuningType != Type::GENERAL) { return SerializationResult::Failure; } - if(m_TuningType == TT_GROUPGEOMETRIC || m_TuningType == TT_GEOMETRIC) + if(m_TuningType == Type::GROUPGEOMETRIC || m_TuningType == Type::GEOMETRIC) { if(m_RatioTable.size() < static_cast(m_GroupSize)) { @@ -671,21 +755,21 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) { m_FineStepCount -= 1; } - m_FineStepCount = mpt::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); + m_FineStepCount = std::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); - if(m_TuningType == TT_GEOMETRIC) + if(m_TuningType == Type::GEOMETRIC) { // Convert old geometric to new groupgeometric because old geometric tunings // can have ratio(0) != 1.0, which would get lost when saving nowadays. - if(mpt::saturate_cast(m_RatioTable.size()) >= m_GroupSize - m_StepMin) + if(mpt::saturate_cast(m_RatioTable.size()) >= m_GroupSize - m_NoteMin) { std::vector ratios; for(NOTEINDEXTYPE n = 0; n < m_GroupSize; ++n) { - ratios.push_back(m_RatioTable[n - m_StepMin]); + ratios.push_back(m_RatioTable[n - m_NoteMin]); } - CreateGroupGeometric(ratios, m_GroupRatio, GetValidityRange(), 0); + CreateGroupGeometric(ratios, m_GroupRatio, GetNoteRange(), 0); } } @@ -693,41 +777,42 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) } -Tuning::SerializationResult CTuningRTI::Serialize(std::ostream& outStrm) const +Tuning::SerializationResult CTuning::Serialize(std::ostream& outStrm) const { // Note: OpenMPT since at least r323 writes version number (4<<24)+4 while it // reads version number (5<<24)+4. // We keep this behaviour. srlztn::SsbWrite ssb(outStrm); ssb.BeginWrite("CTB244RTI", (4 << 24) + 4); // version + ssb.WriteItem(int8(1), "UTF8"); if (m_TuningName.length() > 0) ssb.WriteItem(m_TuningName, "0", WriteStr); uint16 dummyEditMask = 0xffff; ssb.WriteItem(dummyEditMask, "1"); - ssb.WriteItem(m_TuningType, "2"); + ssb.WriteItem(static_cast::type>(m_TuningType), "2"); if (m_NoteNameMap.size() > 0) ssb.WriteItem(m_NoteNameMap, "3", WriteNoteMap); if (GetFineStepCount() > 0) ssb.WriteItem(m_FineStepCount, "4"); - const TUNINGTYPE tt = GetType(); + const Tuning::Type tt = GetType(); if (GetGroupRatio() > 0) ssb.WriteItem(m_GroupRatio, "RTI3"); - if (tt == TT_GROUPGEOMETRIC) + if (tt == Type::GROUPGEOMETRIC) ssb.WriteItem(m_RatioTable, "RTI0", RatioWriter(GetGroupSize())); - if (tt == TT_GENERAL) + if (tt == Type::GENERAL) ssb.WriteItem(m_RatioTable, "RTI0", RatioWriter()); - if (tt == TT_GEOMETRIC) + if (tt == Type::GEOMETRIC) ssb.WriteItem(m_GroupSize, "RTI2"); - if(tt == TT_GEOMETRIC || tt == TT_GROUPGEOMETRIC) + if(tt == Type::GEOMETRIC || tt == Type::GROUPGEOMETRIC) { //For Groupgeometric this data is the number of ratios in ratiotable. UNOTEINDEXTYPE ratiotableSize = static_cast(m_RatioTable.size()); ssb.WriteItem(ratiotableSize, "RTI4"); } - //m_StepMin - ssb.WriteItem(m_StepMin, "RTI1"); + // m_NoteMin + ssb.WriteItem(m_NoteMin, "RTI1"); ssb.FinishWrite(); @@ -737,15 +822,15 @@ Tuning::SerializationResult CTuningRTI::Serialize(std::ostream& outStrm) const #ifdef MODPLUG_TRACKER -bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) const +bool CTuning::WriteSCL(std::ostream &f, const mpt::PathString &filename) const { - mpt::IO::WriteTextCRLF(f, mpt::format("! %1")(mpt::ToCharset(mpt::CharsetISO8859_1, (filename.GetFileName() + filename.GetFileExt()).ToUnicode()))); + mpt::IO::WriteTextCRLF(f, mpt::format("! %1")(mpt::ToCharset(mpt::Charset::ISO8859_1, (filename.GetFileName() + filename.GetFileExt()).ToUnicode()))); mpt::IO::WriteTextCRLF(f, "!"); - std::string name = mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetName()); + std::string name = mpt::ToCharset(mpt::Charset::ISO8859_1, GetName()); for(auto & c : name) { if(static_cast(c) < 32) c = ' '; } // remove control characters if(name.length() >= 1 && name[0] == '!') name[0] = '?'; // do not confuse description with comment mpt::IO::WriteTextCRLF(f, name); - if(GetType() == TT_GEOMETRIC) + if(GetType() == Type::GEOMETRIC) { mpt::IO::WriteTextCRLF(f, mpt::format(" %1")(m_GroupSize)); mpt::IO::WriteTextCRLF(f, "!"); @@ -755,10 +840,10 @@ bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) cons double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( mpt::fmt::fix(cents), - mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetNoteName((n + 1) % m_GroupSize, false)) + mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName((n + 1) % m_GroupSize, false)) )); } - } else if(GetType() == TT_GROUPGEOMETRIC) + } else if(GetType() == Type::GROUPGEOMETRIC) { mpt::IO::WriteTextCRLF(f, mpt::format(" %1")(m_GroupSize)); mpt::IO::WriteTextCRLF(f, "!"); @@ -770,10 +855,10 @@ bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) cons double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( mpt::fmt::fix(cents), - mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetNoteName((n + 1) % m_GroupSize, false)) + mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName((n + 1) % m_GroupSize, false)) )); } - } else if(GetType() == TT_GENERAL) + } else if(GetType() == Type::GENERAL) { mpt::IO::WriteTextCRLF(f, mpt::format(" %1")(m_RatioTable.size() + 1)); mpt::IO::WriteTextCRLF(f, "!"); @@ -788,7 +873,7 @@ bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) cons double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( mpt::fmt::fix(cents), - mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetNoteName(n + m_StepMin, false)) + mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName(n + m_NoteMin, false)) )); } mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( @@ -810,15 +895,16 @@ namespace CTuningS11n void RatioWriter::operator()(std::ostream& oStrm, const std::vector& v) { - const size_t nWriteCount = MIN(v.size(), m_nWriteCount); + const std::size_t nWriteCount = std::min(v.size(), static_cast(m_nWriteCount)); mpt::IO::WriteAdaptiveInt64LE(oStrm, nWriteCount); for(size_t i = 0; i < nWriteCount; i++) mpt::IO::Write(oStrm, IEEE754binary32LE(v[i])); } -void ReadNoteMap(std::istream& iStrm, std::map& m, const size_t) +void ReadNoteMap(std::istream &iStrm, std::map &m, const std::size_t dummy, mpt::Charset charset) { + MPT_UNREFERENCED_PARAMETER(dummy); uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); LimitMax(val, 256u); // Read 256 at max. @@ -828,7 +914,7 @@ void ReadNoteMap(std::istream& iStrm, std::map& m, co mpt::IO::ReadIntLE(iStrm, key); std::string str; mpt::IO::ReadSizedStringLE(iStrm, str); - m[key] = str; + m[key] = mpt::ToUnicode(charset, str); } } @@ -837,7 +923,7 @@ void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t { uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); - v.resize( static_cast(MIN(val, 256u))); // Read 256 vals at max. + v.resize(std::min(mpt::saturate_cast(val), std::size_t(256))); // Read 256 vals at max. for(size_t i = 0; i < v.size(); i++) { IEEE754binary32LE tmp(0.0f); @@ -847,8 +933,10 @@ void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t } -void ReadStr(std::istream& iStrm, std::string& str, const size_t) +void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset) { + MPT_UNREFERENCED_PARAMETER(dummy); + std::string str; uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); size_t nSize = (val > 255) ? 255 : static_cast(val); // Read 255 characters at max. @@ -860,22 +948,24 @@ void ReadStr(std::istream& iStrm, std::string& str, const size_t) { // trim \0 at the end str.resize(str.find_first_of('\0')); } + ustr = mpt::ToUnicode(charset, str); } -void WriteNoteMap(std::ostream& oStrm, const std::map& m) +void WriteNoteMap(std::ostream &oStrm, const std::map &m) { mpt::IO::WriteAdaptiveInt64LE(oStrm, m.size()); for(auto &mi : m) { mpt::IO::WriteIntLE(oStrm, mi.first); - mpt::IO::WriteSizedStringLE(oStrm, mi.second); + mpt::IO::WriteSizedStringLE(oStrm, mpt::ToCharset(mpt::Charset::UTF8, mi.second)); } } -void WriteStr(std::ostream& oStrm, const std::string& str) +void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr) { + std::string str = mpt::ToCharset(mpt::Charset::UTF8, ustr); mpt::IO::WriteAdaptiveInt64LE(oStrm, str.size()); oStrm.write(str.c_str(), str.size()); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h index 828f2e39f..1692d3e6b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h @@ -23,51 +23,52 @@ OPENMPT_NAMESPACE_BEGIN namespace Tuning { -class CTuningRTI +class CTuning { public: - static const char s_FileExtension[5]; + static constexpr char s_FileExtension[5] = ".tun"; - enum - { - TT_GENERAL = 0, - TT_GROUPGEOMETRIC = 1, - TT_GEOMETRIC = 3, - }; - - static const RATIOTYPE s_DefaultFallbackRatio; - enum : NOTEINDEXTYPE { s_StepMinDefault = -64 }; - enum : UNOTEINDEXTYPE { s_RatioTableSizeDefault = 128 }; - enum : USTEPINDEXTYPE { s_RatioTableFineSizeMaxDefault = 1000 }; + static constexpr RATIOTYPE s_DefaultFallbackRatio = 1.0f; + static constexpr NOTEINDEXTYPE s_NoteMinDefault = -64; + static constexpr UNOTEINDEXTYPE s_RatioTableSizeDefault = 128; + static constexpr USTEPINDEXTYPE s_RatioTableFineSizeMaxDefault = 1000; public: - //To return ratio of certain note. - RATIOTYPE GetRatio(const NOTEINDEXTYPE& stepsFromCentre) const; + // To return ratio of certain note. + RATIOTYPE GetRatio(const NOTEINDEXTYPE note) const; - //To return ratio from a 'step'(noteindex + stepindex) - RATIOTYPE GetRatio(const NOTEINDEXTYPE& stepsFromCentre, const STEPINDEXTYPE& fineSteps) const; - - UNOTEINDEXTYPE GetRatioTableSize() const {return static_cast(m_RatioTable.size());} - - NOTEINDEXTYPE GetRatioTableBeginNote() const {return m_StepMin;} + // To return ratio from a 'step'(noteindex + stepindex) + RATIOTYPE GetRatio(const NOTEINDEXTYPE baseNote, const STEPINDEXTYPE baseFineSteps) const; //Tuning might not be valid for arbitrarily large range, //so this can be used to ask where it is valid. Tells the lowest and highest //note that are valid. - VRPAIR GetValidityRange() const {return VRPAIR(m_StepMin, static_cast(m_StepMin + static_cast(m_RatioTable.size()) - 1));} + MPT_FORCEINLINE NoteRange GetNoteRange() const + { + return NoteRange{m_NoteMin, static_cast(m_NoteMin + static_cast(m_RatioTable.size()) - 1)}; + } - //Return true if note is within validity range - false otherwise. - bool IsValidNote(const NOTEINDEXTYPE n) const {return (n >= GetValidityRange().first && n <= GetValidityRange().second);} + // Return true if note is within note range + MPT_FORCEINLINE bool IsValidNote(const NOTEINDEXTYPE n) const + { + return (GetNoteRange().first <= n && n <= GetNoteRange().last); + } - UNOTEINDEXTYPE GetGroupSize() const {return m_GroupSize;} + MPT_FORCEINLINE UNOTEINDEXTYPE GetGroupSize() const + { + return m_GroupSize; + } RATIOTYPE GetGroupRatio() const {return m_GroupRatio;} - //To return (fine)stepcount between two consecutive mainsteps. - USTEPINDEXTYPE GetFineStepCount() const {return m_FineStepCount;} + // To return (fine)stepcount between two consecutive mainsteps. + MPT_FORCEINLINE USTEPINDEXTYPE GetFineStepCount() const + { + return m_FineStepCount; + } //To return 'directed distance' between given notes. STEPINDEXTYPE GetStepDistance(const NOTEINDEXTYPE& from, const NOTEINDEXTYPE& to) const @@ -82,83 +83,81 @@ public: //stepdistances become the same as note distances. void SetFineStepCount(const USTEPINDEXTYPE& fs); - //Multiply all ratios by given number. - bool Multiply(const RATIOTYPE&); + // Multiply all ratios by given number. + bool Multiply(const RATIOTYPE r); bool SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r); - TUNINGTYPE GetType() const {return m_TuningType;} - - std::string GetNoteName(const NOTEINDEXTYPE& x, bool addOctave = true) const; - - void SetNoteName(const NOTEINDEXTYPE&, const std::string&); - - static CTuningRTI* CreateDeserialize(std::istream & f) + MPT_FORCEINLINE Tuning::Type GetType() const { - CTuningRTI *pT = new CTuningRTI(); - if(pT->InitDeserialize(f) != SerializationResult::Success) + return m_TuningType; + } + + mpt::ustring GetNoteName(const NOTEINDEXTYPE &x, bool addOctave = true) const; + + void SetNoteName(const NOTEINDEXTYPE &, const mpt::ustring &); + + static std::unique_ptr CreateDeserialize(std::istream &f, mpt::Charset defaultCharset) + { + std::unique_ptr pT = std::unique_ptr(new CTuning()); + if(pT->InitDeserialize(f, defaultCharset) != SerializationResult::Success) { - delete pT; return nullptr; } return pT; } //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. - static CTuningRTI* CreateDeserializeOLD(std::istream & f) + static std::unique_ptr CreateDeserializeOLD(std::istream &f, mpt::Charset defaultCharset) { - CTuningRTI *pT = new CTuningRTI(); - if(pT->InitDeserializeOLD(f) != SerializationResult::Success) + std::unique_ptr pT = std::unique_ptr(new CTuning()); + if(pT->InitDeserializeOLD(f, defaultCharset) != SerializationResult::Success) { - delete pT; return nullptr; } return pT; } - static CTuningRTI* CreateGeneral(const std::string &name) + static std::unique_ptr CreateGeneral(const mpt::ustring &name) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); return pT; } - static CTuningRTI* CreateGroupGeometric(const std::string &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) + static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); - if(pT->CreateGroupGeometric(groupsize, groupratio, 0) != false) + if(!pT->CreateGroupGeometric(groupsize, groupratio, 0)) { - delete pT; return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } - static CTuningRTI* CreateGroupGeometric(const std::string &name, const std::vector &ratios, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) + static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, const std::vector &ratios, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); - VRPAIR range = std::make_pair(s_StepMinDefault, static_cast(static_cast(s_StepMinDefault) + static_cast(s_RatioTableSizeDefault) - 1)); - range.second = std::max(range.second, mpt::saturate_cast(ratios.size() - 1)); - range.first = 0 - range.second - 1; - if(pT->CreateGroupGeometric(ratios, groupratio, range, 0) != false) + NoteRange range = NoteRange{s_NoteMinDefault, static_cast(s_NoteMinDefault + s_RatioTableSizeDefault - 1)}; + range.last = std::max(range.last, mpt::saturate_cast(ratios.size() - 1)); + range.first = 0 - range.last - 1; + if(!pT->CreateGroupGeometric(ratios, groupratio, range, 0)) { - delete pT; return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } - static CTuningRTI* CreateGeometric(const std::string &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) + static std::unique_ptr CreateGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); - if(pT->CreateGeometric(groupsize, groupratio) != false) + if(!pT->CreateGeometric(groupsize, groupratio)) { - delete pT; return nullptr; } pT->SetFineStepCount(finestepcount); @@ -174,54 +173,55 @@ public: bool ChangeGroupsize(const NOTEINDEXTYPE&); bool ChangeGroupRatio(const RATIOTYPE&); - void SetName(const std::string& s) { m_TuningName = s; } - std::string GetName() const {return m_TuningName;} - -private: - - CTuningRTI(); - - SerializationResult InitDeserialize(std::istream& inStrm); - - //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. - SerializationResult InitDeserializeOLD(std::istream&); - - //Create GroupGeometric tuning of *this using virtual ProCreateGroupGeometric. - bool CreateGroupGeometric(const std::vector&, const RATIOTYPE&, const VRPAIR vr, const NOTEINDEXTYPE ratiostartpos); - - //Create GroupGeometric of *this using ratios from 'itself' and ratios starting from - //position given as third argument. - bool CreateGroupGeometric(const NOTEINDEXTYPE&, const RATIOTYPE&, const NOTEINDEXTYPE&); - - //Create geometric tuning of *this using ratio(0) = 1. - bool CreateGeometric(const UNOTEINDEXTYPE& p, const RATIOTYPE& r) {return CreateGeometric(p,r,GetValidityRange());} - bool CreateGeometric(const UNOTEINDEXTYPE&, const RATIOTYPE&, const VRPAIR vr); - - //The two methods below return false if action was done, true otherwise. - bool ProCreateGroupGeometric(const std::vector&, const RATIOTYPE&, const VRPAIR&, const NOTEINDEXTYPE& ratiostartpos); - bool ProCreateGeometric(const UNOTEINDEXTYPE&, const RATIOTYPE&, const VRPAIR&); - - void UpdateFineStepTable(); - - //Note: Stepdiff should be in range [1, finestepcount] - RATIOTYPE GetRatioFine(const NOTEINDEXTYPE& note, USTEPINDEXTYPE stepDiff) const; - - //GroupPeriodic-specific. - //Get the corresponding note in [0, period-1]. - //For example GetRefNote(-1) is to return note :'groupsize-1'. - NOTEINDEXTYPE GetRefNote(NOTEINDEXTYPE note) const; - - bool IsNoteInTable(const NOTEINDEXTYPE& s) const + void SetName(const mpt::ustring &s) { - if(s < m_StepMin || s >= m_StepMin + static_cast(m_RatioTable.size())) - return false; - else - return true; + m_TuningName = s; + } + + mpt::ustring GetName() const + { + return m_TuningName; } private: - TUNINGTYPE m_TuningType; + CTuning(); + + SerializationResult InitDeserialize(std::istream &inStrm, mpt::Charset defaultCharset); + + //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. + SerializationResult InitDeserializeOLD(std::istream &inStrm, mpt::Charset defaultCharset); + + //Create GroupGeometric tuning of *this using virtual ProCreateGroupGeometric. + bool CreateGroupGeometric(const std::vector &v, const RATIOTYPE &r, const NoteRange &range, const NOTEINDEXTYPE &ratiostartpos); + + //Create GroupGeometric of *this using ratios from 'itself' and ratios starting from + //position given as third argument. + bool CreateGroupGeometric(const NOTEINDEXTYPE &s, const RATIOTYPE &r, const NOTEINDEXTYPE &startindex); + + //Create geometric tuning of *this using ratio(0) = 1. + bool CreateGeometric(const UNOTEINDEXTYPE &p, const RATIOTYPE &r); + bool CreateGeometric(const UNOTEINDEXTYPE &s, const RATIOTYPE &r, const NoteRange &range); + + void UpdateFineStepTable(); + + // GroupPeriodic-specific. + // Get the corresponding note in [0, period-1]. + // For example GetRefNote(-1) is to return note :'groupsize-1'. + MPT_FORCEINLINE NOTEINDEXTYPE GetRefNote(NOTEINDEXTYPE note) const + { + MPT_ASSERT(GetType() == Type::GROUPGEOMETRIC || GetType() == Type::GEOMETRIC); + return static_cast(mpt::wrapping_modulo(note, GetGroupSize())); + } + + static bool IsValidRatio(RATIOTYPE ratio) + { + return (ratio > static_cast(0.0)); + } + +private: + + Tuning::Type m_TuningType; //Noteratios std::vector m_RatioTable; @@ -229,24 +229,21 @@ private: //'Fineratios' std::vector m_RatioTableFine; - //The lowest index of note in the table - NOTEINDEXTYPE m_StepMin; // this should REALLY be called 'm_NoteMin' renaming was missed in r192 + // The lowest index of note in the table + NOTEINDEXTYPE m_NoteMin; //For groupgeometric tunings, tells the 'group size' and 'group ratio' //m_GroupSize should always be >= 0. NOTEINDEXTYPE m_GroupSize; RATIOTYPE m_GroupRatio; - USTEPINDEXTYPE m_FineStepCount; + USTEPINDEXTYPE m_FineStepCount; // invariant: 0 <= m_FineStepCount <= FINESTEPCOUNT_MAX - std::string m_TuningName; + mpt::ustring m_TuningName; - std::map m_NoteNameMap; + std::map m_NoteNameMap; -}; // class CTuningRTI - - -typedef CTuningRTI CTuning; +}; // class CTuning } // namespace Tuning diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp index c8027059b..6fa7eb4bb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp @@ -34,21 +34,19 @@ Version history: */ -const char CTuningCollection::s_FileExtension[4] = ".tc"; - - namespace CTuningS11n { - void ReadStr(std::istream& iStrm, std::string& str, const size_t); - void WriteStr(std::ostream& oStrm, const std::string& str); + void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset); + void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr); } // namespace CTuningS11n using namespace CTuningS11n; -static void ReadTuning(std::istream& iStrm, CTuningCollection& Tc, const size_t) +static void ReadTuning(std::istream &iStrm, CTuningCollection &Tc, const std::size_t dummy, mpt::Charset defaultCharset) { - Tc.AddTuning(iStrm); + MPT_UNREFERENCED_PARAMETER(dummy); + Tc.AddTuning(iStrm, defaultCharset); } static void WriteTuning(std::ostream& oStrm, const CTuning& t) @@ -57,7 +55,7 @@ static void WriteTuning(std::ostream& oStrm, const CTuning& t) } -CTuning* CTuningCollection::GetTuning(const std::string& name) +CTuning* CTuningCollection::GetTuning(const mpt::ustring &name) { for(std::size_t i = 0; i(inStrm, name, 256)) return Tuning::SerializationResult::Failure; + uname = mpt::ToUnicode(defaultCharset, name); } else { + std::string name; if(!mpt::IO::ReadSizedStringLE(inStrm, name)) return Tuning::SerializationResult::Failure; + uname = mpt::ToUnicode(defaultCharset, name); } //4. Editmask @@ -181,8 +187,10 @@ Tuning::SerializationResult CTuningCollection::DeserializeOLD(std::istream& inSt return Tuning::SerializationResult::Failure; for(size_t i = 0; i pT) { if(m_Tunings.size() >= s_nMaxTuningCount) - return true; - - if(pT == NULL) - return true; - - m_Tunings.push_back(std::unique_ptr(pT)); - - return false; + { + return nullptr; + } + if(!pT) + { + return nullptr; + } + CTuning *result = pT.get(); + m_Tunings.push_back(std::move(pT)); + return result; } -bool CTuningCollection::AddTuning(std::istream& inStrm) +CTuning* CTuningCollection::AddTuning(std::istream &inStrm, mpt::Charset defaultCharset) { if(m_Tunings.size() >= s_nMaxTuningCount) - return true; - - if(!inStrm.good()) return true; - - CTuning* pT = CTuning::CreateDeserializeOLD(inStrm); - if(pT == 0) pT = CTuning::CreateDeserialize(inStrm); - - if(pT == 0) - return true; - else { - m_Tunings.push_back(std::unique_ptr(pT)); - return false; + return nullptr; } + if(!inStrm.good()) + { + return nullptr; + } + std::unique_ptr pT = CTuning::CreateDeserializeOLD(inStrm, defaultCharset); + if(!pT) + { + pT = CTuning::CreateDeserialize(inStrm, defaultCharset); + } + if(!pT) + { + return nullptr; + } + CTuning *result = pT.get(); + m_Tunings.push_back(std::move(pT)); + return result; } @@ -271,7 +286,7 @@ bool UnpackTuningCollection(const CTuningCollection &tc, const mpt::PathString & const CTuning & tuning = tc.GetTuning(i); mpt::PathString fn; fn += prefix; - mpt::ustring tuningName = mpt::ToUnicode(mpt::CharsetLocale, tuning.GetName()); + mpt::ustring tuningName = tuning.GetName(); if(tuningName.empty()) { tuningName = U_("untitled"); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp deleted file mode 100644 index 58428acf1..000000000 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * tuningbase.cpp - * -------------- - * Purpose: Alternative sample tuning. - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#include "stdafx.h" -#include "tuningbase.h" -#include "tuning.h" -#include "../common/mptIO.h" -#include "../common/serialization_utils.h" - -#include - -#include -#include - - -OPENMPT_NAMESPACE_BEGIN - - -namespace Tuning { - - -/* -Version history: - 4->5: Lots of changes, finestep interpretation revamp, fileformat revamp. - 3->4: Changed sizetypes in serialisation from size_t(uint32) to - smaller types (uint8, USTEPTYPE) (March 2007) -*/ - - - -const char CTuningRTI::s_FileExtension[5] = ".tun"; - - - -void CTuningRTI::SetNoteName(const NOTEINDEXTYPE& n, const std::string& str) -{ - if(!str.empty()) - { - m_NoteNameMap[n] = str; - } else - { - const auto iter = m_NoteNameMap.find(n); - if(iter != m_NoteNameMap.end()) - { - m_NoteNameMap.erase(iter); - } - } -} - - - -bool CTuningRTI::Multiply(const RATIOTYPE& r) -{ - if(r <= 0) - return true; - - //Note: Multiplying ratios by constant doesn't - //change, e.g. 'geometricness' status. - for(auto & ratio : m_RatioTable) - { - ratio *= r; - } - return false; -} - - -bool CTuningRTI::CreateGroupGeometric(const NOTEINDEXTYPE& s, const RATIOTYPE& r, const NOTEINDEXTYPE& startindex) -{ - if(s < 1 || r <= 0 || startindex < GetValidityRange().first) - return true; - - std::vector v; - v.reserve(s); - for(NOTEINDEXTYPE i = startindex; i& v, const RATIOTYPE& r, const VRPAIR vr, const NOTEINDEXTYPE ratiostartpos) -{ - { - if(vr.first > vr.second || v.size() == 0) return true; - if(ratiostartpos < vr.first || vr.second < ratiostartpos || static_cast(vr.second - ratiostartpos) < static_cast(v.size() - 1)) return true; - if(GetFineStepCount() > FINESTEPCOUNT_MAX) return true; - for(size_t i = 0; i vr.second) return true; - if(s < 1 || r <= 0) return true; - if(ProCreateGeometric(s,r,vr)) - return true; - else - { - m_TuningType = TT_GEOMETRIC; - UpdateFineStepTable(); - return false; - } - } -} - - - - -bool CTuningRTI::ChangeGroupsize(const NOTEINDEXTYPE& s) -{ - if(s < 1) - return true; - - if(m_TuningType == TT_GROUPGEOMETRIC) - return CreateGroupGeometric(s, GetGroupRatio(), 0); - - if(m_TuningType == TT_GEOMETRIC) - return CreateGeometric(s, GetGroupRatio()); - - return true; -} - - - -bool CTuningRTI::ChangeGroupRatio(const RATIOTYPE& r) -{ - if(r <= 0) - return true; - - if(m_TuningType == TT_GROUPGEOMETRIC) - return CreateGroupGeometric(GetGroupSize(), r, 0); - - if(m_TuningType == TT_GEOMETRIC) - return CreateGeometric(GetGroupSize(), r); - - return true; -} - - -} // namespace Tuning - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h index 14654566c..c245fcdad 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h @@ -29,40 +29,46 @@ enum class SerializationResult : int { }; - //NOTEINDEXTYPE: Some signed integer-type. - //UNOTEINDEXTYPE: Unsigned NOTEINDEXTYPE - //RATIOTYPE: Some 'real figure' type able to present ratios. - //STEPINDEXTYPE: Counter of steps between notes. If there is no 'finetune'(finestepcount == 0), - //then 'step difference' between notes is the - //same as differences in NOTEINDEXTYPE. In a way similar to ticks and rows in pattern - - //ticks <-> STEPINDEX, rows <-> NOTEINDEX +using NOTEINDEXTYPE = int16; // Some signed integer-type. +using UNOTEINDEXTYPE = uint16; // Unsigned NOTEINDEXTYPE. - typedef int16 NOTEINDEXTYPE; - typedef uint16 UNOTEINDEXTYPE; - typedef float32 RATIOTYPE; //If changing RATIOTYPE, serialization methods may need modifications. - typedef int32 STEPINDEXTYPE; - typedef uint32 USTEPINDEXTYPE; +using RATIOTYPE = float32; // Some 'real figure' type able to present ratios. If changing RATIOTYPE, serialization methods may need modifications. - typedef std::pair VRPAIR; +// Counter of steps between notes. If there is no 'finetune'(finestepcount == 0), +// then 'step difference' between notes is the +// same as differences in NOTEINDEXTYPE. In a way similar to ticks and rows in pattern - +// ticks <-> STEPINDEX, rows <-> NOTEINDEX +using STEPINDEXTYPE = int32; +using USTEPINDEXTYPE = uint32; - typedef uint16 TUNINGTYPE; +struct NoteRange +{ + NOTEINDEXTYPE first; + NOTEINDEXTYPE last; +}; // Derived from old IsStepCountRangeSufficient(), this is actually a more // sensible value than what was calculated in earlier versions. -static MPT_CONSTEXPR11_VAR STEPINDEXTYPE FINESTEPCOUNT_MAX = 0xffff; +static constexpr STEPINDEXTYPE FINESTEPCOUNT_MAX = 0xffff; + +static constexpr auto NOTEINDEXTYPE_MIN = std::numeric_limits::min(); +static constexpr auto NOTEINDEXTYPE_MAX = std::numeric_limits::max(); +static constexpr auto UNOTEINDEXTYPE_MAX = std::numeric_limits::max(); +static constexpr auto STEPINDEXTYPE_MIN = std::numeric_limits::min(); +static constexpr auto STEPINDEXTYPE_MAX = std::numeric_limits::max(); +static constexpr auto USTEPINDEXTYPE_MAX = std::numeric_limits::max(); -#define NOTEINDEXTYPE_MIN (std::numeric_limits::min)() -#define NOTEINDEXTYPE_MAX (std::numeric_limits::max)() -#define UNOTEINDEXTYPE_MAX (std::numeric_limits::max)() -#define STEPINDEXTYPE_MIN (std::numeric_limits::min)() -#define STEPINDEXTYPE_MAX (std::numeric_limits::max)() -#define USTEPINDEXTYPE_MAX (std::numeric_limits::max)() +enum class Type : uint16 +{ + GENERAL = 0, + GROUPGEOMETRIC = 1, + GEOMETRIC = 3, +}; -class CTuningRTI; -typedef CTuningRTI CTuning; +class CTuning; } // namespace Tuning diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h index 7e388f4fa..91b81980b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h @@ -28,7 +28,7 @@ class CTuningCollection public: - static const char s_FileExtension[4]; + static constexpr char s_FileExtension[4] = ".tc"; // OpenMPT <= 1.26 had to following limits: // * 255 built-in tunings (only 2 were ever actually provided) @@ -44,23 +44,29 @@ public: public: - //Note: Given pointer is deleted by CTuningCollection - //at some point. - bool AddTuning(CTuning *pT); - bool AddTuning(std::istream& inStrm); + // returns observer ptr if successfull + CTuning* AddTuning(std::unique_ptr pT); + CTuning* AddTuning(std::istream &inStrm, mpt::Charset defaultCharset); bool Remove(const std::size_t i); bool Remove(const CTuning *pT); CTuning& GetTuning(size_t i) {return *m_Tunings.at(i).get();} const CTuning& GetTuning(size_t i) const {return *m_Tunings.at(i).get();} - CTuning* GetTuning(const std::string& name); - const CTuning* GetTuning(const std::string& name) const; + CTuning* GetTuning(const mpt::ustring &name); + const CTuning* GetTuning(const mpt::ustring &name) const; size_t GetNumTunings() const {return m_Tunings.size();} - Tuning::SerializationResult Serialize(std::ostream&, const std::string &name) const; - Tuning::SerializationResult Deserialize(std::istream&, std::string &name); + Tuning::SerializationResult Serialize(std::ostream &oStrm, const mpt::ustring &name) const; + Tuning::SerializationResult Deserialize(std::istream &iStrm, mpt::ustring &name, mpt::Charset defaultCharset); + + auto begin() { return m_Tunings.begin(); } + auto begin() const { return m_Tunings.begin(); } + auto cbegin() { return m_Tunings.cbegin(); } + auto end() { return m_Tunings.end(); } + auto end() const { return m_Tunings.end(); } + auto cend() { return m_Tunings.cend(); } private: @@ -68,7 +74,7 @@ private: private: - Tuning::SerializationResult DeserializeOLD(std::istream&, std::string &name); + Tuning::SerializationResult DeserializeOLD(std::istream &inStrm, mpt::ustring &uname, mpt::Charset defaultCharset); }; diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp index 9a3caac11..98b7555c5 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp @@ -197,12 +197,12 @@ MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *exp if(msg) { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_(")") + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::Charset::ASCII, expr) + U_(")") ); } else { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h index 8202fceea..1c49ca33b 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h @@ -190,7 +190,7 @@ private: template inline bool IsEqualEpsilon(const Tx &x, const Ty &y, const Teps &eps) { - return mpt::abs(x - y) <= eps; + return std::abs(x - y) <= eps; } public: diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h b/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h index 978efa508..493dacefb 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h @@ -50,7 +50,7 @@ namespace Test { #define VERIFY_EQUAL_EPS(x,y,eps) \ MPT_DO { \ - if(mpt::abs((x) - (y)) > (eps)) { \ + if(std::abs((x) - (y)) > (eps)) { \ MyDebugBreak(); \ } \ } MPT_WHILE_0 \ diff --git a/Frameworks/OpenMPT/OpenMPT/test/test.cpp b/Frameworks/OpenMPT/OpenMPT/test/test.cpp index 39b2c2da3..2c331256f 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/test.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/test.cpp @@ -32,7 +32,7 @@ #include "../soundlib/ITCompression.h" #include "../soundlib/tuningcollection.h" #include "../soundlib/tuning.h" -#include "../soundlib/Dither.h" +#include "../soundbase/Dither.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Mptrack.h" #include "../mptrack/Moddoc.h" @@ -48,7 +48,7 @@ #ifndef NO_PLUGINS #include "../soundlib/plugins/PlugInterface.h" #endif -#include "../common/mptBufferIO.h" +#include #include #ifdef LIBOPENMPT_BUILD #include @@ -75,7 +75,7 @@ #define MPT_TEST_HAS_FILESYSTEM 0 #endif -#if MPT_COMPILER_MSVC && defined(_MFC_VER) && defined(_DEBUG) +#if MPT_COMPILER_MSVC && defined(MPT_WITH_MFC) && defined(_DEBUG) #define new DEBUG_NEW #endif @@ -140,12 +140,12 @@ void DoTests() 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; + std::cout << "Version.: " << mpt::ToCharset(mpt::Charset::ASCII, Build::GetVersionString(Build::StringVersion | Build::StringRevision | Build::StringBitness | Build::StringSourceInfo | Build::StringBuildFlags | Build::StringBuildFeatures)) << std::endl; + std::cout << "Compiler: " << mpt::ToCharset(mpt::Charset::ASCII, 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; + std::cout << "Required Windows Kernel Level: " << mpt::ToCharset(mpt::Charset::ASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumKernelLevel())) << std::endl; + std::cout << "Required Windows API Level...: " << mpt::ToCharset(mpt::Charset::ASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumAPILevel())) << std::endl; + std::cout << "Windows.: " << mpt::ToCharset(mpt::Charset::ASCII, mpt::Windows::Version::Current().GetName()) << std::endl; #endif std::cout << std::flush; @@ -255,24 +255,28 @@ static MPT_NOINLINE void TestVersion() 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); + VERIFY_EQUAL( MPT_V("1.17.02.28"), Version(18285096) ); + VERIFY_EQUAL( MPT_V("1.fe.02.28"), Version(0x01fe0228) ); + VERIFY_EQUAL( MPT_V("01.fe.02.28"), Version(0x01fe0228) ); + VERIFY_EQUAL( MPT_V("1.22"), Version(0x01220000) ); + VERIFY_EQUAL( MPT_V("1.19.02.00").WithoutTestNumber(), MPT_V("1.19.02.00")); + VERIFY_EQUAL( MPT_V("1.18.03.20").WithoutTestNumber(), MPT_V("1.18.03.00")); + VERIFY_EQUAL( MPT_V("1.18.01.13").IsTestVersion(), true); + VERIFY_EQUAL( MPT_V("1.19.01.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.17.02.54").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.00.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.02.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.02.01").IsTestVersion(), true); // Ensure that versions ending in .00.00 (which are ambiguous to truncated version numbers in certain file formats (e.g. S3M and IT) do not get qualified as test builds. - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,23,00,00).IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.23.00.00").IsTestVersion(), false); - 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 ); + static_assert( MPT_V("1.17.2.28").GetRawVersion() == 18285096 ); + static_assert( MPT_V("1.17.02.48").GetRawVersion() == 18285128 ); + static_assert( MPT_V("01.17.02.52").GetRawVersion() == 18285138 ); // Ensure that bit-shifting works (used in some mod loaders for example) - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,00,00).GetRawVersion() == 0x0117 << 16 ); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,03,00).GetRawVersion() >> 8 == 0x011703 ); + static_assert( MPT_V("01.17.00.00").GetRawVersion() == 0x0117 << 16 ); + static_assert( MPT_V("01.17.03.00").GetRawVersion() >> 8 == 0x011703 ); } #ifdef MODPLUG_TRACKER @@ -284,7 +288,7 @@ static MPT_NOINLINE void TestVersion() DWORD dwVerInfoSize; // Get version information from the application - ::GetModuleFileNameW(NULL, szFullPath, mpt::saturate_cast(mpt::size(szFullPath))); + ::GetModuleFileNameW(NULL, szFullPath, mpt::saturate_cast(std::size(szFullPath))); dwVerInfoSize = ::GetFileVersionInfoSizeW(szFullPath, &dwVerHnd); if (!dwVerInfoSize) throw std::runtime_error("!dwVerInfoSize is true"); @@ -356,10 +360,10 @@ static MPT_NOINLINE void TestVersion() static MPT_NOINLINE void TestTypes() { - MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); + 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*)); + static_assert(__SIZEOF_POINTER__ == mpt::pointer_size); + static_assert(__SIZEOF_POINTER__ == sizeof(void*)); #endif VERIFY_EQUAL(int8_min, (std::numeric_limits::min)()); @@ -379,54 +383,54 @@ static MPT_NOINLINE void TestTypes() VERIFY_EQUAL(uint64_max, (std::numeric_limits::max)()); - STATIC_ASSERT(int8_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint8_max == (std::numeric_limits::max)()); + static_assert(int8_max == (std::numeric_limits::max)()); + static_assert(uint8_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int16_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint16_max == (std::numeric_limits::max)()); + static_assert(int16_max == (std::numeric_limits::max)()); + static_assert(uint16_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int32_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint32_max == (std::numeric_limits::max)()); + static_assert(int32_max == (std::numeric_limits::max)()); + static_assert(uint32_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int64_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint64_max == (std::numeric_limits::max)()); + 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::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::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)()); - 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)()); } @@ -623,12 +627,12 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(mpt::fmt::center(3, "a"), " a "); VERIFY_EQUAL(mpt::fmt::center(4, "a"), " a "); - #if defined(_MFC_VER) + #if defined(MPT_WITH_MFC) VERIFY_EQUAL(mpt::cfmt::left(3, CString(_T("a"))), CString(_T("a "))); VERIFY_EQUAL(mpt::cfmt::right(3, CString(_T("a"))), CString(_T(" a"))); VERIFY_EQUAL(mpt::cfmt::center(3, CString(_T("a"))), CString(_T(" a "))); VERIFY_EQUAL(mpt::cfmt::center(4, CString(_T("a"))), CString(_T(" a "))); - #endif + #endif // MPT_WITH_MFC VERIFY_EQUAL(ConvertStrTo("586"), 586u); VERIFY_EQUAL(ConvertStrTo("2147483647"), (uint32)int32_max); @@ -725,12 +729,12 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(mpt::format("%%%1")("a"), "%a"); VERIFY_EQUAL(mpt::format("%b")("a"), "%b"); -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); VERIFY_EQUAL(mpt::format(CString(_T("%1%2%3")))(1,2,3), _T("123")); VERIFY_EQUAL(mpt::format(CString(_T("%1%2%3")))(1,mpt::cfmt::dec0<3>(2),3), _T("10023")); -#endif +#endif // MPT_WITH_MFC } @@ -776,14 +780,12 @@ 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) +static MPT_CONSTEXPR20_FUN int32le TestEndianConstexpr(uint32 x) { - int32le foo; + int32le foo{}; foo = x; return foo; } -#endif static MPT_NOINLINE void TestMisc1() { @@ -791,24 +793,31 @@ static MPT_NOINLINE void TestMisc1() #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) + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) + { VERIFY_EQUAL(mpt::get_endian(), mpt::endian::little); - VERIFY_EQUAL(mpt::endian::native, mpt::endian::little); + MPT_MAYBE_CONSTANT_IF((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) + { + 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) + } + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) + { VERIFY_EQUAL(mpt::get_endian(), mpt::endian::big); - VERIFY_EQUAL(mpt::endian::native, mpt::endian::big); + MPT_MAYBE_CONSTANT_IF((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == 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 + } -#if MPT_ENDIAN_IS_CONSTEXPR - constexpr int32le foo = TestEndianConstexpr(23); - (void)foo; -#endif + MPT_CONSTEXPR20_VAR int32le foo = TestEndianConstexpr(23); + static_cast(foo); VERIFY_EQUAL(mpt::detail::SwapBytes(uint8(0x12)), 0x12); VERIFY_EQUAL(mpt::detail::SwapBytes(uint16(0x1234)), 0x3412); @@ -918,75 +927,75 @@ static MPT_NOINLINE void TestMisc1() 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::popcount(static_cast(int32(-1))), 32); + VERIFY_EQUAL(mpt::popcount(0u), 0); + VERIFY_EQUAL(mpt::popcount(1u), 1); + VERIFY_EQUAL(mpt::popcount(2u), 1); + VERIFY_EQUAL(mpt::popcount(3u), 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::has_single_bit(0u), false); + VERIFY_EQUAL(mpt::has_single_bit(1u), true); + VERIFY_EQUAL(mpt::has_single_bit(2u), true); + VERIFY_EQUAL(mpt::has_single_bit(3u), false); + VERIFY_EQUAL(mpt::has_single_bit(4u), true); + VERIFY_EQUAL(mpt::has_single_bit(5u), false); + VERIFY_EQUAL(mpt::has_single_bit(6u), false); + VERIFY_EQUAL(mpt::has_single_bit(7u), false); + VERIFY_EQUAL(mpt::has_single_bit(8u), true); + VERIFY_EQUAL(mpt::has_single_bit(9u), false); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0x7fffffffu)), false); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0x80000000u)), true); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0x80000001u)), false); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0xfffffffeu)), false); + VERIFY_EQUAL(mpt::has_single_bit(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::bit_ceil(0u), 1u); + VERIFY_EQUAL(mpt::bit_ceil(1u), 1u); + VERIFY_EQUAL(mpt::bit_ceil(2u), 2u); + VERIFY_EQUAL(mpt::bit_ceil(3u), 4u); + VERIFY_EQUAL(mpt::bit_ceil(4u), 4u); + VERIFY_EQUAL(mpt::bit_ceil(5u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(6u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(7u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(8u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(9u), 16u); + VERIFY_EQUAL(mpt::bit_ceil(uint32(0x7fffffffu)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_ceil(uint32(0x80000000u)), 0x80000000u); + //VERIFY_EQUAL(mpt::bit_ceil(uint32(0x80000001u)), 0u); + //VERIFY_EQUAL(mpt::bit_ceil(uint32(0xfffffffeu)), 0u); + //VERIFY_EQUAL(mpt::bit_ceil(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::bit_floor(0u), 0u); + VERIFY_EQUAL(mpt::bit_floor(1u), 1u); + VERIFY_EQUAL(mpt::bit_floor(2u), 2u); + VERIFY_EQUAL(mpt::bit_floor(3u), 2u); + VERIFY_EQUAL(mpt::bit_floor(4u), 4u); + VERIFY_EQUAL(mpt::bit_floor(5u), 4u); + VERIFY_EQUAL(mpt::bit_floor(6u), 4u); + VERIFY_EQUAL(mpt::bit_floor(7u), 4u); + VERIFY_EQUAL(mpt::bit_floor(8u), 8u); + VERIFY_EQUAL(mpt::bit_floor(9u), 8u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0x7fffffffu)), 0x40000000u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0x80000000u)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0x80000001u)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0xfffffffeu)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_floor(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); + VERIFY_EQUAL(mpt::bit_width(0u), 0u); + VERIFY_EQUAL(mpt::bit_width(1u), 1u); + VERIFY_EQUAL(mpt::bit_width(2u), 2u); + VERIFY_EQUAL(mpt::bit_width(3u), 2u); + VERIFY_EQUAL(mpt::bit_width(4u), 3u); + VERIFY_EQUAL(mpt::bit_width(5u), 3u); + VERIFY_EQUAL(mpt::bit_width(6u), 3u); + VERIFY_EQUAL(mpt::bit_width(7u), 3u); + VERIFY_EQUAL(mpt::bit_width(8u), 4u); + VERIFY_EQUAL(mpt::bit_width(9u), 4u); + VERIFY_EQUAL(mpt::bit_width(uint32(0x7fffffffu)), 31u); + VERIFY_EQUAL(mpt::bit_width(uint32(0x80000000u)), 32u); + VERIFY_EQUAL(mpt::bit_width(uint32(0x80000001u)), 32u); + VERIFY_EQUAL(mpt::bit_width(uint32(0xfffffffeu)), 32u); + VERIFY_EQUAL(mpt::bit_width(uint32(0xffffffffu)), 32u); // trivials VERIFY_EQUAL( mpt::saturate_cast(-1), -1 ); @@ -1255,7 +1264,7 @@ static MPT_NOINLINE void TestMisc1() #endif - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 1), mpt::rshift_signed_standard(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 1), mpt::rshift_signed_standard(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 1), mpt::rshift_signed_standard(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 1), mpt::rshift_signed_standard(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 1), mpt::rshift_signed_standard( -1ll, 1)); @@ -1264,7 +1273,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 1), mpt::rshift_signed_standard( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 1), mpt::rshift_signed_standard( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 63), mpt::rshift_signed_standard(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 63), mpt::rshift_signed_standard(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 63), mpt::rshift_signed_standard(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 63), mpt::rshift_signed_standard(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 63), mpt::rshift_signed_standard( -1ll, 63)); @@ -1273,7 +1282,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 63), mpt::rshift_signed_standard( 0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 63), mpt::rshift_signed_standard( 0x7fffffffffffffffll, 63)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 1), mpt::lshift_signed_standard(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 1), mpt::lshift_signed_standard(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 1), mpt::lshift_signed_standard(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 1), mpt::lshift_signed_standard(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 1), mpt::lshift_signed_standard( -1ll, 1)); @@ -1282,7 +1291,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::lshift_signed( 0x7ffffffffffffffell, 1), mpt::lshift_signed_standard( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( 0x7fffffffffffffffll, 1), mpt::lshift_signed_standard( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 63), mpt::lshift_signed_standard(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 63), mpt::lshift_signed_standard(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 63), mpt::lshift_signed_standard(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 63), mpt::lshift_signed_standard(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 63), mpt::lshift_signed_standard( -1ll, 63)); @@ -1293,7 +1302,7 @@ static MPT_NOINLINE void TestMisc1() #if MPT_COMPILER_SHIFT_SIGNED - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 1), mpt::rshift_signed_undefined(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 1), mpt::rshift_signed_undefined(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 1), mpt::rshift_signed_undefined( -1ll, 1)); @@ -1302,7 +1311,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 63), mpt::rshift_signed_undefined(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 63), mpt::rshift_signed_undefined(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 63), mpt::rshift_signed_undefined( -1ll, 63)); @@ -1311,7 +1320,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined( 0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined( 0x7fffffffffffffffll, 63)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 1), mpt::lshift_signed_undefined(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 1), mpt::lshift_signed_undefined(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 1), mpt::lshift_signed_undefined( -1ll, 1)); @@ -1320,7 +1329,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::lshift_signed( 0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( 0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 63), mpt::lshift_signed_undefined(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 63), mpt::lshift_signed_undefined(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 63), mpt::lshift_signed_undefined( -1ll, 63)); @@ -1494,14 +1503,18 @@ static MPT_NOINLINE void TestMisc2() // UUID { VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull).ToUString(), U_("2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32")); - #if defined(MODPLUG_TRACKER) || !defined(NO_DMO) + #if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) constexpr mpt::UUID uuid_tmp = "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid; VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), uuid_tmp); VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(Util::StringToGUID(_T("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}")))); VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(Util::StringToCLSID(_T("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}")))); + VERIFY_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull), mpt::UUID(Util::StringToGUID(_T("{00112233-4455-6677-8899-AABBCCDDEEFF}")))); + VERIFY_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull), mpt::UUID(Util::StringToGUID(_T("{00112233-4455-6677-C899-AABBCCDDEEFF}")))); + VERIFY_EQUAL(Util::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull)), _T("{00112233-4455-6677-8899-AABBCCDDEEFF}")); + VERIFY_EQUAL(Util::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull)), _T("{00112233-4455-6677-C899-AABBCCDDEEFF}")); #endif -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) VERIFY_EQUAL(Util::IsValid(Util::CreateGUID()), true); { mpt::UUID uuid = mpt::UUID::Generate(); @@ -1523,12 +1536,12 @@ 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]{}; + std::byte uuiddata[16]{}; for(std::size_t i = 0; i < 16; ++i) { - uuiddata[i] = mpt::byte_cast(static_cast(i)); + uuiddata[i] = mpt::byte_cast(static_cast(i)); } - STATIC_ASSERT(sizeof(mpt::UUID) == 16); + static_assert(sizeof(mpt::UUID) == 16); UUIDbin uuid2; std::memcpy(&uuid2, uuiddata, 16); VERIFY_EQUAL(mpt::UUID(uuid2).ToUString(), U_("00010203-0405-0607-0809-0a0b0c0d0e0f")); @@ -1539,17 +1552,17 @@ static MPT_NOINLINE void TestMisc2() // 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)); } - { mpt::ostringstream ss; ss.seekp(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::ostringstream ss; ss.seekp(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } - { mpt::ostringstream ss; ss.seekp(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } - { mpt::istringstream ss; VERIFY_EQUAL(ss.tellg(), std::streampos(0)); } - { mpt::istringstream ss; ss.seekg(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::istringstream ss; ss.seekg(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } - { mpt::istringstream ss; ss.seekg(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } + { std::ostringstream ss; VERIFY_EQUAL(ss.tellp(), std::streampos(0)); } + { std::ostringstream ss; ss.seekp(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::ostringstream ss; ss.seekp(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } + { std::ostringstream ss; ss.seekp(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } + { std::istringstream ss; VERIFY_EQUAL(ss.tellg(), std::streampos(0)); } + { std::istringstream ss; ss.seekg(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::istringstream ss; ss.seekg(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } + { std::istringstream ss; ss.seekg(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } { - mpt::ostringstream s; + std::ostringstream s; char b = 23; VERIFY_EQUAL(!s.fail(), true); VERIFY_EQUAL(s.tellp(), std::streampos(0)); @@ -1574,7 +1587,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s; + std::istringstream s; VERIFY_EQUAL(!s.fail(), true); VERIFY_EQUAL(s.tellg(), std::streampos(0)); VERIFY_EQUAL(!s.fail(), true); @@ -1589,7 +1602,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s("a"); + std::istringstream s("a"); char a = 0; VERIFY_EQUAL(!s.fail(), true); VERIFY_EQUAL(s.tellg(), std::streampos(0)); @@ -1616,14 +1629,14 @@ static MPT_NOINLINE void TestMisc2() // check that empty native and fixed stringstream both behaves correctly with out IO functions - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::TellWrite(ss), 0); } - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::TellRead(ss), 0); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::TellWrite(ss), 0); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::TellRead(ss), 0); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::TellWrite(ss), 0); } { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } @@ -1635,7 +1648,7 @@ static MPT_NOINLINE void TestMisc2() { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { - mpt::ostringstream s; + std::ostringstream s; char b = 23; VERIFY_EQUAL(mpt::IO::IsValid(s), true); VERIFY_EQUAL(mpt::IO::TellWrite(s), 0); @@ -1660,7 +1673,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s; + std::istringstream s; VERIFY_EQUAL(mpt::IO::IsValid(s), true); VERIFY_EQUAL(mpt::IO::TellRead(s), 0); VERIFY_EQUAL(mpt::IO::IsValid(s), true); @@ -1675,7 +1688,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s("a"); + std::istringstream s("a"); char a = 0; VERIFY_EQUAL(mpt::IO::IsValid(s), true); VERIFY_EQUAL(mpt::IO::TellRead(s), 0); @@ -1767,7 +1780,7 @@ static MPT_NOINLINE void TestMisc2() { auto TestAdaptive16 = [](uint16 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { - mpt::stringstream f; + std::stringstream f; VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt16LE(f, value, fixedSize), true); VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); if(bytes) @@ -1787,7 +1800,7 @@ static MPT_NOINLINE void TestMisc2() }; auto TestAdaptive32 = [](uint32 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { - mpt::stringstream f; + std::stringstream f; VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt32LE(f, value, fixedSize), true); VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); if(bytes) @@ -1807,7 +1820,7 @@ static MPT_NOINLINE void TestMisc2() }; auto TestAdaptive64 = [](uint64 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { - mpt::stringstream f; + std::stringstream f; VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt64LE(f, value, fixedSize), true); VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); if(bytes) @@ -1955,7 +1968,7 @@ static MPT_NOINLINE void TestMisc2() #ifdef MPT_ENABLE_FILEIO { - std::vector data; + std::vector data; data.push_back(mpt::as_byte(0)); data.push_back(mpt::as_byte(255)); data.push_back(mpt::as_byte(1)); @@ -1964,7 +1977,7 @@ static MPT_NOINLINE void TestMisc2() RemoveFile(fn); mpt::LazyFileRef f(fn); f = data; - std::vector data2; + std::vector data2; data2 = f; VERIFY_EQUAL(data.size(), data2.size()); for(std::size_t i = 0; i < data.size() && i < data2.size(); ++i) @@ -2336,98 +2349,98 @@ static MPT_NOINLINE void TestCharsets() // MPT_UTF8 version // Charset conversions (basic sanity checks) - 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")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ASCII, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::UTF8, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ISO8859_1, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ASCII, "a"), U_("a")); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, U_("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetLocale, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::Locale, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::Locale, "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"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetASCII, MPT_UTF8("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetUTF8, "a"), MPT_UTF8("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetISO8859_1, "a"), MPT_UTF8("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetASCII, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ASCII, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::UTF8, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ISO8859_1, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ASCII, "a"), MPT_UTF8("a")); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, MPT_UTF8("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetLocale, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::Locale, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::Locale, "a"), MPT_UTF8("a")); #endif // Check that some character replacement is done (and not just empty strings or truncated strings are returned) // We test german umlaut-a (U+00E4) (\xC3\xA4) and CJK U+5BB6 (\xE5\xAE\xB6) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); #endif - 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); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),U_("abc")),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - 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); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xC3\xA4xyz"),U_("abc")),true); #endif - 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); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - 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); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); #endif // Check that characters are correctly converted // We test german umlaut-a (U+00E4) and CJK U+5BB6 // cp437 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc\x84xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::CharsetCP437,"abc\x84xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc\x84xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::Charset::CP437,"abc\x84xyz")); // iso8859 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xE4xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xE4xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xE4xyz")); // utf8 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xC3\xA4xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz")); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc\xE5\xAE\xB6xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xE5\xAE\xB6xyz"),mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xC3\xA4xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::Charset::UTF8,"abc\xC3\xA4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc\xE5\xAE\xB6xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xE5\xAE\xB6xyz"),mpt::ToUnicode(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz")); #if MPT_WSTRING_CONVERT @@ -2435,15 +2448,15 @@ static MPT_NOINLINE void TestCharsets() // wide L"" version // Charset conversions (basic sanity checks) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8, L"a"), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1, L"a"), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetASCII, L"a"), "a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetUTF8, "a"), L"a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetISO8859_1, "a"), L"a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetASCII, "a"), L"a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8, L"a"), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1, L"a"), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ASCII, L"a"), "a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::UTF8, "a"), L"a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::ISO8859_1, "a"), L"a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::ASCII, "a"), L"a"); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, L"a"), "a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetLocale, "a"), L"a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::Locale, L"a"), "a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::Locale, "a"), L"a"); #endif // Check that some character replacement is done (and not just empty strings or truncated strings are returned) @@ -2454,74 +2467,74 @@ static MPT_NOINLINE void TestCharsets() #pragma warning(disable:4428) // universal-character-name encountered in source #endif - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u00E4xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u00E4xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u00E4xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u00E4xyz"),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u00E4xyz"),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u5BB6xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u5BB6xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u5BB6xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u5BB6xyz"),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u5BB6xyz"),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xC3\xA4xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xC3\xA4xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),L"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xC3\xA4xyz"),L"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),L"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),L"abc"),true); #endif // Check that characters are correctly converted // We test german umlaut-a (U+00E4) and CJK U+5BB6 // cp437 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetCP437,L"abc\u00E4xyz"),"abc\x84xyz"); - VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::CharsetCP437,"abc\x84xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::CP437,L"abc\u00E4xyz"),"abc\x84xyz"); + VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::Charset::CP437,"abc\x84xyz")); // iso8859 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u00E4xyz"),"abc\xE4xyz"); - VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::CharsetISO8859_1,"abc\xE4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u00E4xyz"),"abc\xE4xyz"); + VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xE4xyz")); // utf8 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u00E4xyz"),"abc\xC3\xA4xyz"); - VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::CharsetUTF8,"abc\xC3\xA4xyz")); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u5BB6xyz"),"abc\xE5\xAE\xB6xyz"); - VERIFY_EQUAL(L"abc\u5BB6xyz",mpt::ToWide(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u00E4xyz"),"abc\xC3\xA4xyz"); + VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::Charset::UTF8,"abc\xC3\xA4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u5BB6xyz"),"abc\xE5\xAE\xB6xyz"); + VERIFY_EQUAL(L"abc\u5BB6xyz",mpt::ToWide(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz")); #if MPT_COMPILER_MSVC #pragma warning(pop) @@ -3045,7 +3058,7 @@ static void TestLoadXMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeModern); 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_dwCreatedWithVersion, MPT_V("1.19.02.05")); VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 1); // Macros @@ -3055,21 +3068,21 @@ static void TestLoadXMFile(const CSoundFile &sndFile) // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 2); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[0].szName, "First Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); #endif // NO_PLUGINS - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[1].szName, "Second Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); #endif // NO_PLUGINS // Samples VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 3); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[1], "Pulse Sample"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[2], "Empty Sample"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[3], "Unassigned Sample"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[1] == "Pulse Sample"), true); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Empty Sample"), true); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[3] == "Unassigned Sample"), true); #ifdef MODPLUG_TRACKER VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(1), 1); VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(2), 1); @@ -3122,7 +3135,7 @@ static void TestLoadXMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0); VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), false); VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0); - VERIFY_EQUAL_NONCONT(pIns->nFilterMode, FLTMODE_UNCHANGED); + VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::Unchanged); VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0); VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0); @@ -3245,7 +3258,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeModern); 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_dwCreatedWithVersion, MPT_V("1.19.02.05")); VERIFY_EQUAL_NONCONT(sndFile.m_nResampling, SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(sndFile.m_songArtist, U_("Tester")); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing.size(), 6); @@ -3275,7 +3288,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 70); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[0].szName, "First Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nPan, 32); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nVolume, 32); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].dwFlags, CHN_MUTE); @@ -3283,7 +3296,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); #endif // NO_PLUGINS - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[1].szName, "Second Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nPan, 128); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nVolume, 16); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].dwFlags, CHN_SURROUND); @@ -3291,7 +3304,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); #endif // NO_PLUGINS - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[69].szName, "Last Channel______X"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[69].szName == "Last Channel______X"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nPan, 256); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nVolume, 7); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].dwFlags, ChannelFlags(0)); @@ -3338,7 +3351,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) { const ModSample &sample = sndFile.GetSample(2); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[2], "Stereo / 16-Bit"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Stereo / 16-Bit"), true); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); @@ -3359,8 +3372,8 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // External sample { const ModSample &sample = sndFile.GetSample(4); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[4], "Overridden Name"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sample.filename, "External"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[4] == "Overridden Name"), true); + VERIFY_EQUAL_NONCONT((sample.filename == "External"), true); #ifdef MPT_EXTERNAL_SAMPLES VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); @@ -3415,7 +3428,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0x32); VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), true); VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0x64); - VERIFY_EQUAL_NONCONT(pIns->nFilterMode, FLTMODE_HIGHPASS); + VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::HighPass); VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0x30); VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0x18); @@ -3470,14 +3483,14 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.Order.GetNumSequences(), 2); VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetLengthTailTrimmed(), 3); - VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetName(), "First Sequence"); + VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetName(), U_("First Sequence")); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[0], sndFile.Order.GetIgnoreIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[1], 0); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[2], sndFile.Order.GetIgnoreIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetRestartPos(), 1); VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetLengthTailTrimmed(), 3); - VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetName(), "Second Sequence"); + VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetName(), U_("Second Sequence")); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[0], 1); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[1], 2); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[2], 3); @@ -3537,8 +3550,11 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(plug.IsMasterEffect(), true); VERIFY_EQUAL_NONCONT(plug.GetGain(), 11); VERIFY_EQUAL_NONCONT(plug.pMixPlugin != nullptr, true); - VERIFY_EQUAL_NONCONT(plug.pMixPlugin->GetParameter(1), 0.5f); - VERIFY_EQUAL_NONCONT(plug.pMixPlugin->IsInstrument(), false); + if(plug.pMixPlugin) + { + VERIFY_EQUAL_NONCONT(plug.pMixPlugin->GetParameter(1), 0.5f); + VERIFY_EQUAL_NONCONT(plug.pMixPlugin->IsInstrument(), false); + } #endif // NO_PLUGINS #ifdef MODPLUG_TRACKER @@ -3576,7 +3592,7 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) 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 ? Version(Version::Current().GetRawVersion() & 0xFFFF0000u) : MAKE_VERSION_NUMERIC(1, 27, 00, 00)); + VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version(Version::Current().GetRawVersion() & 0xFFFF0000u) : MPT_V("1.27.00.00")); VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); // Channels @@ -3597,8 +3613,8 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 4); { const ModSample &sample = sndFile.GetSample(1); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[1], "Sample_1__________________X"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sample.filename, "Filename_1_X"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[1] == "Sample_1__________________X"), true); + VERIFY_EQUAL_NONCONT((sample.filename == "Filename_1_X"), true); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); @@ -3624,15 +3640,15 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) { const ModSample &sample = sndFile.GetSample(2); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[2], "Empty"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Empty"), true); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 16384); VERIFY_EQUAL_NONCONT(sample.nVolume, 2 * 4); } { const ModSample &sample = sndFile.GetSample(3); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[3], "Stereo / 16-Bit"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sample.filename, "Filename_3_X"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[3] == "Stereo / 16-Bit"), true); + VERIFY_EQUAL_NONCONT((sample.filename == "Filename_3_X"), true); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); @@ -3653,8 +3669,8 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) { 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((sndFile.m_szNames[4] == "adlib"), true); + VERIFY_EQUAL_NONCONT((sample.filename == ""), true); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 8363); VERIFY_EQUAL_NONCONT(sample.nVolume, 58 * 4); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_ADLIB); @@ -3699,38 +3715,20 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) 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(), U_("\\")).size() - 1; - for(std::size_t i = 0; i < pathComponents; ++i) + mpt::PathString theFile = theApp.GetInstallPath(); + if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) { - if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) + if((theFile + P_("test\\test.mptm")).IsFile()) { - if((theFile + P_("test\\test.mptm")).IsFile()) - { - return true; - } + return true; } - theFile += P_("..\\"); } return false; } static mpt::PathString GetTestFilenameBase() { - mpt::PathString theFile = theApp.GetAppDirPath(); - 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 + P_("test")).IsDirectory()) - { - if((theFile + P_("test\\test.mptm")).IsFile()) - { - break; - } - } - theFile += P_("..\\"); - } + mpt::PathString theFile = theApp.GetInstallPath(); theFile += P_("test/test."); return theFile; } @@ -3975,7 +3973,7 @@ static MPT_NOINLINE void TestLoadSaveFile() // General file I/O tests { - mpt::ostringstream f; + std::ostringstream f; size_t bytesWritten; mpt::IO::WriteVarInt(f, uint16(0), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); mpt::IO::WriteVarInt(f, uint16(127), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); @@ -4000,15 +3998,16 @@ static MPT_NOINLINE void TestLoadSaveFile() // This is both, compile-time and run-time cheking. // Run-time in case some weird compiler gets confused by our templates // and only writes the first array element. - mpt::ostringstream f; + std::ostringstream f; uint16be data[2]; + Clear(data); data[0] = 0x1234; data[1] = 0x5678; mpt::IO::Write(f, data); VERIFY_EQUAL(f.str(), std::string("\x12\x34\x56\x78")); } { - mpt::ostringstream f; + std::ostringstream f; std::vector data; data.resize(3); data[0] = 0x1234; @@ -4018,8 +4017,9 @@ static MPT_NOINLINE void TestLoadSaveFile() VERIFY_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34")); } { - mpt::ostringstream f; + std::ostringstream f; int16be data[3]; + Clear(data); data[0] = 0x1234; data[1] = 0x5678; data[2] = 0x1234; @@ -4070,10 +4070,10 @@ static MPT_NOINLINE void TestEditing() // Rearrange samples sndFile.m_nSamples = 2; - mpt::String::Copy(sndFile.GetSample(1).filename, "1"); - mpt::String::Copy(sndFile.m_szNames[1], "1"); - mpt::String::Copy(sndFile.GetSample(2).filename, "2"); - mpt::String::Copy(sndFile.m_szNames[2], "2"); + sndFile.GetSample(1).filename = "1"; + sndFile.m_szNames[1] = "1"; + sndFile.GetSample(2).filename = "2"; + sndFile.m_szNames[2] = "2"; sndFile.GetSample(2).nLength = 16; sndFile.GetSample(2).AllocateSample(); modDoc->ReArrangeSamples({ 2, SAMPLEINDEX_INVALID, 1 }); @@ -4119,7 +4119,7 @@ static void RunITCompressionTest(const std::vector &sampleData, FlagSet pSndFile = mpt::make_unique(); + std::unique_ptr pSndFile = std::make_unique(); CSoundFile &sndFile = *pSndFile.get(); sndFile.m_nType = MOD_TYPE_MPT; sndFile.Patterns.DestroyPatterns(); @@ -4260,7 +4260,7 @@ static MPT_NOINLINE void TestPCnoteSerialization() // Copy pattern data for comparison. CPatternContainer patterns{ sndFile.Patterns }; - mpt::stringstream mem; + std::stringstream mem; WriteModPatterns(mem, sndFile.Patterns); VERIFY_EQUAL_NONCONT( mem.good(), true ); @@ -4284,6 +4284,27 @@ static MPT_NOINLINE void TestPCnoteSerialization() } +static inline std::size_t strnlen(const char *str, std::size_t n) +{ +#if MPT_COMPILER_MSVC + return ::strnlen(str, n); +#else + if(n >= std::numeric_limits::max()) + { + return std::strlen(str); + } + for(std::size_t i = 0; i < n; ++i) + { + if(str[i] == '\0') + { + return i; + } + } + return n; +#endif +} + + // Test String I/O functionality static MPT_NOINLINE void TestStringIO() { @@ -4297,27 +4318,17 @@ static MPT_NOINLINE void TestStringIO() #define ReadTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ - mpt::String::Read(dst, 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 */ \ - 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 */ \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ #define WriteTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ - mpt::String::Write(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 */ \ - 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(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ @@ -4437,28 +4448,21 @@ static MPT_NOINLINE void TestStringIO() { std::string dststring; - 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)); + std::string src0string = std::string(src0, std::size(src0)); + std::string src1string = std::string(src1, std::size(src1)); + std::string src2string = std::string(src2, std::size(src2)); + std::string src3string = std::string(src3, std::size(src3)); #define ReadTest(mode, dst, src, expectedResult) \ - 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, 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(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ @@ -4553,9 +4557,120 @@ static MPT_NOINLINE void TestStringIO() mpt::String::FixNullString(src1); mpt::String::FixNullString(src2); mpt::String::FixNullString(src3); - 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); + VERIFY_EQUAL_NONCONT(strncmp(src1, "X ", std::size(src1)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src2, "XYZ", std::size(src2)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src3, "XYZ", std::size(src3)), 0); + + { + + char s0[4] = {'\0', 'X', ' ', 'X' }; + char s2[4] = { 'X', ' ','\0', 'X' }; + char s4[4] = { 'X', 'Y', 'Z', ' ' }; + + char d2[2] = {'\0','\0'}; + char d3[3] = {'\0','\0','\0'}; + char d4[4] = {'\0','\0','\0','\0'}; + char d5[5] = {'\0','\0','\0','\0','\0'}; + + #define CopyTest(dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + CopyTest(d2, s0, ""); + CopyTest(d2, s2, "X"); + CopyTest(d2, s4, "X"); + CopyTest(d3, s0, ""); + CopyTest(d3, s2, "X "); + CopyTest(d3, s4, "XY"); + CopyTest(d4, s0, ""); + CopyTest(d4, s2, "X "); + CopyTest(d4, s4, "XYZ"); + CopyTest(d5, s0, ""); + CopyTest(d5, s2, "X "); + CopyTest(d5, s4, "XYZ "); + + #undef CopyTest + + #define CopyTestN(dst, src, len, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src, std::min(std::size(src), static_cast(len))); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + CopyTestN(d2, s0, 1, ""); + CopyTestN(d2, s2, 1, "X"); + CopyTestN(d2, s4, 1, "X"); + CopyTestN(d3, s0, 1, ""); + CopyTestN(d3, s2, 1, "X"); + CopyTestN(d3, s4, 1, "X"); + CopyTestN(d4, s0, 1, ""); + CopyTestN(d4, s2, 1, "X"); + CopyTestN(d4, s4, 1, "X"); + CopyTestN(d5, s0, 1, ""); + CopyTestN(d5, s2, 1, "X"); + CopyTestN(d5, s4, 1, "X"); + + CopyTestN(d2, s0, 2, ""); + CopyTestN(d2, s2, 2, "X"); + CopyTestN(d2, s4, 2, "X"); + CopyTestN(d3, s0, 2, ""); + CopyTestN(d3, s2, 2, "X "); + CopyTestN(d3, s4, 2, "XY"); + CopyTestN(d4, s0, 2, ""); + CopyTestN(d4, s2, 2, "X "); + CopyTestN(d4, s4, 2, "XY"); + CopyTestN(d5, s0, 2, ""); + CopyTestN(d5, s2, 2, "X "); + CopyTestN(d5, s4, 2, "XY"); + + CopyTestN(d2, s0, 3, ""); + CopyTestN(d2, s2, 3, "X"); + CopyTestN(d2, s4, 3, "X"); + CopyTestN(d3, s0, 3, ""); + CopyTestN(d3, s2, 3, "X "); + CopyTestN(d3, s4, 3, "XY"); + CopyTestN(d4, s0, 3, ""); + CopyTestN(d4, s2, 3, "X "); + CopyTestN(d4, s4, 3, "XYZ"); + CopyTestN(d5, s0, 3, ""); + CopyTestN(d5, s2, 3, "X "); + CopyTestN(d5, s4, 3, "XYZ"); + + CopyTestN(d2, s0, 4, ""); + CopyTestN(d2, s2, 4, "X"); + CopyTestN(d2, s4, 4, "X"); + CopyTestN(d3, s0, 4, ""); + CopyTestN(d3, s2, 4, "X "); + CopyTestN(d3, s4, 4, "XY"); + CopyTestN(d4, s0, 4, ""); + CopyTestN(d4, s2, 4, "X "); + CopyTestN(d4, s4, 4, "XYZ"); + CopyTestN(d5, s0, 4, ""); + CopyTestN(d5, s2, 4, "X "); + CopyTestN(d5, s4, 4, "XYZ "); + + CopyTestN(d2, s0, 5, ""); + CopyTestN(d2, s2, 5, "X"); + CopyTestN(d2, s4, 5, "X"); + CopyTestN(d3, s0, 5, ""); + CopyTestN(d3, s2, 5, "X "); + CopyTestN(d3, s4, 5, "XY"); + CopyTestN(d4, s0, 5, ""); + CopyTestN(d4, s2, 5, "X "); + CopyTestN(d4, s4, 5, "XYZ"); + CopyTestN(d5, s0, 5, ""); + CopyTestN(d5, s2, 5, "X "); + CopyTestN(d5, s4, 5, "XYZ "); + + #undef CopyTest + + } } @@ -4582,9 +4697,9 @@ static MPT_NOINLINE void TestSampleConversion() uint8 *unsigned8 = static_cast(targetBuf) + 256; int8 *delta8 = static_cast(targetBuf) + 512; int8 delta = 0; - CopySample(signed8, 256, 1, mpt::byte_cast(source8), 256, 1); - CopySample(reinterpret_cast(unsigned8), 256, 1, mpt::byte_cast(source8), 256, 1); - CopySample(delta8, 256, 1, mpt::byte_cast(source8), 256, 1); + CopySample(signed8, 256, 1, mpt::byte_cast(source8), 256, 1); + CopySample(reinterpret_cast(unsigned8), 256, 1, mpt::byte_cast(source8), 256, 1); + CopySample(delta8, 256, 1, mpt::byte_cast(source8), 256, 1); for(size_t i = 0; i < 256; i++) { @@ -4612,9 +4727,9 @@ static MPT_NOINLINE void TestSampleConversion() uint16 *unsigned16 = static_cast(targetBuf) + 65536; int16 *delta16 = static_cast(targetBuf) + 65536 * 2; int16 delta = 0; - CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); for(size_t i = 0; i < 65536; i++) { @@ -4632,9 +4747,9 @@ static MPT_NOINLINE void TestSampleConversion() source16[i * 2 + 1] = static_cast(i & 0xFF); } - CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); delta = 0; for(size_t i = 0; i < 65536; i++) @@ -4663,8 +4778,8 @@ static MPT_NOINLINE void TestSampleConversion() sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); sample.pData.pSample = (static_cast(targetBuf) + 65536); - CopyAndNormalizeSample, SC::DecodeInt24<0, littleEndian24> > >(sample, mpt::byte_cast(source24), 3*65536); - CopySample, SC::DecodeInt24<0, littleEndian24> > >(truncated16, 65536, 1, mpt::byte_cast(source24), 65536 * 3, 1); + 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++) { @@ -4691,16 +4806,13 @@ static MPT_NOINLINE void TestSampleConversion() sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); sample.pData.pSample = static_cast(targetBuf) + 65536; - CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, mpt::byte_cast(source32), 4*65536); - CopySample, SC::DecodeFloat32 > >(truncated16, 65536, 1, mpt::byte_cast(source32), 65536 * 4, 1); + CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, mpt::byte_cast(source32), 4*65536); + CopySample, SC::DecodeFloat32 > >(truncated16, 65536, 1, mpt::byte_cast(source32), 65536 * 4, 1); for(size_t i = 0; i < 65536; i++) { VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast(i - 0x8000u)); - if(mpt::abs(truncated16[i] - static_cast((i - 0x8000u) / 2)) > 1) - { - VERIFY_EQUAL_QUIET_NONCONT(true, false); - } + VERIFY_EQUAL_QUIET_NONCONT(std::abs(truncated16[i] - static_cast((i - 0x8000u) / 2)) <= 1, true); } } @@ -4709,7 +4821,7 @@ static MPT_NOINLINE void TestSampleConversion() int8 oneSample = 1; char *signed8 = reinterpret_cast(targetBuf); memset(signed8, 0, 4); - CopySample(reinterpret_cast(targetBuf), 4, 1, reinterpret_cast(&oneSample), sizeof(oneSample), 1); + CopySample(reinterpret_cast(targetBuf), 4, 1, reinterpret_cast(&oneSample), sizeof(oneSample), 1); VERIFY_EQUAL_NONCONT(signed8[0], 1); VERIFY_EQUAL_NONCONT(signed8[1], 0); VERIFY_EQUAL_NONCONT(signed8[2], 0); @@ -4718,11 +4830,14 @@ static MPT_NOINLINE void TestSampleConversion() // Dither { - std::vector buffer(64); + std::vector buffer(64); Dither dither(mpt::global_random_device()); dither.SetMode(DitherModPlug); - dither.Process(buffer.data(), 64, 1, 16); - std::vector expected = { + for(std::size_t i = 0; i < 64; ++i) + { + buffer[i] = dither.ModPlugDither().process<16>(0, buffer[i]); + } + std::vector expected = { 727, -557, -552, diff --git a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj index 71543d4f6..e513bf845 100644 --- a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj @@ -25,6 +25,16 @@ 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 */; }; + 83AA7D1D2519B619004C5298 /* mptOSError.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D1B2519B618004C5298 /* mptOSError.h */; }; + 83AA7D1E2519B619004C5298 /* mptOSException.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D1C2519B618004C5298 /* mptOSException.h */; }; + 83AA7D252519B643004C5298 /* Dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D212519B643004C5298 /* Dither.cpp */; }; + 83AA7D262519B643004C5298 /* SampleBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D222519B643004C5298 /* SampleBuffer.h */; }; + 83AA7D272519B643004C5298 /* SampleTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D232519B643004C5298 /* SampleTypes.h */; }; + 83AA7D282519B643004C5298 /* Dither.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D242519B643004C5298 /* Dither.h */; }; + 83AA7D322519B694004C5298 /* TinyFFT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D2E2519B694004C5298 /* TinyFFT.cpp */; }; + 83AA7D332519B694004C5298 /* SampleFormatSFZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D2F2519B694004C5298 /* SampleFormatSFZ.cpp */; }; + 83AA7D342519B694004C5298 /* SampleFormatBRR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D302519B694004C5298 /* SampleFormatBRR.cpp */; }; + 83AA7D352519B694004C5298 /* TinyFFT.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D312519B694004C5298 /* TinyFFT.h */; }; 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 */; }; @@ -86,10 +96,8 @@ 83E5FCCE1FFEFA1A00659F0F /* libopenmpt.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FC9F1FFEFA1A00659F0F /* libopenmpt.h */; settings = {ATTRIBUTES = (Public, ); }; }; 83E5FCCF1FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA01FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp */; }; 83E5FCD01FFEFA1A00659F0F /* libopenmpt_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA11FFEFA1A00659F0F /* libopenmpt_internal.h */; }; - 83E5FCD21FFEFA1A00659F0F /* libopenmpt_modplug.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCA31FFEFA1A00659F0F /* libopenmpt_modplug.c */; }; 83E5FCD31FFEFA1A00659F0F /* libopenmpt_c.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCA41FFEFA1A00659F0F /* libopenmpt_c.cpp */; }; 83E5FCD51FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA61FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp */; }; - 83E5FCD71FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCA81FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp */; }; 83E5FCD81FFEFA1A00659F0F /* libopenmpt_config.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCA91FFEFA1A00659F0F /* libopenmpt_config.h */; settings = {ATTRIBUTES = (Public, ); }; }; 83E5FCDB1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FCAC1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h */; }; 83E5FCDC1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FCAD1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp */; }; @@ -131,7 +139,6 @@ 83E5FDC91FFEFA8500659F0F /* ChunkReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD211FFEFA8400659F0F /* ChunkReader.h */; }; 83E5FDCA1FFEFA8500659F0F /* ITCompression.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD221FFEFA8400659F0F /* ITCompression.h */; }; 83E5FDCB1FFEFA8500659F0F /* Load_psm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD231FFEFA8400659F0F /* Load_psm.cpp */; }; - 83E5FDCC1FFEFA8500659F0F /* Dither.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD241FFEFA8400659F0F /* Dither.h */; }; 83E5FDCD1FFEFA8500659F0F /* S3MTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD251FFEFA8400659F0F /* S3MTools.h */; }; 83E5FDCE1FFEFA8500659F0F /* Load_far.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD261FFEFA8400659F0F /* Load_far.cpp */; }; 83E5FDCF1FFEFA8500659F0F /* patternContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD271FFEFA8400659F0F /* patternContainer.cpp */; }; @@ -179,7 +186,6 @@ 83E5FDFA1FFEFA8500659F0F /* Flanger.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD541FFEFA8400659F0F /* Flanger.h */; }; 83E5FDFB1FFEFA8500659F0F /* PluginManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD551FFEFA8400659F0F /* PluginManager.h */; }; 83E5FDFC1FFEFA8500659F0F /* Load_ams.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD561FFEFA8400659F0F /* Load_ams.cpp */; }; - 83E5FDFD1FFEFA8500659F0F /* tuningbase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */; }; 83E5FDFE1FFEFA8500659F0F /* ContainerUMX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */; }; 83E5FDFF1FFEFA8500659F0F /* Load_ptm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD591FFEFA8400659F0F /* Load_ptm.cpp */; }; 83E5FE001FFEFA8500659F0F /* SampleIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */; }; @@ -246,7 +252,6 @@ 83E5FE3D1FFEFA8500659F0F /* Load_s3m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD971FFEFA8400659F0F /* Load_s3m.cpp */; }; 83E5FE3E1FFEFA8500659F0F /* tuningCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */; }; 83E5FE3F1FFEFA8500659F0F /* SampleIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */; }; - 83E5FE401FFEFA8500659F0F /* Dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */; }; 83E5FE411FFEFA8500659F0F /* Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD9B1FFEFA8400659F0F /* Resampler.h */; }; 83E5FE421FFEFA8500659F0F /* ModChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD9C1FFEFA8400659F0F /* ModChannel.h */; }; 83E5FE431FFEFA8500659F0F /* MixerSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5FD9D1FFEFA8400659F0F /* MixerSettings.h */; }; @@ -299,6 +304,16 @@ 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 = ""; }; + 83AA7D1B2519B618004C5298 /* mptOSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptOSError.h; sourceTree = ""; }; + 83AA7D1C2519B618004C5298 /* mptOSException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptOSException.h; sourceTree = ""; }; + 83AA7D212519B643004C5298 /* Dither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dither.cpp; sourceTree = ""; }; + 83AA7D222519B643004C5298 /* SampleBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleBuffer.h; sourceTree = ""; }; + 83AA7D232519B643004C5298 /* SampleTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleTypes.h; sourceTree = ""; }; + 83AA7D242519B643004C5298 /* Dither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dither.h; sourceTree = ""; }; + 83AA7D2E2519B694004C5298 /* TinyFFT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TinyFFT.cpp; sourceTree = ""; }; + 83AA7D2F2519B694004C5298 /* SampleFormatSFZ.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatSFZ.cpp; sourceTree = ""; }; + 83AA7D302519B694004C5298 /* SampleFormatBRR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatBRR.cpp; sourceTree = ""; }; + 83AA7D312519B694004C5298 /* TinyFFT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TinyFFT.h; 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; }; @@ -364,10 +379,8 @@ 83E5FC9F1FFEFA1A00659F0F /* libopenmpt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt.h; sourceTree = ""; }; 83E5FCA01FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = libopenmpt_plugin_settings.hpp; sourceTree = ""; }; 83E5FCA11FFEFA1A00659F0F /* libopenmpt_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_internal.h; sourceTree = ""; }; - 83E5FCA31FFEFA1A00659F0F /* libopenmpt_modplug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libopenmpt_modplug.c; sourceTree = ""; }; 83E5FCA41FFEFA1A00659F0F /* libopenmpt_c.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_c.cpp; sourceTree = ""; }; 83E5FCA61FFEFA1A00659F0F /* libopenmpt_ext_impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = libopenmpt_ext_impl.hpp; sourceTree = ""; }; - 83E5FCA81FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_modplug_cpp.cpp; sourceTree = ""; }; 83E5FCA91FFEFA1A00659F0F /* libopenmpt_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_config.h; sourceTree = ""; }; 83E5FCAC1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libopenmpt_stream_callbacks_file.h; sourceTree = ""; }; 83E5FCAD1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libopenmpt_ext_impl.cpp; sourceTree = ""; }; @@ -409,7 +422,6 @@ 83E5FD211FFEFA8400659F0F /* ChunkReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChunkReader.h; sourceTree = ""; }; 83E5FD221FFEFA8400659F0F /* ITCompression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ITCompression.h; sourceTree = ""; }; 83E5FD231FFEFA8400659F0F /* Load_psm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_psm.cpp; sourceTree = ""; }; - 83E5FD241FFEFA8400659F0F /* Dither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dither.h; sourceTree = ""; }; 83E5FD251FFEFA8400659F0F /* S3MTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S3MTools.h; sourceTree = ""; }; 83E5FD261FFEFA8400659F0F /* Load_far.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_far.cpp; sourceTree = ""; }; 83E5FD271FFEFA8400659F0F /* patternContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = patternContainer.cpp; sourceTree = ""; }; @@ -457,7 +469,6 @@ 83E5FD541FFEFA8400659F0F /* Flanger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Flanger.h; sourceTree = ""; }; 83E5FD551FFEFA8400659F0F /* PluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginManager.h; sourceTree = ""; }; 83E5FD561FFEFA8400659F0F /* Load_ams.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_ams.cpp; sourceTree = ""; }; - 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tuningbase.cpp; sourceTree = ""; }; 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerUMX.cpp; sourceTree = ""; }; 83E5FD591FFEFA8400659F0F /* Load_ptm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_ptm.cpp; sourceTree = ""; }; 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleIO.h; sourceTree = ""; }; @@ -524,7 +535,6 @@ 83E5FD971FFEFA8400659F0F /* Load_s3m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_s3m.cpp; sourceTree = ""; }; 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tuningCollection.cpp; sourceTree = ""; }; 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleIO.cpp; sourceTree = ""; }; - 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dither.cpp; sourceTree = ""; }; 83E5FD9B1FFEFA8400659F0F /* Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Resampler.h; sourceTree = ""; }; 83E5FD9C1FFEFA8400659F0F /* ModChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModChannel.h; sourceTree = ""; }; 83E5FD9D1FFEFA8400659F0F /* MixerSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MixerSettings.h; sourceTree = ""; }; @@ -671,6 +681,8 @@ 83E5FC371FFEFA0D00659F0F /* mptMutex.h */, 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */, 83E5FC5C1FFEFA0D00659F0F /* mptOS.h */, + 83AA7D1B2519B618004C5298 /* mptOSError.h */, + 83AA7D1C2519B618004C5298 /* mptOSException.h */, 83E5FC4D1FFEFA0D00659F0F /* mptPathString.cpp */, 83E5FC591FFEFA0D00659F0F /* mptPathString.h */, 83E5FC5B1FFEFA0D00659F0F /* mptRandom.cpp */, @@ -716,8 +728,6 @@ 83E5FCBF1FFEFA1A00659F0F /* libopenmpt_impl.cpp */, 83E5FC9C1FFEFA1A00659F0F /* libopenmpt_impl.hpp */, 83E5FCA11FFEFA1A00659F0F /* libopenmpt_internal.h */, - 83E5FCA81FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp */, - 83E5FCA31FFEFA1A00659F0F /* libopenmpt_modplug.c */, 83E5FCA01FFEFA1A00659F0F /* libopenmpt_plugin_settings.hpp */, 83E5FCCA1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_buffer.h */, 83E5FC9D1FFEFA1A00659F0F /* libopenmpt_stream_callbacks_fd.h */, @@ -732,9 +742,13 @@ 83E5FCF61FFEFA7400659F0F /* soundbase */ = { isa = PBXGroup; children = ( + 83AA7D212519B643004C5298 /* Dither.cpp */, + 83AA7D242519B643004C5298 /* Dither.h */, + 83AA7D222519B643004C5298 /* SampleBuffer.h */, 83E5FCF71FFEFA7400659F0F /* SampleFormat.h */, - 83E5FCF81FFEFA7400659F0F /* SampleFormatCopy.h */, 83E5FCF91FFEFA7400659F0F /* SampleFormatConverters.h */, + 83E5FCF81FFEFA7400659F0F /* SampleFormatCopy.h */, + 83AA7D232519B643004C5298 /* SampleTypes.h */, ); path = soundbase; sourceTree = ""; @@ -767,8 +781,6 @@ 83E5FD881FFEFA8400659F0F /* ContainerPP20.cpp */, 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */, 83E5FD5D1FFEFA8400659F0F /* ContainerXPK.cpp */, - 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */, - 83E5FD241FFEFA8400659F0F /* Dither.h */, 83E5FD7A1FFEFA8400659F0F /* Dlsbank.cpp */, 83E5FD911FFEFA8400659F0F /* Dlsbank.h */, 83E5FDAE1FFEFA8400659F0F /* Fastmix.cpp */, @@ -862,11 +874,13 @@ 83E5FD321FFEFA8400659F0F /* RowVisitor.h */, 83E5FD901FFEFA8400659F0F /* S3MTools.cpp */, 83E5FD251FFEFA8400659F0F /* S3MTools.h */, + 83AA7D302519B694004C5298 /* SampleFormatBRR.cpp */, 83E5FD751FFEFA8400659F0F /* SampleFormatFLAC.cpp */, 83E5FDB51FFEFA8400659F0F /* SampleFormatMediaFoundation.cpp */, 83E5FD5E1FFEFA8400659F0F /* SampleFormatMP3.cpp */, 83E5FDAC1FFEFA8400659F0F /* SampleFormatOpus.cpp */, 83E5FDA11FFEFA8400659F0F /* SampleFormats.cpp */, + 83AA7D2F2519B694004C5298 /* SampleFormatSFZ.cpp */, 83E5FD8B1FFEFA8400659F0F /* SampleFormatVorbis.cpp */, 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */, 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */, @@ -882,9 +896,10 @@ 83E5FD811FFEFA8400659F0F /* Tables.h */, 83E5FDB01FFEFA8400659F0F /* Tagging.cpp */, 83E5FD7B1FFEFA8400659F0F /* Tagging.h */, + 83AA7D2E2519B694004C5298 /* TinyFFT.cpp */, + 83AA7D312519B694004C5298 /* TinyFFT.h */, 83E5FD5F1FFEFA8400659F0F /* tuning.cpp */, 83E5FDA51FFEFA8400659F0F /* tuning.h */, - 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */, 83E5FD831FFEFA8400659F0F /* tuningbase.h */, 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */, 83E5FD7C1FFEFA8400659F0F /* tuningcollection.h */, @@ -996,8 +1011,9 @@ 83E5FC6C1FFEFA0D00659F0F /* mptStringFormat.h in Headers */, 83E5FDBA1FFEFA8500659F0F /* MixerInterface.h in Headers */, 83E5FDD81FFEFA8500659F0F /* ITTools.h in Headers */, + 83AA7D272519B643004C5298 /* SampleTypes.h in Headers */, 83E5FE241FFEFA8500659F0F /* FloatMixer.h in Headers */, - 83E5FDCC1FFEFA8500659F0F /* Dither.h in Headers */, + 83AA7D262519B643004C5298 /* SampleBuffer.h in Headers */, 83E5FC681FFEFA0D00659F0F /* stdafx.h in Headers */, 83E5FDD21FFEFA8500659F0F /* MPEGFrame.h in Headers */, 83E5FDCA1FFEFA8500659F0F /* ITCompression.h in Headers */, @@ -1065,7 +1081,10 @@ 83E5FDC91FFEFA8500659F0F /* ChunkReader.h in Headers */, 83E5FDF91FFEFA8500659F0F /* Distortion.h in Headers */, 83E5FE261FFEFA8500659F0F /* AudioCriticalSection.h in Headers */, + 83AA7D352519B694004C5298 /* TinyFFT.h in Headers */, 83E5FCD01FFEFA1A00659F0F /* libopenmpt_internal.h in Headers */, + 83AA7D1E2519B619004C5298 /* mptOSException.h in Headers */, + 83AA7D1D2519B619004C5298 /* mptOSError.h in Headers */, 83E5FC991FFEFA0D00659F0F /* versionNumber.h in Headers */, 83E5FC771FFEFA0D00659F0F /* mptTime.h in Headers */, 83E5FC8C1FFEFA0D00659F0F /* mptString.h in Headers */, @@ -1095,6 +1114,7 @@ 83E5FE391FFEFA8500659F0F /* IntMixer.h in Headers */, 83E5FDF01FFEFA8500659F0F /* WavesReverb.h in Headers */, 83E5FDED1FFEFA8500659F0F /* I3DL2Reverb.h in Headers */, + 83AA7D282519B643004C5298 /* Dither.h in Headers */, 83E5FC701FFEFA0D00659F0F /* mptMutex.h in Headers */, 83E5FD0C1FFEFA7D00659F0F /* DSP.h in Headers */, 83E5FC631FFEFA0D00659F0F /* mptCRC.h in Headers */, @@ -1219,6 +1239,7 @@ 83E5FC6D1FFEFA0D00659F0F /* Logging.cpp in Sources */, 83E5FE151FFEFA8500659F0F /* Sndmix.cpp in Sources */, 83E5FDB71FFEFA8500659F0F /* WAVTools.cpp in Sources */, + 83AA7D322519B694004C5298 /* TinyFFT.cpp in Sources */, 83E5FDEE1FFEFA8500659F0F /* I3DL2Reverb.cpp in Sources */, 83E5FC9A1FFEFA0D00659F0F /* misc_util.cpp in Sources */, 83E5FE0C1FFEFA8500659F0F /* Load_mtm.cpp in Sources */, @@ -1240,15 +1261,16 @@ 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 */, 83E5FE1D1FFEFA8500659F0F /* ModInstrument.cpp in Sources */, 83E5FE461FFEFA8500659F0F /* WindowedFIR.cpp in Sources */, + 83AA7D252519B643004C5298 /* Dither.cpp in Sources */, 83E5FE1E1FFEFA8500659F0F /* Load_mo3.cpp in Sources */, 83E5FE351FFEFA8500659F0F /* MixerSettings.cpp in Sources */, 83E5FC891FFEFA0D00659F0F /* mptString.cpp in Sources */, 83E5FDC61FFEFA8500659F0F /* mod_specifications.cpp in Sources */, 831132D621F955B2001F678F /* mptAlloc.cpp in Sources */, + 83AA7D332519B694004C5298 /* SampleFormatSFZ.cpp in Sources */, 831132D821F955B2001F678F /* mptStringBuffer.cpp in Sources */, 83E5FDD61FFEFA8500659F0F /* modcommand.cpp in Sources */, 83E5FD091FFEFA7D00659F0F /* DSP.cpp in Sources */, @@ -1270,9 +1292,9 @@ 83E5FDBD1FFEFA8500659F0F /* Load_dbm.cpp in Sources */, 83E5FCDC1FFEFA1A00659F0F /* libopenmpt_ext_impl.cpp in Sources */, 83E5FDE71FFEFA8500659F0F /* DMOPlugin.cpp in Sources */, - 83E5FCD71FFEFA1A00659F0F /* libopenmpt_modplug_cpp.cpp in Sources */, 83E5FE091FFEFA8500659F0F /* Load_669.cpp in Sources */, 83E5FDCF1FFEFA8500659F0F /* patternContainer.cpp in Sources */, + 83AA7D342519B694004C5298 /* SampleFormatBRR.cpp in Sources */, 83E5FE141FFEFA8500659F0F /* Load_sfx.cpp in Sources */, 83E5FE571FFEFA8500659F0F /* ITCompression.cpp in Sources */, 83E5FE591FFEFA8500659F0F /* MPEGFrame.cpp in Sources */, @@ -1280,7 +1302,6 @@ 83E5FC941FFEFA0D00659F0F /* mptRandom.cpp in Sources */, 83E5FDD31FFEFA8500659F0F /* Paula.cpp in Sources */, 83E5FE251FFEFA8500659F0F /* Load_itp.cpp in Sources */, - 83E5FE401FFEFA8500659F0F /* Dither.cpp in Sources */, 83E5FDDB1FFEFA8500659F0F /* Load_uax.cpp in Sources */, 83E5F8801FFEF9E400659F0F /* minimp3.c in Sources */, 83E5F9831FFEF9E400659F0F /* stb_vorbis.c in Sources */, @@ -1296,7 +1317,6 @@ 83E5FE051FFEFA8500659F0F /* tuning.cpp in Sources */, 83E5FD0B1FFEFA7D00659F0F /* Reverb.cpp in Sources */, 83E5FE481FFEFA8500659F0F /* Load_wav.cpp in Sources */, - 83E5FDFD1FFEFA8500659F0F /* tuningbase.cpp in Sources */, 83E5FE2F1FFEFA8500659F0F /* RowVisitor.cpp in Sources */, 83E5FDE91FFEFA8500659F0F /* Distortion.cpp in Sources */, 83E5FE201FFEFA8500659F0F /* Dlsbank.cpp in Sources */, @@ -1327,7 +1347,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -1395,7 +1415,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; diff --git a/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj index d1722e4f1..e6bbb05d5 100644 --- a/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj +++ b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj @@ -238,7 +238,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -295,7 +295,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES;