Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: unblock X-Debbugs-Cc: libopen...@packages.debian.org, sramac...@debian.org Control: affects -1 + src:libopenmpt
Please unblock package libopenmpt. [ Reason ] libopenmpt 0.6.7 is a bugfix release only that fixes issues with parsing/rending (potentially untrusted) media files: [**Bug**] An exception could be thrown during rendering when trying to access the release node of an empty envelope. (from upstream's changelog) It also contains some targetted bug fixes, but no new features. [ Impact ] Users may observe crashes when opening and playing certain media files. [ Tests ] The package has autopkgtests that succeed. [ Risks ] The package is a key-package because of ffmpeg. [ Checklist ] [x] all changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in testing unblock libopenmpt/0.6.9-1 Cheers -- Sebastian Ramacher
diff -Nru libopenmpt-0.6.8/common/versionNumber.h libopenmpt-0.6.9/common/versionNumber.h --- libopenmpt-0.6.8/common/versionNumber.h 2023-01-29 12:59:32.000000000 +0100 +++ libopenmpt-0.6.9/common/versionNumber.h 2023-03-05 13:24:48.000000000 +0100 @@ -17,7 +17,7 @@ // Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 #define VER_MAJOR 30 -#define VER_MINOR 10 +#define VER_MINOR 11 #define VER_MINORMINOR 00 OPENMPT_NAMESPACE_END diff -Nru libopenmpt-0.6.8/configure libopenmpt-0.6.9/configure --- libopenmpt-0.6.8/configure 2023-01-29 13:25:43.000000000 +0100 +++ libopenmpt-0.6.9/configure 2023-03-05 13:49:28.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for libopenmpt 0.6.8+release.autotools. +# Generated by GNU Autoconf 2.69 for libopenmpt 0.6.9+release.autotools. # # Report bugs to <https://bugs.openmpt.org/>. # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='libopenmpt' PACKAGE_TARNAME='libopenmpt' -PACKAGE_VERSION='0.6.8+release.autotools' -PACKAGE_STRING='libopenmpt 0.6.8+release.autotools' +PACKAGE_VERSION='0.6.9+release.autotools' +PACKAGE_STRING='libopenmpt 0.6.9+release.autotools' PACKAGE_BUGREPORT='https://bugs.openmpt.org/' PACKAGE_URL='https://lib.openmpt.org/' @@ -1475,7 +1475,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libopenmpt 0.6.8+release.autotools to adapt to many kinds of systems. +\`configure' configures libopenmpt 0.6.9+release.autotools to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1546,7 +1546,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libopenmpt 0.6.8+release.autotools:";; + short | recursive ) echo "Configuration of libopenmpt 0.6.9+release.autotools:";; esac cat <<\_ACEOF @@ -1732,7 +1732,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libopenmpt configure 0.6.8+release.autotools +libopenmpt configure 0.6.9+release.autotools generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2222,7 +2222,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libopenmpt $as_me 0.6.8+release.autotools, which was +It was created by libopenmpt $as_me 0.6.9+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3086,7 +3086,7 @@ # Define the identity of the package. PACKAGE='libopenmpt' - VERSION='0.6.8+release.autotools' + VERSION='0.6.9+release.autotools' cat >>confdefs.h <<_ACEOF @@ -17253,13 +17253,13 @@ -$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.8\"" >>confdefs.h +$as_echo "#define MPT_SVNURL \"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.9\"" >>confdefs.h -$as_echo "#define MPT_SVNVERSION \"18680\"" >>confdefs.h +$as_echo "#define MPT_SVNVERSION \"18817\"" >>confdefs.h -$as_echo "#define MPT_SVNDATE \"2023-01-29T12:13:49.877060Z\"" >>confdefs.h +$as_echo "#define MPT_SVNDATE \"2023-03-05T12:41:15.297347Z\"" >>confdefs.h $as_echo "#define MPT_PACKAGE true" >>confdefs.h @@ -22545,7 +22545,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libopenmpt $as_me 0.6.8+release.autotools, which was +This file was extended by libopenmpt $as_me 0.6.9+release.autotools, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -22603,7 +22603,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -libopenmpt config.status 0.6.8+release.autotools +libopenmpt config.status 0.6.9+release.autotools configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru libopenmpt-0.6.8/configure.ac libopenmpt-0.6.9/configure.ac --- libopenmpt-0.6.8/configure.ac 2023-01-29 13:25:29.000000000 +0100 +++ libopenmpt-0.6.9/configure.ac 2023-03-05 13:49:07.000000000 +0100 @@ -1,4 +1,4 @@ -AC_INIT([libopenmpt], [0.6.8+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/]) +AC_INIT([libopenmpt], [0.6.9+release.autotools], [https://bugs.openmpt.org/], [libopenmpt], [https://lib.openmpt.org/]) AC_PREREQ([2.68]) AC_CONFIG_MACRO_DIR([m4]) @@ -26,9 +26,9 @@ AC_SUBST([LIBOPENMPT_LTVER_REVISION]) AC_SUBST([LIBOPENMPT_LTVER_AGE]) -AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.8"], [svn version]) -AC_DEFINE([MPT_SVNVERSION], ["18680"], [svn version]) -AC_DEFINE([MPT_SVNDATE], ["2023-01-29T12:13:49.877060Z"], [svn date]) +AC_DEFINE([MPT_SVNURL], ["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.6.9"], [svn version]) +AC_DEFINE([MPT_SVNVERSION], ["18817"], [svn version]) +AC_DEFINE([MPT_SVNDATE], ["2023-03-05T12:41:15.297347Z"], [svn date]) AC_DEFINE([MPT_PACKAGE], [true], [is package]) diff -Nru libopenmpt-0.6.8/debian/changelog libopenmpt-0.6.9/debian/changelog --- libopenmpt-0.6.8/debian/changelog 2023-02-02 09:28:27.000000000 +0100 +++ libopenmpt-0.6.9/debian/changelog 2023-03-09 23:30:20.000000000 +0100 @@ -1,3 +1,9 @@ +libopenmpt (0.6.9-1) unstable; urgency=medium + + * New upstream version 0.6.9 + + -- Sebastian Ramacher <sramac...@debian.org> Thu, 09 Mar 2023 23:30:20 +0100 + libopenmpt (0.6.8-1) unstable; urgency=medium * New upstream version 0.6.8 diff -Nru libopenmpt-0.6.8/libopenmpt/dox/changelog.md libopenmpt-0.6.9/libopenmpt/dox/changelog.md --- libopenmpt-0.6.8/libopenmpt/dox/changelog.md 2023-01-29 13:13:46.000000000 +0100 +++ libopenmpt-0.6.9/libopenmpt/dox/changelog.md 2023-03-05 13:41:12.000000000 +0100 @@ -5,6 +5,20 @@ For fully detailed change log, please see the source repository directly. This is just a high-level summary. +### libopenmpt 0.6.9 (2023-03-05) + + * [**Bug**] An exception could be thrown during rendering when trying to + access the release node of an empty envelope. + + * The fix for the OPL cutoff bug introduced in libopenmpt 0.6.7 was + incomplete. + * ULT: Offset commands exceeding 65535 samples were sometimes not imported + correctly even if there was room for them. + * After seeking with seek.sync_samples=1, the filter settings of playing notes + were not updated since libopenmpt 0.6.7. + * Loading of and seeking inside (malformed) modules with thousands of short + sub-songs has been sped up. + ### libopenmpt 0.6.8 (2023-01-29) * [**Bug**] DSYM: Loading DSYM files got broken in 0.6.7. diff -Nru libopenmpt-0.6.8/libopenmpt/libopenmpt_version.h libopenmpt-0.6.9/libopenmpt/libopenmpt_version.h --- libopenmpt-0.6.8/libopenmpt/libopenmpt_version.h 2023-01-29 13:13:46.000000000 +0100 +++ libopenmpt-0.6.9/libopenmpt/libopenmpt_version.h 2023-03-05 13:41:12.000000000 +0100 @@ -21,7 +21,7 @@ /*! \brief libopenmpt minor version number */ #define OPENMPT_API_VERSION_MINOR 6 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 8 +#define OPENMPT_API_VERSION_PATCH 9 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff -Nru libopenmpt-0.6.8/libopenmpt/libopenmpt_version.mk libopenmpt-0.6.9/libopenmpt/libopenmpt_version.mk --- libopenmpt-0.6.8/libopenmpt/libopenmpt_version.mk 2023-01-29 13:13:46.000000000 +0100 +++ libopenmpt-0.6.9/libopenmpt/libopenmpt_version.mk 2023-03-05 13:41:12.000000000 +0100 @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 LIBOPENMPT_VERSION_MINOR=6 -LIBOPENMPT_VERSION_PATCH=8 +LIBOPENMPT_VERSION_PATCH=9 LIBOPENMPT_VERSION_PREREL= LIBOPENMPT_LTVER_CURRENT=3 -LIBOPENMPT_LTVER_REVISION=8 +LIBOPENMPT_LTVER_REVISION=9 LIBOPENMPT_LTVER_AGE=3 diff -Nru libopenmpt-0.6.8/man/openmpt123.1 libopenmpt-0.6.9/man/openmpt123.1 --- libopenmpt-0.6.8/man/openmpt123.1 2023-01-29 13:25:28.000000000 +0100 +++ libopenmpt-0.6.9/man/openmpt123.1 2023-03-05 13:49:06.000000000 +0100 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH OPENMPT123 "1" "January 2023" "openmpt123 v0.6.8" "User Commands" +.TH OPENMPT123 "1" "March 2023" "openmpt123 v0.6.9" "User Commands" .SH NAME openmpt123 - command line module music player based on libopenmpt .SH SYNOPSIS diff -Nru libopenmpt-0.6.8/soundlib/Load_symmod.cpp libopenmpt-0.6.9/soundlib/Load_symmod.cpp --- libopenmpt-0.6.8/soundlib/Load_symmod.cpp 2022-02-14 09:52:01.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Load_symmod.cpp 2023-03-03 19:32:39.000000000 +0100 @@ -1135,7 +1135,7 @@ chunk = FileReader(mpt::as_span(unpackedSample)); } - if(!ReadIFFSample(sample, chunk) + if(!ReadIFFSample(sample, chunk, false) && !ReadWAVSample(sample, chunk) && !ReadAIFFSample(sample, chunk) && !ReadRawSymSample(Samples[sample], chunk)) diff -Nru libopenmpt-0.6.8/soundlib/Load_uax.cpp libopenmpt-0.6.9/soundlib/Load_uax.cpp --- libopenmpt-0.6.8/soundlib/Load_uax.cpp 2021-03-17 23:07:37.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Load_uax.cpp 2023-03-01 20:27:31.000000000 +0100 @@ -72,7 +72,7 @@ { InitializeChannels(); SetType(MOD_TYPE_MPT); - m_ContainerType = MOD_CONTAINERTYPE_UAX; + m_ContainerType = ModContainerType::UAX; m_nChannels = 4; Patterns.Insert(0, 64); Order().assign(1, 0); diff -Nru libopenmpt-0.6.8/soundlib/Load_ult.cpp libopenmpt-0.6.9/soundlib/Load_ult.cpp --- libopenmpt-0.6.8/soundlib/Load_ult.cpp 2021-10-30 15:38:01.000000000 +0200 +++ libopenmpt-0.6.9/soundlib/Load_ult.cpp 2023-02-25 15:51:57.000000000 +0100 @@ -239,7 +239,7 @@ { uint32 offset = param1 * 4; param1 = mpt::saturate_cast<uint8>(offset); - if(offset > 0xFF && ModCommand::GetEffectWeight(cmd2) < ModCommand::GetEffectType(CMD_OFFSET)) + if(offset > 0xFF && ModCommand::GetEffectWeight(cmd2) < ModCommand::GetEffectWeight(CMD_OFFSET)) { m.command = CMD_OFFSET; m.param = static_cast<ModCommand::PARAM>(offset); @@ -251,7 +251,7 @@ { uint32 offset = param2 * 4; param2 = mpt::saturate_cast<uint8>(offset); - if(offset > 0xFF && ModCommand::GetEffectWeight(cmd1) < ModCommand::GetEffectType(CMD_OFFSET)) + if(offset > 0xFF && ModCommand::GetEffectWeight(cmd1) < ModCommand::GetEffectWeight(CMD_OFFSET)) { m.command = CMD_OFFSET; m.param = static_cast<ModCommand::PARAM>(offset); diff -Nru libopenmpt-0.6.8/soundlib/Load_wav.cpp libopenmpt-0.6.9/soundlib/Load_wav.cpp --- libopenmpt-0.6.8/soundlib/Load_wav.cpp 2021-05-24 22:48:24.000000000 +0200 +++ libopenmpt-0.6.9/soundlib/Load_wav.cpp 2023-03-01 20:27:31.000000000 +0100 @@ -80,7 +80,7 @@ } InitializeGlobals(MOD_TYPE_MPT); - m_ContainerType = MOD_CONTAINERTYPE_WAV; + m_ContainerType = ModContainerType::WAV; m_nChannels = std::max(wavFile.GetNumChannels(), uint16(2)); Patterns.ResizeArray(2); if(!Patterns.Insert(0, 64) || !Patterns.Insert(1, 64)) diff -Nru libopenmpt-0.6.8/soundlib/MIDIMacros.cpp libopenmpt-0.6.9/soundlib/MIDIMacros.cpp --- libopenmpt-0.6.8/soundlib/MIDIMacros.cpp 2022-03-06 20:54:06.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/MIDIMacros.cpp 2023-03-02 19:09:40.000000000 +0100 @@ -341,7 +341,11 @@ // Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings. void MIDIMacroConfig::UpgradeMacros() { - for(auto ¯o : *this) + for(auto ¯o : SFx) + { + macro.UpgradeLegacyMacro(); + } + for(auto ¯o : Zxx) { macro.UpgradeLegacyMacro(); } diff -Nru libopenmpt-0.6.8/soundlib/modcommand.cpp libopenmpt-0.6.9/soundlib/modcommand.cpp --- libopenmpt-0.6.8/soundlib/modcommand.cpp 2023-01-13 21:55:59.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/modcommand.cpp 2023-02-25 13:00:27.000000000 +0100 @@ -557,7 +557,7 @@ { switch(command) { - case CMD_TONEPORTAVOL: // lacks memory -> 500 is the same as 300 + case CMD_TONEPORTAVOL: // lacks memory -> 500 is the same as 300 if(param == 0x00) command = CMD_TONEPORTAMENTO; break; @@ -574,7 +574,15 @@ command = CMD_NONE; break; - case CMD_MODCMDEX: // This would turn into "Set Active Macro", so let's better remove it + case CMD_MODCMDEX: + // No effect memory + if(param == 0x10 || param == 0x20 || param == 0xA0 || param == 0xB0) + command = CMD_NONE; + // This would turn into "Set Active Macro", so let's better remove it + if((param & 0xF0) == 0xF0) + command = CMD_NONE; + break; + case CMD_S3MCMDEX: if((param & 0xF0) == 0xF0) command = CMD_NONE; diff -Nru libopenmpt-0.6.8/soundlib/ModInstrument.cpp libopenmpt-0.6.9/soundlib/ModInstrument.cpp --- libopenmpt-0.6.8/soundlib/ModInstrument.cpp 2022-05-09 20:29:48.000000000 +0200 +++ libopenmpt-0.6.9/soundlib/ModInstrument.cpp 2023-03-02 18:55:38.000000000 +0100 @@ -70,6 +70,9 @@ // returns value in range [0, rangeOut]. int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int32 rangeIn) const { + if(empty()) + return 0; + uint32 pt = size() - 1u; const int32 ENV_PRECISION = 1 << 16; @@ -126,13 +129,20 @@ it->tick = std::max(it->tick, (it - 1)->tick); LimitMax(it->value, maxValue); } + LimitMax(nLoopEnd, static_cast<decltype(nLoopEnd)>(size() - 1)); + LimitMax(nLoopStart, nLoopEnd); + LimitMax(nSustainEnd, static_cast<decltype(nSustainEnd)>(size() - 1)); + LimitMax(nSustainStart, nSustainEnd); + if(nReleaseNode != ENV_RELEASE_NODE_UNSET) + LimitMax(nReleaseNode, static_cast<decltype(nReleaseNode)>(size() - 1)); + } else + { + nLoopStart = 0; + nLoopEnd = 0; + nSustainStart = 0; + nSustainEnd = 0; + nReleaseNode = ENV_RELEASE_NODE_UNSET; } - LimitMax(nLoopEnd, static_cast<decltype(nLoopEnd)>(size() - 1)); - LimitMax(nLoopStart, nLoopEnd); - LimitMax(nSustainEnd, static_cast<decltype(nSustainEnd)>(size() - 1)); - LimitMax(nSustainStart, nSustainEnd); - if(nReleaseNode != ENV_RELEASE_NODE_UNSET) - LimitMax(nReleaseNode, static_cast<decltype(nReleaseNode)>(size() - 1)); } @@ -312,7 +322,7 @@ { if(Keyboard[i] == 0) continue; - if(!NoteMap[i] || NoteMap[i] == (i + 1)) + if(NoteMap[i] == NOTE_NONE) continue; const int8 relativeNote = static_cast<int8>(NoteMap[i] - (i + NOTE_MIN)); @@ -320,6 +330,16 @@ return {}; transposeMap[Keyboard[i]] = relativeNote; } + // Remove all samples that wouldn't be transposed. + // They were previously inserted into the map to catch the case where a specific sample's + // map would start with a transpose value of 0 but end with a different value. + for(auto it = transposeMap.begin(); it != transposeMap.end();) + { + if(it->second == 0) + it = transposeMap.erase(it); + else + it++; + } return transposeMap; } diff -Nru libopenmpt-0.6.8/soundlib/plugins/PlugInterface.cpp libopenmpt-0.6.9/soundlib/plugins/PlugInterface.cpp --- libopenmpt-0.6.8/soundlib/plugins/PlugInterface.cpp 2022-09-07 02:30:11.000000000 +0200 +++ libopenmpt-0.6.9/soundlib/plugins/PlugInterface.cpp 2023-02-11 14:35:35.000000000 +0100 @@ -215,7 +215,7 @@ #ifdef MODPLUG_TRACKER if(m_SndFile.GetpModDoc()) - m_SndFile.GetpModDoc()->UpdateAllViews(nullptr, PluginHint(m_nSlot + 1).Info(), nullptr); + m_SndFile.GetpModDoc()->UpdateAllViews(PluginHint(m_nSlot + 1).Info()); #endif // MODPLUG_TRACKER } diff -Nru libopenmpt-0.6.8/soundlib/RowVisitor.cpp libopenmpt-0.6.9/soundlib/RowVisitor.cpp --- libopenmpt-0.6.8/soundlib/RowVisitor.cpp 2021-01-24 19:04:57.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/RowVisitor.cpp 2023-03-04 01:05:25.000000000 +0100 @@ -221,36 +221,45 @@ { const auto &order = Order(); const ORDERINDEX endOrder = order.GetLengthTailTrimmed(); - for(ord = 0; ord < endOrder; ord++) + for(ORDERINDEX o = 0; o < endOrder; o++) { - if(!order.IsValidPat(ord)) + if(!order.IsValidPat(o)) continue; - if(ord >= m_visitedRows.size()) + if(o >= m_visitedRows.size()) { // Not yet initialized => unvisited + ord = o; row = 0; return true; } - const auto &visitedRows = m_visitedRows[ord]; - const auto firstUnplayedRow = std::find(visitedRows.begin(), visitedRows.end(), onlyUnplayedPatterns); - if(onlyUnplayedPatterns && firstUnplayedRow == visitedRows.end()) + const auto &visitedRows = m_visitedRows[o]; + ROWINDEX firstUnplayedRow = 0; + for(; firstUnplayedRow < visitedRows.size(); firstUnplayedRow++) + { + if(visitedRows[firstUnplayedRow] == onlyUnplayedPatterns) + break; + } + if(onlyUnplayedPatterns && firstUnplayedRow == visitedRows.size()) { // No row of this pattern has been played yet. + ord = o; row = 0; return true; } else if(!onlyUnplayedPatterns) { // Return the first unplayed row in this pattern - if(firstUnplayedRow != visitedRows.end()) + if(firstUnplayedRow < visitedRows.size()) { - row = static_cast<ROWINDEX>(std::distance(visitedRows.begin(), firstUnplayedRow)); + ord = o; + row = firstUnplayedRow; return true; } - if(visitedRows.size() < m_sndFile.Patterns[order[ord]].GetNumRows()) + if(visitedRows.size() < m_sndFile.Patterns[order[o]].GetNumRows()) { // History is not fully initialized + ord = o; row = static_cast<ROWINDEX>(visitedRows.size()); return true; } diff -Nru libopenmpt-0.6.8/soundlib/SampleFormats.cpp libopenmpt-0.6.9/soundlib/SampleFormats.cpp --- libopenmpt-0.6.8/soundlib/SampleFormats.cpp 2022-11-25 23:48:33.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/SampleFormats.cpp 2023-03-03 22:16:07.000000000 +0100 @@ -2542,7 +2542,7 @@ MPT_BINARY_STRUCT(IFFSampleHeader, 20) -bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file) +bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file, bool allowLittleEndian) { file.Rewind(); @@ -2612,14 +2612,24 @@ const uint8 numChannels = chanChunk.ReadUint32BE() == 6 ? 2 : 1; const uint8 bytesPerFrame = bytesPerSample * numChannels; - // While this is an Amiga format, the 16SV version appears to be only used on PC, and only with little-endian sample data. if(bytesPerSample == 2) - sampleIO = SampleIO(SampleIO::_16bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); + sampleIO |= SampleIO::_16bit; if(numChannels == 2) sampleIO |= SampleIO::stereoSplit; loopStart = sampleHeader.oneShotHiSamples / bytesPerFrame; - loopLength = sampleHeader.repeatHiSamples / bytesPerFrame; + // Loops are a complicated mess in IFF samples. + // Some samples (e.g. those from the Ensoniq Mirage for Amiga collection available at https://archive.org/details/mirage4amiga) + // have a repeatHiSamples portion that includes garbage. However, these samples also have an appropriate samplesPerHiCycle value set + // which indicates the length of one cycle of the repeating waveform. If we just take that one cycle into account, the samples loop cleanly. + // However, some other, non-musical 8SVX samples use bogus samplesPerHiCycle values. The following conditions help us spot this sort of samples: + // - If samplesPerHiCycle is 32 or lower, we simply ignore it. + // - According to the documentation, repeatHiSamples is intended to be a multiple of samplesPerHiCycle if the latter is set (otherwise we wouldn't get a clean loop). + // So if this is not the case, we ignore samplesPerHiCycle and only use repeatHiSamples. + if(sampleHeader.samplesPerHiCycle > 32 && sampleHeader.samplesPerHiCycle < sampleHeader.repeatHiSamples && (sampleHeader.repeatHiSamples % sampleHeader.samplesPerHiCycle) == 0) + loopLength = sampleHeader.samplesPerHiCycle / bytesPerFrame; + else + loopLength = sampleHeader.repeatHiSamples / bytesPerFrame; sampleRate = sampleHeader.samplesPerSec; volume = sampleHeader.volume; numSamples = mpt::saturate_cast<SmpLength>(sampleData.GetLength() / bytesPerFrame); @@ -2635,7 +2645,7 @@ sample.uFlags.set(CHN_LOOP); sample.nC5Speed = sampleRate; - if(!sample.nC5Speed) + if(sample.nC5Speed <= 1) sample.nC5Speed = 22050; sample.nVolume = static_cast<uint16>(volume / 256); @@ -2651,6 +2661,59 @@ m_szNames[nSample] = ""; sampleIO.ReadSample(sample, sampleData); + + if(allowLittleEndian && !memcmp(fileHeader.magic, "16SV", 4)) + { + // Fasttracker 2 (and also Awave Studio up to version 11.7) writes little-endian 16-bit data. Great... + // It is relatively safe to assume (see raw sample import dialog) that "proper" sample data usually has some smoothness to it, + // i.e. its first derivative mostly consists of small-ish values. + // When interpreting the sample data with incorrect endianness, however, the first derivative is usually a lot more jumpy. + // So we compare the two derivatives when interpreting the data as little-endian and big-endian, + // and choose the waveform that has less jumps in it. + // Sample data is normalized for those comparisons, otherwise 8-bit data converted to 16-bit will almost always be more optimal + // when byte-swapped (as the upper byte is always 0). + const uint8 numChannels = sample.GetNumChannels(); + auto sample16 = mpt::as_span(sample.sample16(), sample.nLength * numChannels); + int32 minNative = int16_max, maxNative = int16_min, minSwapped = int16_max, maxSwapped = int16_min; + for(const auto vNative : sample16) + { + const int16 vSwapped = mpt::SwapBytesImpl(vNative); + if(vNative < minNative) + minNative = vNative; + if(vNative > maxNative) + maxNative = vNative; + if(vSwapped < minSwapped) + minSwapped = vSwapped; + if(vSwapped > maxSwapped) + maxSwapped = vSwapped; + } + + const int32 minMaxNative = std::max({int32(1), -minNative, maxNative}); + const int32 minMaxSwapped = std::max({int32(1), -minSwapped, maxSwapped}); + const double factorNative = 1.0 / minMaxNative, factorSwapped = 1.0 / minMaxSwapped; + double errorNative = 0.0, errorSwapped = 0.0; + for(uint8 chn = 0; chn < numChannels; chn++) + { + const int16 *vNative = sample.sample16() + chn; + int32 prev = 0; + for(SmpLength i = sample.nLength; i != 0; i--, vNative += numChannels) + { + const double diffNative = (*vNative - prev) * factorNative; + errorNative += diffNative * diffNative; + const double diffSwapped = (mpt::SwapBytesImpl(*vNative) - mpt::SwapBytesImpl(static_cast<int16>(prev))) * factorSwapped; + errorSwapped += diffSwapped * diffSwapped; + prev = *vNative; + } + } + if(errorNative > errorSwapped) + { + for(auto &v : sample16) + { + v = mpt::SwapBytesImpl(v); + } + } + } + sample.PrecomputeLoops(*this, false); return true; diff -Nru libopenmpt-0.6.8/soundlib/Snd_defs.h libopenmpt-0.6.9/soundlib/Snd_defs.h --- libopenmpt-0.6.8/soundlib/Snd_defs.h 2023-01-07 15:48:47.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Snd_defs.h 2023-03-01 20:27:31.000000000 +0100 @@ -105,15 +105,16 @@ DECLARE_FLAGSET(MODTYPE) -enum MODCONTAINERTYPE +enum class ModContainerType { - MOD_CONTAINERTYPE_NONE = 0x0, - MOD_CONTAINERTYPE_UMX = 0x3, - MOD_CONTAINERTYPE_XPK = 0x4, - MOD_CONTAINERTYPE_PP20 = 0x5, - MOD_CONTAINERTYPE_MMCMP= 0x6, - MOD_CONTAINERTYPE_WAV = 0x7, // WAV as module - MOD_CONTAINERTYPE_UAX = 0x8, // Unreal sample set as module + None, + UMX, + XPK, + PP20, + MMCMP, + WAV, // WAV as module + UAX, // Unreal sample set as module + Generic, // Generic CUnarchiver container }; diff -Nru libopenmpt-0.6.8/soundlib/Sndfile.cpp libopenmpt-0.6.9/soundlib/Sndfile.cpp --- libopenmpt-0.6.8/soundlib/Sndfile.cpp 2023-01-07 15:48:47.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Sndfile.cpp 2023-03-01 20:27:31.000000000 +0100 @@ -160,7 +160,7 @@ Instruments[i] = nullptr; } - m_ContainerType = MOD_CONTAINERTYPE_NONE; + m_ContainerType = ModContainerType::None; m_nChannels = 0; m_nInstruments = 0; m_nSamples = 0; @@ -405,6 +405,7 @@ { m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment())); } + m_ContainerType = ModContainerType::Generic; return true; } } @@ -420,17 +421,17 @@ if(file.IsValid()) { std::vector<ContainerItem> containerItems; - MODCONTAINERTYPE packedContainerType = MOD_CONTAINERTYPE_NONE; + ModContainerType packedContainerType = ModContainerType::None; if(!(loadFlags & skipContainer)) { ContainerLoadingFlags containerLoadFlags = (loadFlags == onlyVerifyHeader) ? ContainerOnlyVerifyHeader : ContainerUnwrapData; #if !defined(MPT_WITH_ANCIENT) - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackXPK(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_XPK; - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackPP20(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_PP20; - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackMMCMP(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_MMCMP; + if(packedContainerType == ModContainerType::None && UnpackXPK(containerItems, file, containerLoadFlags)) packedContainerType = ModContainerType::XPK; + if(packedContainerType == ModContainerType::None && UnpackPP20(containerItems, file, containerLoadFlags)) packedContainerType = ModContainerType::PP20; + if(packedContainerType == ModContainerType::None && UnpackMMCMP(containerItems, file, containerLoadFlags)) packedContainerType = ModContainerType::MMCMP; #endif // !MPT_WITH_ANCIENT - if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackUMX(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_UMX; - if(packedContainerType != MOD_CONTAINERTYPE_NONE) + if(packedContainerType == ModContainerType::None && UnpackUMX(containerItems, file, containerLoadFlags)) packedContainerType = ModContainerType::UMX; + if(packedContainerType != ModContainerType::None) { if(loadFlags == onlyVerifyHeader) { @@ -462,14 +463,14 @@ if(!loaderSuccess) { m_nType = MOD_TYPE_NONE; - m_ContainerType = MOD_CONTAINERTYPE_NONE; + m_ContainerType = ModContainerType::None; } if(loadFlags == onlyVerifyHeader) { return loaderSuccess; } - if(packedContainerType != MOD_CONTAINERTYPE_NONE && m_ContainerType == MOD_CONTAINERTYPE_NONE) + if(packedContainerType != ModContainerType::None && m_ContainerType == ModContainerType::None) { m_ContainerType = packedContainerType; } @@ -737,7 +738,7 @@ #endif // NO_PLUGINS m_nType = MOD_TYPE_NONE; - m_ContainerType = MOD_CONTAINERTYPE_NONE; + m_ContainerType = ModContainerType::None; m_nChannels = m_nSamples = m_nInstruments = 0; return true; } diff -Nru libopenmpt-0.6.8/soundlib/Sndfile.h libopenmpt-0.6.9/soundlib/Sndfile.h --- libopenmpt-0.6.8/soundlib/Sndfile.h 2023-01-04 22:27:07.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Sndfile.h 2023-03-03 19:32:39.000000000 +0100 @@ -457,7 +457,7 @@ #endif // MODPLUG_TRACKER Enum<MODTYPE> m_nType; private: - MODCONTAINERTYPE m_ContainerType = MOD_CONTAINERTYPE_NONE; + ModContainerType m_ContainerType = ModContainerType::None; public: CHANNELINDEX m_nChannels = 0; SAMPLEINDEX m_nSamples = 0; @@ -735,7 +735,7 @@ bool Destroy(); Enum<MODTYPE> GetType() const noexcept { return m_nType; } - MODCONTAINERTYPE GetContainerType() const noexcept { return m_ContainerType; } + ModContainerType GetContainerType() const noexcept { return m_ContainerType; } // rough heuristic, could be improved mpt::Charset GetCharsetFile() const // 8bit string encoding of strings in the on-disk file @@ -927,8 +927,8 @@ static std::vector<const char *> GetSupportedExtensions(bool otherFormats); static bool IsExtensionSupported(std::string_view ext); // UTF8, casing of ext is ignored - static mpt::ustring ModContainerTypeToString(MODCONTAINERTYPE containertype); - static mpt::ustring ModContainerTypeToTracker(MODCONTAINERTYPE containertype); + static mpt::ustring ModContainerTypeToString(ModContainerType containertype); + static mpt::ustring ModContainerTypeToTracker(ModContainerType containertype); // Repair non-standard stuff in modules saved with previous ModPlug versions void UpgradeModule(); @@ -1127,6 +1127,7 @@ void SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume); int SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier = 256) const; + int HandleNoteChangeFilter(ModChannel &chn) const; // Low-Level effect processing void DoFreqSlide(ModChannel &chn, int32 &period, int32 amount, bool isTonePorta = false) const; @@ -1189,7 +1190,7 @@ bool ReadXISample(SAMPLEINDEX nSample, FileReader &file); bool ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewind = true); bool ReadITISample(SAMPLEINDEX nSample, FileReader &file); - bool ReadIFFSample(SAMPLEINDEX sample, FileReader &file); + bool ReadIFFSample(SAMPLEINDEX sample, FileReader &file, bool allowLittleEndian = true); bool ReadBRRSample(SAMPLEINDEX sample, FileReader &file); bool ReadFLACSample(SAMPLEINDEX sample, FileReader &file); bool ReadOpusSample(SAMPLEINDEX sample, FileReader &file); diff -Nru libopenmpt-0.6.8/soundlib/Snd_fx.cpp libopenmpt-0.6.9/soundlib/Snd_fx.cpp --- libopenmpt-0.6.8/soundlib/Snd_fx.cpp 2023-01-12 09:15:47.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Snd_fx.cpp 2023-02-19 00:53:36.000000000 +0100 @@ -955,6 +955,7 @@ chn.nNewNote = chn.nLastNote; if(chn.nNewIns != 0) InstrumentChange(chn, chn.nNewIns, porta); NoteChange(chn, m.note, porta); + HandleNoteChangeFilter(chn); HandleDigiSamplePlayDirection(playState, nChn); memory.chnSettings[nChn].incChanged = true; diff -Nru libopenmpt-0.6.8/soundlib/Sndmix.cpp libopenmpt-0.6.9/soundlib/Sndmix.cpp --- libopenmpt-0.6.8/soundlib/Sndmix.cpp 2023-01-28 17:08:59.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Sndmix.cpp 2023-03-01 19:49:32.000000000 +0100 @@ -1985,6 +1985,44 @@ } +int CSoundFile::HandleNoteChangeFilter(ModChannel &chn) const +{ + int cutoff = -1; + if(!chn.triggerNote) + return cutoff; + + bool useFilter = !m_SongFlags[SONG_MPTFILTERMODE]; + if(const ModInstrument *pIns = chn.pModInstrument; pIns != nullptr) + { + if(pIns->IsResonanceEnabled()) + { + chn.nResonance = pIns->GetResonance(); + useFilter = true; + } + if(pIns->IsCutoffEnabled()) + { + chn.nCutOff = pIns->GetCutoff(); + useFilter = true; + } + if(useFilter && (pIns->filterMode != FilterMode::Unchanged)) + { + chn.nFilterMode = pIns->filterMode; + } + } else + { + chn.nVolSwing = chn.nPanSwing = 0; + chn.nCutSwing = chn.nResSwing = 0; + } + if((chn.nCutOff < 0x7F || m_playBehaviour[kITFilterBehaviour]) && useFilter) + { + cutoff = SetupChannelFilter(chn, true); + if(cutoff >= 0) + cutoff = chn.nCutOff / 2u; + } + return cutoff; +} + + // Returns channel increment and frequency with FREQ_FRACBITS fractional bits std::pair<SamplePosition, uint32> CSoundFile::GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const { @@ -2251,38 +2289,8 @@ } // Setup Initial Filter for this note - int cutoff = -1; - if(chn.triggerNote) - { - bool useFilter = !m_SongFlags[SONG_MPTFILTERMODE]; - if(pIns) - { - if(pIns->IsResonanceEnabled()) - { - chn.nResonance = pIns->GetResonance(); - useFilter = true; - } - if(pIns->IsCutoffEnabled()) - { - chn.nCutOff = pIns->GetCutoff(); - useFilter = true; - } - if(useFilter && (pIns->filterMode != FilterMode::Unchanged)) - { - chn.nFilterMode = pIns->filterMode; - } - } else - { - chn.nVolSwing = chn.nPanSwing = 0; - chn.nCutSwing = chn.nResSwing = 0; - } - if((chn.nCutOff < 0x7F || m_playBehaviour[kITFilterBehaviour]) && useFilter) - { - cutoff = SetupChannelFilter(chn, true); - if(cutoff >= 0) - cutoff = chn.nCutOff / 2u; - } - } + if(int cutoff = HandleNoteChangeFilter(chn); cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->Volume(nChn, static_cast<uint8>(cutoff), true); // Now that all relevant envelopes etc. have been processed, we can parse the MIDI macro data. ProcessMacroOnChannel(nChn); @@ -2291,14 +2299,11 @@ if(samplePlaying) { int envCutoff = ProcessPitchFilterEnvelope(chn, period); - if(envCutoff >= 0) - cutoff = envCutoff / 4; + // Cutoff doubles as modulator intensity for FM instruments + if(envCutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->Volume(nChn, static_cast<uint8>(envCutoff / 4), true); } - // Cutoff doubles as modulator intensity for FM instruments - if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl) - m_opl->Volume(nChn, static_cast<uint8>(cutoff), true); - if(chn.rowCommand.volcmd == VOLCMD_VIBRATODEPTH && (chn.rowCommand.command == CMD_VIBRATO || chn.rowCommand.command == CMD_VIBRATOVOL || chn.rowCommand.command == CMD_FINEVIBRATO)) { diff -Nru libopenmpt-0.6.8/soundlib/Tables.cpp libopenmpt-0.6.9/soundlib/Tables.cpp --- libopenmpt-0.6.8/soundlib/Tables.cpp 2021-11-23 22:44:07.000000000 +0100 +++ libopenmpt-0.6.9/soundlib/Tables.cpp 2023-03-01 20:27:31.000000000 +0100 @@ -117,7 +117,7 @@ struct ModContainerInfo { - MODCONTAINERTYPE format; // MOD_CONTAINERTYPE_XXXX + ModContainerType format; // ModContainerType::XXX const mpt::uchar *name; // "Unreal Music" const char *extension; // "umx" }; @@ -125,13 +125,14 @@ static constexpr ModContainerInfo modContainerInfo[] = { // Container formats - { MOD_CONTAINERTYPE_UMX, UL_("Unreal Music"), "umx" }, - { MOD_CONTAINERTYPE_XPK, UL_("XPK packed"), "xpk" }, - { MOD_CONTAINERTYPE_PP20, UL_("PowerPack PP20"), "ppm" }, - { MOD_CONTAINERTYPE_MMCMP, UL_("Music Module Compressor"), "mmcmp" }, + { ModContainerType::UMX, UL_("Unreal Music"), "umx" }, + { ModContainerType::XPK, UL_("XPK packed"), "xpk" }, + { ModContainerType::PP20, UL_("PowerPack PP20"), "ppm" }, + { ModContainerType::MMCMP, UL_("Music Module Compressor"), "mmcmp" }, #ifdef MODPLUG_TRACKER - { MOD_CONTAINERTYPE_WAV, UL_("Wave"), "wav" }, - { MOD_CONTAINERTYPE_UAX, UL_("Unreal Sounds"), "uax" }, + { ModContainerType::WAV, UL_("Wave"), "wav" }, + { ModContainerType::UAX, UL_("Unreal Sounds"), "uax" }, + { ModContainerType::Generic, UL_("Generic Archive"), "???" }, #endif }; @@ -214,7 +215,7 @@ } -mpt::ustring CSoundFile::ModContainerTypeToString(MODCONTAINERTYPE containertype) +mpt::ustring CSoundFile::ModContainerTypeToString(ModContainerType containertype) { for(const auto &containerInfo : modContainerInfo) { @@ -227,7 +228,7 @@ } -mpt::ustring CSoundFile::ModContainerTypeToTracker(MODCONTAINERTYPE containertype) +mpt::ustring CSoundFile::ModContainerTypeToTracker(ModContainerType containertype) { std::set<mpt::ustring> retvals; mpt::ustring retval; diff -Nru libopenmpt-0.6.8/src/mpt/base/alloc.hpp libopenmpt-0.6.9/src/mpt/base/alloc.hpp --- libopenmpt-0.6.8/src/mpt/base/alloc.hpp 2022-09-25 18:55:57.000000000 +0200 +++ libopenmpt-0.6.9/src/mpt/base/alloc.hpp 2023-02-11 19:48:52.000000000 +0100 @@ -123,7 +123,7 @@ // casts between vector<->string of byte-castable types template <typename Tdst, typename Tsrc> -inline Tdst buffer_cast(Tsrc src) { +inline Tdst buffer_cast(const Tsrc & src) { return buffer_cast_impl<Tdst, Tsrc>()(src); } diff -Nru libopenmpt-0.6.8/src/mpt/base/detect_compiler.hpp libopenmpt-0.6.9/src/mpt/base/detect_compiler.hpp --- libopenmpt-0.6.8/src/mpt/base/detect_compiler.hpp 2022-11-09 14:40:27.000000000 +0100 +++ libopenmpt-0.6.9/src/mpt/base/detect_compiler.hpp 2023-02-22 10:36:59.000000000 +0100 @@ -50,7 +50,9 @@ #elif defined(_MSC_VER) #define MPT_COMPILER_MSVC 1 -#if (_MSC_VER >= 1934) +#if (_MSC_VER >= 1935) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 5) +#elif (_MSC_VER >= 1934) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 4) #elif (_MSC_VER >= 1933) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 3)