Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libopenmpt for openSUSE:Factory 
checked in at 2026-08-25 13:18:14
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libopenmpt (Old)
 and      /work/SRC/openSUSE:Factory/.libopenmpt.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libopenmpt"

Tue Aug 25 13:18:14 2026 rev:60 rq:1373139 version:0.8.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/libopenmpt/libopenmpt.changes    2026-05-20 
16:48:18.225633492 +0200
+++ /work/SRC/openSUSE:Factory/.libopenmpt.new.1258/libopenmpt.changes  
2026-08-25 13:18:19.515227557 +0200
@@ -1,0 +2,22 @@
+Sat Aug 22 20:03:14 UTC 2026 - Mia Herkt <[email protected]>
+
+- Update to version 0.8.9:
+  * [Sec] Possible heap out-of-bounds write when loading SymMOD
+    files containing WAV IMA ADPCM samples.
+    See also 
https://github.com/OpenMPT/openmpt/security/advisories/GHSA-fxf7-wc37-p2cx
+  * [Sec] Possible heap out-of-bounds read when loading custom
+    tunings from MPTM files.
+- Charges in version 0.8.8:
+  * IT: Due to an Impulse Tracker bug in Compatible Gxx mode,
+    Envelope Carry may not resume the envelope from the correct
+    position when there is both an instrument number and tone
+    portamento next to a note.
+  * XM: NitroTracker ignores instrument numbers where there is no
+    note next to them, so they are no longer imported. Fixes
+    various NitroTracker-made XMs such as notominous-a19.xm.
+  * STK: Loosen heuristics a bit to allow STK.CRB-GreatMuzaxs6 to
+    load.
+  * GT2: Loading file versions 6 and later was broken since
+    libopenmpt 0.8.0.
+
+-------------------------------------------------------------------

Old:
----
  libopenmpt-0.8.7+release.autotools.tar.gz

New:
----
  libopenmpt-0.8.9+release.autotools.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libopenmpt.spec ++++++
--- /var/tmp/diff_new_pack.s5gf0J/_old  2026-08-25 13:18:20.234252890 +0200
+++ /var/tmp/diff_new_pack.s5gf0J/_new  2026-08-25 13:18:20.236252960 +0200
@@ -21,7 +21,7 @@
 %define libopenmpt_modplug_version 0.8.9.0
 
 Name:           libopenmpt
-Version:        0.8.7
+Version:        0.8.9
 Release:        0
 Summary:        C++ and C library to decode tracker music files
 License:        BSD-3-Clause

++++++ libopenmpt-0.8.7+release.autotools.tar.gz -> 
libopenmpt-0.8.9+release.autotools.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/common/versionNumber.h 
new/libopenmpt-0.8.9+release.autotools/common/versionNumber.h
--- old/libopenmpt-0.8.7+release.autotools/common/versionNumber.h       
2026-05-17 12:54:41.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/common/versionNumber.h       
2026-08-18 23:11:45.000000000 +0200
@@ -15,5 +15,5 @@
 // Version definitions. The only thing that needs to be changed when changing 
version number.
 #define VER_MAJORMAJOR  1
 #define VER_MAJOR      32
-#define VER_MINOR      10
-#define VER_MINORMINOR 00
+#define VER_MINOR      11
+#define VER_MINORMINOR 02
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libopenmpt-0.8.7+release.autotools/configure 
new/libopenmpt-0.8.9+release.autotools/configure
--- old/libopenmpt-0.8.7+release.autotools/configure    2026-05-17 
14:06:38.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/configure    2026-08-19 
11:21:40.000000000 +0200
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for libopenmpt 0.8.7+release.autotools.
+# Generated by GNU Autoconf 2.71 for libopenmpt 0.8.9+release.autotools.
 #
 # Report bugs to <https://bugs.openmpt.org/>.
 #
@@ -621,8 +621,8 @@
 # Identity of this package.
 PACKAGE_NAME='libopenmpt'
 PACKAGE_TARNAME='libopenmpt'
-PACKAGE_VERSION='0.8.7+release.autotools'
-PACKAGE_STRING='libopenmpt 0.8.7+release.autotools'
+PACKAGE_VERSION='0.8.9+release.autotools'
+PACKAGE_STRING='libopenmpt 0.8.9+release.autotools'
 PACKAGE_BUGREPORT='https://bugs.openmpt.org/'
 PACKAGE_URL='https://lib.openmpt.org/'
 
@@ -1514,7 +1514,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.8.7+release.autotools to adapt to many 
kinds of systems.
+\`configure' configures libopenmpt 0.8.9+release.autotools to adapt to many 
kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1585,7 +1585,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of libopenmpt 
0.8.7+release.autotools:";;
+     short | recursive ) echo "Configuration of libopenmpt 
0.8.9+release.autotools:";;
    esac
   cat <<\_ACEOF
 
@@ -1772,7 +1772,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-libopenmpt configure 0.8.7+release.autotools
+libopenmpt configure 0.8.9+release.autotools
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2152,7 +2152,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.8.7+release.autotools, which was
+It was created by libopenmpt $as_me 0.8.9+release.autotools, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3648,7 +3648,7 @@
 
 # Define the identity of the package.
  PACKAGE='libopenmpt'
- VERSION='0.8.7+release.autotools'
+ VERSION='0.8.9+release.autotools'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -23188,13 +23188,13 @@
 
 
 
-printf "%s\n" "#define MPT_SVNURL 
\"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.8.7\""; >>confdefs.h
+printf "%s\n" "#define MPT_SVNURL 
\"https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.8.9\""; >>confdefs.h
 
 
-printf "%s\n" "#define MPT_SVNVERSION \"25325\"" >>confdefs.h
+printf "%s\n" "#define MPT_SVNVERSION \"25652\"" >>confdefs.h
 
 
-printf "%s\n" "#define MPT_SVNDATE \"2026-05-17T11:08:12.701337Z\"" 
>>confdefs.h
+printf "%s\n" "#define MPT_SVNDATE \"2026-08-19T07:15:57.388610Z\"" 
>>confdefs.h
 
 
 printf "%s\n" "#define MPT_PACKAGE true" >>confdefs.h
@@ -27168,7 +27168,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.8.7+release.autotools, which was
+This file was extended by libopenmpt $as_me 0.8.9+release.autotools, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -27228,7 +27228,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-libopenmpt config.status 0.8.7+release.autotools
+libopenmpt config.status 0.8.9+release.autotools
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libopenmpt-0.8.7+release.autotools/configure.ac 
new/libopenmpt-0.8.9+release.autotools/configure.ac
--- old/libopenmpt-0.8.7+release.autotools/configure.ac 2026-05-17 
14:06:09.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/configure.ac 2026-08-19 
11:21:10.000000000 +0200
@@ -1,4 +1,4 @@
-AC_INIT([libopenmpt], [0.8.7+release.autotools], [https://bugs.openmpt.org/], 
[libopenmpt], [https://lib.openmpt.org/])
+AC_INIT([libopenmpt], [0.8.9+release.autotools], [https://bugs.openmpt.org/], 
[libopenmpt], [https://lib.openmpt.org/])
 AC_PREREQ([2.69])
 # we do want 2.70, but distributions are not ready yet
 #AC_PREREQ([2.70])
@@ -88,9 +88,9 @@
 AC_SUBST([LIBOPENMPT_LTVER_REVISION])
 AC_SUBST([LIBOPENMPT_LTVER_AGE])
 
-AC_DEFINE([MPT_SVNURL], 
["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.8.7";], [svn version])
-AC_DEFINE([MPT_SVNVERSION], ["25325"], [svn version])
-AC_DEFINE([MPT_SVNDATE], ["2026-05-17T11:08:12.701337Z"], [svn date])
+AC_DEFINE([MPT_SVNURL], 
["https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.8.9";], [svn version])
+AC_DEFINE([MPT_SVNVERSION], ["25652"], [svn version])
+AC_DEFINE([MPT_SVNDATE], ["2026-08-19T07:15:57.388610Z"], [svn date])
 AC_DEFINE([MPT_PACKAGE], [true], [is package])
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/doc/libopenmpt/changelog.md 
new/libopenmpt-0.8.9+release.autotools/doc/libopenmpt/changelog.md
--- old/libopenmpt-0.8.7+release.autotools/doc/libopenmpt/changelog.md  
2026-05-17 13:08:10.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/doc/libopenmpt/changelog.md  
2026-08-19 09:15:55.000000000 +0200
@@ -5,6 +5,30 @@
 For fully detailed change log, please see the source repository directly. This
 is just a high-level summary.
 
+### libopenmpt 0.8.9 (2026-08-19)
+
+ *  [**Sec**] Possible heap out-of-bounds write when loading SymMOD files
+    containing WAV IMA ADPCM samples (r25623, r25645). See also
+    
<https://github.com/OpenMPT/openmpt/security/advisories/GHSA-fxf7-wc37-p2cx>
+    . Found by [Tristan Madani](https://github.com/TristanInSec).
+ *  [**Sec**] Possible heap out-of-bounds read when loading custom tunings from
+    MPTM files (r25609, r25615).
+
+### libopenmpt 0.8.8 (2026-08-15)
+
+ *  IT: Due to an Impulse Tracker bug in Compatible Gxx mode, Envelope Carry 
may
+    not resume the envelope from the correct position when there is both an
+    instrument number and tone portamento next to a note.
+ *  XM: NitroTracker ignores instrument numbers where there is no note next to
+    them, so they are no longer imported. Fixes various NitroTracker-made XMs
+    such as notominous-a19.xm.
+ *  STK: Loosen heuristics a bit to allow STK.CRB-GreatMuzaxs6 to load.
+ *  GT2: Loading file versions 6 and later was broken since libopenmpt 0.8.0.
+
+ *  mpg123: Update to v1.33.7 (2026-08-03).
+ *  miniz: Update to v3.1.2 (2026-07-01).
+ *  pugixml: Update to v1.16 (2026-06-16).
+
 ### libopenmpt 0.8.7 (2026-05-17)
 
  *  ULT: Sustain loops were stopped after a portamento.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/libopenmpt/libopenmpt_ext_impl.cpp 
new/libopenmpt-0.8.9+release.autotools/libopenmpt/libopenmpt_ext_impl.cpp
--- old/libopenmpt-0.8.7+release.autotools/libopenmpt/libopenmpt_ext_impl.cpp   
2024-11-19 19:26:05.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/libopenmpt/libopenmpt_ext_impl.cpp   
2026-07-12 16:13:40.000000000 +0200
@@ -270,7 +270,7 @@
                chn.nMasterChn = 0;     // remove NNA association
                chn.nNewNote = chn.nLastNote = static_cast<std::uint8_t>(note);
                chn.ResetEnvelopes();
-               m_sndFile->InstrumentChange(chn, instrument + 1);
+               m_sndFile->InstrumentChange(m_sndFile->m_PlayState, 
free_channel, instrument + 1);
                chn.nFadeOutVol = 0x10000;
                m_sndFile->NoteChange(chn, note, false, true, true);
                chn.nPan = mpt::saturate_round<std::int32_t>( OpenMPT::Clamp( 
panning * 128.0, -128.0, 128.0 ) + 128.0 );
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/libopenmpt/libopenmpt_version.h 
new/libopenmpt-0.8.9+release.autotools/libopenmpt/libopenmpt_version.h
--- old/libopenmpt-0.8.7+release.autotools/libopenmpt/libopenmpt_version.h      
2026-05-17 13:08:10.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/libopenmpt/libopenmpt_version.h      
2026-08-19 09:15:55.000000000 +0200
@@ -21,7 +21,7 @@
 /*! \brief libopenmpt minor version number */
 #define OPENMPT_API_VERSION_MINOR 8
 /*! \brief libopenmpt patch version number */
-#define OPENMPT_API_VERSION_PATCH 7
+#define OPENMPT_API_VERSION_PATCH 9
 /*! \brief libopenmpt pre-release tag */
 #define OPENMPT_API_VERSION_PREREL ""
 /*! \brief libopenmpt pre-release flag */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/libopenmpt/libopenmpt_version.mk 
new/libopenmpt-0.8.9+release.autotools/libopenmpt/libopenmpt_version.mk
--- old/libopenmpt-0.8.7+release.autotools/libopenmpt/libopenmpt_version.mk     
2026-05-17 13:08:10.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/libopenmpt/libopenmpt_version.mk     
2026-08-19 09:15:55.000000000 +0200
@@ -1,8 +1,8 @@
 LIBOPENMPT_VERSION_MAJOR=0
 LIBOPENMPT_VERSION_MINOR=8
-LIBOPENMPT_VERSION_PATCH=7
+LIBOPENMPT_VERSION_PATCH=9
 LIBOPENMPT_VERSION_PREREL=
 
 LIBOPENMPT_LTVER_CURRENT=5
-LIBOPENMPT_LTVER_REVISION=7
+LIBOPENMPT_LTVER_REVISION=9
 LIBOPENMPT_LTVER_AGE=5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libopenmpt-0.8.7+release.autotools/man/openmpt123.1 
new/libopenmpt-0.8.9+release.autotools/man/openmpt123.1
--- old/libopenmpt-0.8.7+release.autotools/man/openmpt123.1     2026-05-17 
14:06:07.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/man/openmpt123.1     2026-08-19 
11:21:08.000000000 +0200
@@ -1,12 +1,12 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.49.3.
-.TH OPENMPT123 "1" "May 2026" "openmpt123 v0.8.7" "User Commands"
+.TH OPENMPT123 "1" "August 2026" "openmpt123 v0.8.9" "User Commands"
 .SH NAME
 openmpt123 - command line module music player based on libopenmpt
 .SH SYNOPSIS
 .B openmpt123
 [\fI\,options\/\fR] [\fI\,--\/\fR] \fI\,file1 \/\fR[\fI\,file2\/\fR] ...
 .SH DESCRIPTION
-openmpt123 v0.8.7, libopenmpt 0.8.7+r25325 (OpenMPT 1.32.10.00 
https://source.openmpt.org/svn/openmpt/tags/libopenmpt\-0.8.7@25325 
(2026\-05\-17T11:08:12.701337Z) clean)
+openmpt123 v0.8.9, libopenmpt 0.8.9+r25652 (OpenMPT 1.32.11.02\-r25652 
https://source.openmpt.org/svn/openmpt/tags/libopenmpt\-0.8.9@25652 
(2026\-08\-19T07:15:57.388610Z) clean)
 Copyright \(co 2013\-2026 OpenMPT Project Developers and Contributors 
<https://lib.openmpt.org/>
 .PP
 openmpt123 plays module music files.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Load_gt2.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Load_gt2.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Load_gt2.cpp        
2024-11-01 21:00:41.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Load_gt2.cpp        
2026-08-01 11:19:11.000000000 +0200
@@ -1181,7 +1181,9 @@
                return true;
 
        std::vector<uint16be> pannedTracks;
-       file.ReadVector(pannedTracks, fileHeader.numPannedTracks);
+       if(fileHeader.fileVersion <= 5)
+               file.ReadVector(pannedTracks, fileHeader.numPannedTracks);
+       file.Seek(fileHeader.headerSize);
 
        ChunkReader chunkFile(file);
        auto chunks = chunkFile.ReadChunksUntil<GT2Chunk>(1, GT2Chunk::idENDC);
@@ -1230,7 +1232,6 @@
                        ChnSettings[chn].nPan = 
std::min(static_cast<uint16>(Util::muldivr_unsigned(pannedTracks[chn], 256, 
4095)), uint16(256));
                }
        }
-       file.Seek(fileHeader.headerSize);
 
        if(auto chunk = chunks.GetChunk(GT2Chunk::idSONG); chunk.CanRead(2))
        {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Load_it.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Load_it.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Load_it.cpp 2025-09-09 
19:24:42.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Load_it.cpp 2026-07-13 
20:27:14.000000000 +0200
@@ -1317,6 +1317,7 @@
                                        {SchismVersionFromDate<2024,  5, 
27>::date, kITOffsetWithInstrNumber      },  // 
https://github.com/schismtracker/schismtracker/commit/9237960d45079a54ad73f87bacfe5dd8ae82e273
                                        {SchismVersionFromDate<2024, 10, 
13>::date, kITDoublePortamentoSlides     },  // 
https://github.com/schismtracker/schismtracker/commit/223e327d9448561931b8cac8a55180286b17276c
                                        {SchismVersionFromDate<2025,  1,  
8>::date, kITCarryAfterNoteOff          },  // 
https://github.com/schismtracker/schismtracker/commit/ff7a817df327c8f13d97b8c6546a9329f59edff8
+                                       {SchismVersionFromDate<2026,  7, 
13>::date, kITCompatGxxCarryPortaWithIns },  // 
https://github.com/schismtracker/schismtracker/commit/21bbd9a74df8266efe10c1c7c8aaccfc7512ede5
                                };
                                for(const auto &quirk : SchismQuirks)
                                {
@@ -1351,6 +1352,15 @@
                        else
                                madeWithTracker = UL_("Unknown");
                        break;
+               case 0x0E:
+                       if(fileHeader.cwtv == 0xEFFF)
+                               madeWithTracker = UL_("rosetracker");
+                       else
+                               madeWithTracker = UL_("Unknown");
+                       break;
+               default:
+                       madeWithTracker = UL_("Unknown");
+                       break;
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Load_mdl.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Load_mdl.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Load_mdl.cpp        
2024-07-14 01:44:35.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Load_mdl.cpp        
2026-08-16 22:27:21.000000000 +0200
@@ -644,7 +644,7 @@
                                if(sampleHeader.smpNum == 0 || 
sampleHeader.smpNum > GetNumSamples())
                                        continue;
 
-                               LimitMax(sampleHeader.lastNote, 
static_cast<uint8>(std::size(mptIns->Keyboard)));
+                               LimitMax(sampleHeader.lastNote, 
static_cast<uint8>(std::size(mptIns->Keyboard) - 1));
                                for(uint8 n = firstNote; n <= 
sampleHeader.lastNote; n++)
                                {
                                        mptIns->Keyboard[n] = 
sampleHeader.smpNum;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Load_stk.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Load_stk.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Load_stk.cpp        
2024-11-27 23:18:46.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Load_stk.cpp        
2026-07-12 17:23:25.000000000 +0200
@@ -52,7 +52,7 @@
        SmpLength totalSampleLen = 0;
        uint8 allVolumes = 0;
        uint8 validNameCount = 0;
-       bool invalidNames = false;
+       uint8 invalidNames = 0;
 
        for(SAMPLEINDEX smp = 0; smp < 15; smp++)
        {
@@ -66,7 +66,7 @@
                if(const auto nameType = ClassifyName(sampleHeader.name); 
nameType == NameClassification::ValidASCII)
                        validNameCount++;
                else if(nameType == NameClassification::Invalid)
-                       invalidNames = true;
+                       invalidNames++;
 
                // Sanity checks - invalid character count adjusted for ata.mod 
(MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 
3322ca62258adb9e0ae8e9afe6e0c29d39add874)
                // Sample length adjusted for romantic.stk which has a (valid) 
sample of length 72222
@@ -82,7 +82,8 @@
        }
 
        // scramble_2.mod has a lot of garbage in the song title, but it has 
lots of properly-formatted sample names, so we consider those to be more 
important than the garbage bytes.
-       if(invalidCharsInTitle > 5 && (validNameCount < 4 || invalidNames))
+       // STK.CRB-GreatMuzaxs6 also has a garbage title and first sample name, 
so we consider one invalid sample name to be okay.
+       if(invalidCharsInTitle > 5 && (validNameCount < 4 || invalidNames > 1))
                return false;
 
        // Reject any files with no (or only silent) samples at all, as this 
might just be a random binary file (e.g. ID3 tags with tons of padding)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Load_xm.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Load_xm.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Load_xm.cpp 2025-01-01 
00:28:24.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Load_xm.cpp 2026-07-14 
18:20:44.000000000 +0200
@@ -239,6 +239,8 @@
 // Read .XM patterns
 static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, 
CSoundFile &sndFile)
 {
+       const bool isNitroTracker = !memcmp(fileHeader.trackerName, 
"NitroTracker", 13);
+
        // Reading patterns
        sndFile.Patterns.ResizeArray(fileHeader.patterns);
        for(PATTERNINDEX pat = 0; pat < fileHeader.patterns; pat++)
@@ -327,6 +329,10 @@
                        if(m.instr == 0xFF)
                        {
                                m.instr = 0;
+                       } else if(isNitroTracker && m.instr && !m.IsNote())
+                       {
+                               // NitroTracker ignores instrument numbers 
unless there's a note next to them
+                               m.instr = 0;
                        }
 
                        if(vol >= 0x10 && vol <= 0x50)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/PlayState.h 
new/libopenmpt-0.8.9+release.autotools/soundlib/PlayState.h
--- old/libopenmpt-0.8.7+release.autotools/soundlib/PlayState.h 2026-03-28 
00:59:47.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/PlayState.h 2026-07-12 
16:10:37.000000000 +0200
@@ -60,6 +60,8 @@
        ORDERINDEX m_nSeqOverride = ORDERINDEX_INVALID;  // Queued order to be 
processed next, regardless of what order would normally follow
        OrderTransitionMode m_seqOverrideMode = 
OrderTransitionMode::AtPatternEnd;
 
+       CHANNELINDEX m_lastMovedChannel = CHANNELINDEX_INVALID;  // IT compat 
Gxx + carry + porta bug emulation
+
        // Global volume
 public:
        int32 m_nGlobalVolume = MAX_GLOBAL_VOLUME;  // Current global volume 
(0...MAX_GLOBAL_VOLUME)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/PlaybackTest.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/PlaybackTest.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/PlaybackTest.cpp    
2025-02-16 08:40:28.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/PlaybackTest.cpp    
2026-08-12 08:25:13.000000000 +0200
@@ -273,7 +273,6 @@
                return result;
        }
 #if MPT_GCC_AT_LEAST(12, 0, 0) && MPT_GCC_BEFORE(13, 1, 0)
-#pragma GCC diagnostic pop
 #pragma GCC pop_options
 #endif
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/SampleFormatFLAC.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/SampleFormatFLAC.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/SampleFormatFLAC.cpp        
2025-06-29 22:12:39.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/SampleFormatFLAC.cpp        
2026-08-18 13:55:04.000000000 +0200
@@ -151,7 +151,6 @@
                // Source bit depth
                const unsigned int bps = frame->header.bits_per_sample;
 
-               MPT_ASSERT((bps <= 8 && sample.GetElementarySampleSize() == 1) 
|| (bps > 8 && sample.GetElementarySampleSize() == 2));
                MPT_ASSERT(modChannels <= 
FLAC__stream_decoder_get_channels(decoder));
                MPT_ASSERT(bps == 
FLAC__stream_decoder_get_bits_per_sample(decoder));
                MPT_UNREFERENCED_PARAMETER(decoder); // decoder is unused if 
ASSERTs are compiled out
@@ -159,22 +158,82 @@
                // Do the sample conversion
                for(uint8 chn = 0; chn < modChannels; chn++)
                {
-                       if(bps <= 8)
+                       if(sample.GetElementarySampleSize() == 1)
                        {
                                int8 *sampleData8 = sample.sample8() + offset;
-                               
CopySample<SC::ConversionChain<SC::ConvertShift< int8, int32,  0>, 
SC::DecodeIdentity<int32> > >(sampleData8  + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1);
-                       } else if(bps <= 16)
+                               switch(bps)
+                               {
+                                       case  1: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int8, int32, 7>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  2: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int8, int32, 6>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  3: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int8, int32, 5>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  4: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int8, int32, 4>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  5: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int8, int32, 3>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  6: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int8, int32, 2>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  7: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int8, int32, 1>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  8: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 0>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  9: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  1>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 10: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  2>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 11: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  3>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 12: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  4>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 13: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  5>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 14: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  6>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 15: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  7>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 16: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  8>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 17: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32,  9>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 18: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 10>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 19: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 11>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 20: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 12>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 21: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 13>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 22: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 14>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 23: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 15>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 24: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 16>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 25: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 17>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 26: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 18>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 27: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 19>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 28: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 20>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 29: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 21>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 30: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 22>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 31: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 23>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 32: 
CopySample<SC::ConversionChain<SC::ConvertShift<int8, int32, 24>, 
SC::DecodeIdentity<int32>>>(sampleData8 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                               }
+                       } else if(sample.GetElementarySampleSize() == 2)
                        {
                                int16 *sampleData16 = sample.sample16() + 
offset;
-                               
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  0>, 
SC::DecodeIdentity<int32> > >(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1);
-                       } else if(bps <= 24)
-                       {
-                               int16 *sampleData16 = sample.sample16() + 
offset;
-                               
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  8>, 
SC::DecodeIdentity<int32> > >(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1);
-                       } else if(bps <= 32)
-                       {
-                               int16 *sampleData16 = sample.sample16() + 
offset;
-                               
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 16>, 
SC::DecodeIdentity<int32> > >(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1);
+                               switch(bps)
+                               {
+                                       case  1: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32, 15>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  2: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32, 14>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  3: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32, 13>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  4: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32, 12>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  5: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32, 11>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  6: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32, 10>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  7: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  9>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  8: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  8>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case  9: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  7>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 10: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  6>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 11: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  5>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 12: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  4>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 13: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  3>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 14: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  2>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 15: 
CopySample<SC::ConversionChain<SC::ConvertShiftUp<int16, int32,  1>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 16: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 0>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 17: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  1>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 18: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  2>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 19: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  3>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 20: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  4>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 21: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  5>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 22: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  6>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 23: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  7>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 24: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  8>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 25: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32,  9>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 26: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 10>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 27: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 11>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 28: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 12>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 29: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 13>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 30: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 14>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 31: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 15>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                                       case 32: 
CopySample<SC::ConversionChain<SC::ConvertShift<int16, int32, 16>, 
SC::DecodeIdentity<int32>>>(sampleData16 + chn, copySamples, modChannels, 
buffer[chn], srcSize, 1); break;
+                               }
                        }
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/SampleFormats.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/SampleFormats.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/SampleFormats.cpp   
2025-06-06 00:15:38.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/SampleFormats.cpp   
2026-08-19 07:36:54.000000000 +0200
@@ -354,11 +354,34 @@
 ////////////////////////////////////////////////////////////////////////
 // IMA ADPCM Support for WAV files
 
+struct IMAADPCM_BlockHeader
+{
+       int16le value;
+       int8le index;
+       int8le reserved;
+};
+
+MPT_BINARY_STRUCT(IMAADPCM_BlockHeader, 4)
+
+struct IMAADPCM_Chunk
+{
+       uint8 data[4];
+};
+
+MPT_BINARY_STRUCT(IMAADPCM_Chunk, 4)
 
 static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader 
file, uint16 blockAlign, uint32 numChannels)
 {
-       static constexpr int8 IMAIndexTab[8] =  { -1, -1, -1, -1, 2, 4, 6, 8 };
-       static constexpr int16 IMAUnpackTable[90] =
+
+       // reference code from 
<https://www.cs.columbia.edu/~hgs/audio/dvi/IMA_ADPCM.pdf>
+
+       // v reference code
+       static constexpr int8 indexTable[16] = 
+       {
+               -1, -1, -1, -1,  2,  4,  6,  8,
+               -1, -1, -1, -1,  2,  4,  6,  8
+       };
+       static constexpr int16 stepsizeTable[89] =
        {
                7,     8,     9,     10,    11,    12,    13,    14,
                16,    17,    19,    21,    23,    25,    28,    31,
@@ -371,60 +394,118 @@
                3327,  3660,  4026,  4428,  4871,  5358,  5894,  6484,
                7132,  7845,  8630,  9493,  10442, 11487, 12635, 13899,
                15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
-               32767, 0
+               32767
        };
+       // ^ reference code
 
-       if(target == nullptr || blockAlign < 4u * numChannels)
+       if(!target)
+       {
                return false;
-
-       SmpLength samplePos = 0;
+       }
+       if(blockAlign < (sizeof(IMAADPCM_BlockHeader) * numChannels))
+       {
+               return false;
+       }
+       // stricter rejection of bogus blockAlign
+       /*
+       if(((blockAlign - (sizeof(IMAADPCM_BlockHeader) * numChannels)) % 
(sizeof(IMAADPCM_Chunk) * numChannels)) != 0)
+       {
+               return false;
+       }
+       */
        sampleLen *= numChannels;
-       while(file.CanRead(4u * numChannels) && samplePos < sampleLen)
+       static constexpr SmpLength samplesPerChunk = 8;
+       // round up expected chunks, assume zero data for short blocks
+       const uint16 chunksPerChannelPerBlock = 
static_cast<uint16>(mpt::align_up(blockAlign - (sizeof(IMAADPCM_BlockHeader) * 
numChannels), sizeof(IMAADPCM_Chunk) * numChannels) / (sizeof(IMAADPCM_Chunk) * 
numChannels));
+       SmpLength samplePos = 0;
+       while(file.CanRead(sizeof(IMAADPCM_BlockHeader) * numChannels))
        {
                FileReader block = file.ReadChunk(blockAlign);
-               FileReader::PinnedView blockView = block.GetPinnedView();
-               const std::byte *data = blockView.data();
-               const uint32 blockSize = static_cast<uint32>(blockView.size());
-
-               for(uint32 chn = 0; chn < numChannels; chn++)
+               FileReader blockChunks = 
block.GetChunkAt(sizeof(IMAADPCM_BlockHeader) * numChannels, 
chunksPerChannelPerBlock * numChannels * sizeof(IMAADPCM_Chunk));
+               FileReader::PinnedView blockChunksView = 
blockChunks.GetPinnedView();
+               MemoryFileReader chunks{blockChunksView.GetSpan()};
+               for(uint32 channel = 0; channel < numChannels; ++channel)
                {
-                       // Block header
-                       int32 value = block.ReadInt16LE();
-                       int32 nIndex = block.ReadUint8();
-                       Limit(nIndex, 0, 89);
-                       block.Skip(1);
-
-                       SmpLength smpPos = samplePos + chn;
-                       uint32 dataPos = (numChannels + chn) * 4;
-                       // Block data
-                       while(smpPos <= (sampleLen - 8) && dataPos <= 
(blockSize - 4))
+                       block.Seek(sizeof(IMAADPCM_BlockHeader) * channel);
+                       IMAADPCM_BlockHeader header;
+                       block.ReadStruct(header);
+
+                       // v reference code
+                       int32 predictedSample = header.value;
+                       int32 index = header.index;
+                       //int32 stepsize = 7;  // wrong
+                       // ^ reference code
+                       index = std::clamp(index, static_cast<int32>(0), 
static_cast<int32>(88));
+                       int32 stepsize = stepsizeTable[index];
+
+                       // skip writing output after given sample length
+                       if(samplePos < sampleLen)
+                       {
+                               target[samplePos] = 
static_cast<int16>(predictedSample);
+                       }
+                       samplePos += numChannels;
+
+                       for(uint16 chunk = 0; chunk < chunksPerChannelPerBlock; 
++chunk)
                        {
-                               for(uint32 i = 0; i < 8; i++)
+                               IMAADPCM_Chunk chunkdata;
+                               // assume zero data for short blocks
+                               if(chunks.Seek((sizeof(IMAADPCM_Chunk) * 
numChannels * chunk) + (channel * sizeof(IMAADPCM_Chunk))))
+                               {
+                                       chunks.ReadStructPartial(chunkdata);
+                               } else
                                {
-                                       uint8 delta = 
mpt::byte_cast<uint8>(data[dataPos]);
-                                       if(i & 1)
+                                       mpt::memclear(chunkdata);
+                               }
+                               // always decode the complete chunk, even if 
the output only has space for a partial chunk at the end of the sample
+                               for(SmpLength chunkSample = 0; chunkSample < 
samplesPerChunk; ++chunkSample)
+                               {
+                                       const int32 originalSample = 
(chunkdata.data[chunkSample >> 1u] >> ((chunkSample & 1u) * 4u)) & 0x0fu;
+                                       int32 newSample = predictedSample;
+
+                                       // v reference code
+                                       int32 difference = 0;
+                                       if(originalSample & 4)
                                        {
-                                               delta >>= 4;
-                                               dataPos++;
-                                       } else
+                                               difference += stepsize;
+                                       }
+                                       if(originalSample & 2)
+                                       {
+                                               difference += stepsize >> 1;
+                                       }
+                                       if(originalSample & 1)
+                                       {
+                                               difference += stepsize >> 2;
+                                       }
+                                       difference += stepsize >> 3;
+                                       if(originalSample & 8)
                                        {
-                                               delta &= 0x0F;
+                                               difference = -difference;
                                        }
-                                       int32 v = IMAUnpackTable[nIndex] >> 3;
-                                       if (delta & 1) v += 
IMAUnpackTable[nIndex] >> 2;
-                                       if (delta & 2) v += 
IMAUnpackTable[nIndex] >> 1;
-                                       if (delta & 4) v += 
IMAUnpackTable[nIndex];
-                                       if (delta & 8) value -= v; else value 
+= v;
-                                       nIndex += IMAIndexTab[delta & 7];
-                                       Limit(nIndex, 0, 88);
-                                       Limit(value, -32768, 32767);
-                                       target[smpPos] = 
static_cast<int16>(value);
-                                       smpPos += numChannels;
+                                       newSample += difference;
+                                       newSample = std::clamp(newSample, 
static_cast<int32>(-32768), static_cast<int32>(32767));
+                                       // ^ reference code
+
+                                       // skip writing output after given 
sample length
+                                       predictedSample = newSample;
+                                       if(samplePos < sampleLen)
+                                       {
+                                               target[samplePos] = 
static_cast<int16>(predictedSample);
+                                       }
+
+                                       // v reference code
+                                       index += indexTable[originalSample];
+                                       index = std::clamp(index, 
static_cast<int32>(0), static_cast<int32>(88));
+                                       stepsize = stepsizeTable[index];
+                                       // ^ reference code
+
+                                       samplePos += numChannels;
                                }
-                               dataPos += (numChannels - 1) * 4u;
                        }
+                       samplePos -= ((samplesPerChunk * 
chunksPerChannelPerBlock) + 1) * numChannels;
+                       samplePos += 1;
                }
-               samplePos += ((blockSize - (numChannels * 4u)) * 2u);
+               samplePos -= numChannels;
+               samplePos += ((samplesPerChunk * chunksPerChannelPerBlock) + 1) 
* numChannels;
        }
 
        return true;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Snd_defs.h 
new/libopenmpt-0.8.9+release.autotools/soundlib/Snd_defs.h
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Snd_defs.h  2025-08-31 
17:10:41.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Snd_defs.h  2026-07-12 
16:10:37.000000000 +0200
@@ -620,6 +620,7 @@
        kITNoteCutWithPorta,            // Note Cut (SCx) resets note frequency 
and interacts with tone portamento with row delay
        kITVolColNoSlidePropagation,    // Don't propagate volume command c/d 
parameter to regular command D memory
        kITStoppedFilterEnvAtStart,     // Stopped filter envelope is still 
applied even when its first tick has not been processed yet
+       kITCompatGxxCarryPortaWithIns,  // Envelope Carry quirk in Compatible 
Gxx mode with portamento and instrument number
 
        // Add new play behaviours here.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Snd_fx.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Snd_fx.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Snd_fx.cpp  2026-02-10 
22:32:08.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Snd_fx.cpp  2026-07-12 
16:10:37.000000000 +0200
@@ -95,6 +95,7 @@
                state->m_ppqPosFract = 0.0;
                state->m_ppqPosBeat = 0;
                state->m_nGlobalVolume = sndFile.m_nDefaultGlobalVolume;
+               state->m_lastMovedChannel = CHANNELINDEX_INVALID;
                state->m_globalScriptState.Initialize(sndFile);
                chnSettings.assign(sndFile.GetNumChannels(), {});
                const auto muteFlag = CSoundFile::GetChannelMuteFlag();
@@ -1102,7 +1103,7 @@
                                                chn.increment = 
GetChannelIncrement(chn, chn.nPeriod, 0).first;
                                        }
                                        int32 setPan = chn.nPan;
-                                       if(chn.nNewIns != 0) 
InstrumentChange(chn, chn.nNewIns, porta);
+                                       if(chn.nNewIns != 0) 
InstrumentChange(playState, nChn, chn.nNewIns, porta);
                                        NoteChange(chn, m.note, porta);
                                        HandleNoteChangeFilter(chn);
                                        
HandleDigiSamplePlayDirection(playState, nChn);
@@ -1459,8 +1460,9 @@
 // Effects
 
 // Change sample or instrument number.
-void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, 
bool bUpdVol, bool bResetEnv) const
+void CSoundFile::InstrumentChange(PlayState &playState, CHANNELINDEX channel, 
uint32 instr, bool bPorta, bool bUpdVol, bool bResetEnv) const
 {
+       ModChannel &chn = playState.Chn[channel];
        const ModInstrument *pIns = instr <= GetNumInstruments() ? 
Instruments[instr] : nullptr;
        const ModSample *pSmp = &Samples[instr <= GetNumSamples() ? instr : 0];
        const auto oldInsVol = chn.nInsVol;
@@ -1668,9 +1670,34 @@
                                        chn.ResetEnvelopes();
                                } else
                                {
-                                       if(!pIns->VolEnv.dwFlags[ENV_CARRY]) 
chn.VolEnv.Reset();
-                                       if(!pIns->PanEnv.dwFlags[ENV_CARRY]) 
chn.PanEnv.Reset();
-                                       if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) 
chn.PitchEnv.Reset();
+                                       // IT Compatibility: In Compatible Gxx 
mode, Envelope Carry mode has a
+                                       // really terrible quirk where, if a 
note has both an instrument number
+                                       // and tone portamento, it does not 
resume envelopes where the previously
+                                       // playing note left off:
+                                       // Internally, IT keeps track of the 
last allocated NNA channel in a
+                                       // global variable, which is then used 
to get the old envelope information
+                                       // from.
+                                       // However in this particular case 
(porta + instrument number), the last
+                                       // NNA channel memory is not updated. 
Due to this, the 'last' channel may
+                                       // actually be some earlier, already 
stopped note from the same channel, or
+                                       // it might be a completely different 
pattern channel altogether. Nasty!
+                                       // Test case: 
CarryCompatGxxPortaWithIns.it
+                                       const bool compatGxxCarryReset = 
m_playBehaviour[kITCompatGxxCarryPortaWithIns] && bPorta && 
m_SongFlags[SONG_ITCOMPATGXX];
+                                       const ModChannel *lastChn = 
playState.m_lastMovedChannel < playState.Chn.size() ? 
&playState.Chn[playState.m_lastMovedChannel] : nullptr;
+                                       if(!pIns->VolEnv.dwFlags[ENV_CARRY])
+                                               chn.VolEnv.Reset();
+                                       else if(compatGxxCarryReset)
+                                               chn.VolEnv.nEnvPosition = 
lastChn ? lastChn->VolEnv.nEnvPosition : 0;
+
+                                       if(!pIns->PanEnv.dwFlags[ENV_CARRY])
+                                               chn.PanEnv.Reset();
+                                       else if(compatGxxCarryReset)
+                                               chn.PanEnv.nEnvPosition = 
lastChn ? lastChn->PanEnv.nEnvPosition : 0;
+
+                                       if(!pIns->PitchEnv.dwFlags[ENV_CARRY])
+                                               chn.PitchEnv.Reset();
+                                       else if(compatGxxCarryReset)
+                                               chn.PitchEnv.nEnvPosition = 
lastChn ? lastChn->PitchEnv.nEnvPosition : 0;
                                }
                        }
 
@@ -1997,7 +2024,7 @@
                chn.nResSwing = chn.nCutSwing = 0;
                if(pIns)
                {
-                       // IT Compatiblity: NNA is reset on every note change, 
not every instrument change (fixes spx-farspacedance.it).
+                       // IT Compatibility: NNA is reset on every note change, 
not every instrument change (fixes spx-farspacedance.it).
                        if(m_playBehaviour[kITNNAReset]) chn.nNNA = pIns->nNNA;
 
                        if(!pIns->VolEnv.dwFlags[ENV_CARRY]) chn.VolEnv.Reset();
@@ -2333,6 +2360,8 @@
                }
 
                const CHANNELINDEX nnaChn = GetNNAChannel(nChn);
+               if(m_PlayState.m_lastMovedChannel == nChn)
+                       m_PlayState.m_lastMovedChannel = nnaChn;
                if(nnaChn == CHANNELINDEX_INVALID)
                        return CHANNELINDEX_INVALID;
                ModChannel &chn = m_PlayState.Chn[nnaChn];
@@ -2485,11 +2514,15 @@
                }
        }
 
+       m_PlayState.m_lastMovedChannel = CHANNELINDEX_INVALID;
+
        // New Note Action
        if(!srcChn.IsSamplePlaying() && !applyNNAtoPlug)
                return CHANNELINDEX_INVALID;
 
        const CHANNELINDEX nnaChn = GetNNAChannel(nChn);
+       if(srcChn.pModInstrument == pIns)
+               m_PlayState.m_lastMovedChannel = nnaChn;
 
 #ifndef NO_PLUGINS
        if(applyNNAtoPlug)
@@ -3087,7 +3120,7 @@
                                const ModSample *oldSample = chn.pModSample;
                                //const ModInstrument *oldInstrument = 
chn.pModInstrument;
 
-                               InstrumentChange(chn, instr, bPorta, true);
+                               InstrumentChange(m_PlayState, nChn, instr, 
bPorta, true);
 
                                if(!chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE] && 
chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl)
                                {
@@ -3152,7 +3185,7 @@
                                        
if(m_playBehaviour[kITEmptyNoteMapSlotIgnoreCell] && 
ModCommand::IsNote(previousNewNote))
                                                chn.nNewNote = previousNewNote;
 
-                                       InstrumentChange(chn, chn.nNewIns, 
bPorta, chn.pModSample == nullptr && chn.pModInstrument == nullptr, !(GetType() 
& (MOD_TYPE_XM|MOD_TYPE_MT2)));
+                                       InstrumentChange(m_PlayState, nChn, 
chn.nNewIns, bPorta, chn.pModSample == nullptr && chn.pModInstrument == 
nullptr, !(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)));
                                        chn.nNewNote = note;
                                        chn.swapSampleIndex = chn.nNewIns = 0;
                                }
@@ -5975,7 +6008,7 @@
                {
                        if(chn.rowCommand.instr && param < 0x100)
                        {
-                               InstrumentChange(chn, chn.rowCommand.instr, 
false, false);
+                               InstrumentChange(m_PlayState, nChn, 
chn.rowCommand.instr, false, false);
                                resetEnv = true;
                        }
                        if(param < 0x100)
@@ -5986,7 +6019,7 @@
                if(m_playBehaviour[kMODSampleSwap] && chn.rowCommand.instr)
                {
                        auto oldFineTune = chn.nFineTune;
-                       InstrumentChange(chn, chn.rowCommand.instr, false, 
false);
+                       InstrumentChange(m_PlayState, nChn, 
chn.rowCommand.instr, false, false);
                        chn.nFineTune = oldFineTune;
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Sndfile.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Sndfile.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Sndfile.cpp 2025-11-15 
15:03:20.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Sndfile.cpp 2026-07-12 
16:10:37.000000000 +0200
@@ -937,6 +937,7 @@
        m_PlayState.m_lTotalSampleCount = 0;
        m_PlayState.m_ppqPosFract = 0.0;
        m_PlayState.m_ppqPosBeat = 0;
+       m_PlayState.m_lastMovedChannel = CHANNELINDEX_INVALID;
        m_PlayState.m_globalScriptState.Initialize(*this);
 }
 
@@ -1232,6 +1233,7 @@
                playBehaviour.set(kITNoteCutWithPorta);
                playBehaviour.set(kITVolColNoSlidePropagation);
                playBehaviour.set(kITStoppedFilterEnvAtStart);
+               playBehaviour.set(kITCompatGxxCarryPortaWithIns);
                break;
 
        case MOD_TYPE_XM:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Sndfile.h 
new/libopenmpt-0.8.9+release.autotools/soundlib/Sndfile.h
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Sndfile.h   2025-07-12 
23:07:46.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Sndfile.h   2026-07-12 
16:10:37.000000000 +0200
@@ -1024,7 +1024,7 @@
        CHANNELINDEX CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, bool 
forceCut);
        void StopOldNNA(ModChannel &chn, CHANNELINDEX channel);
        void NoteChange(ModChannel &chn, int note, bool bPorta = false, bool 
bResetEnv = true, bool bManual = false, CHANNELINDEX channelHint = 
CHANNELINDEX_INVALID) const;
-       void InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta = 
false, bool bUpdVol = true, bool bResetEnv = true) const;
+       void InstrumentChange(PlayState &playState, CHANNELINDEX channel, 
uint32 instr, bool bPorta = false, bool bUpdVol = true, bool bResetEnv = true) 
const;
        void ApplyInstrumentPanning(ModChannel &chn, const ModInstrument 
*instr, const ModSample *smp) const;
        uint32 CalculateXParam(PATTERNINDEX pat, ROWINDEX row, CHANNELINDEX 
chn, uint32 *extendedRows = nullptr) const;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/Sndmix.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/Sndmix.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/Sndmix.cpp  2026-01-26 
22:15:33.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/Sndmix.cpp  2026-07-12 
16:10:37.000000000 +0200
@@ -2176,14 +2176,6 @@
        for(CHANNELINDEX nChn = 0; nChn < m_PlayState.Chn.size(); nChn++)
        {
                ModChannel &chn = m_PlayState.Chn[nChn];
-               // FT2 Compatibility: Prevent notes to be stopped after a 
fadeout. This way, a portamento effect can pick up a faded instrument which is 
long enough.
-               // This occurs for example in the bassline (channel 11) of 
jt_burn.xm. I hope this won't break anything else...
-               // I also suppose this could decrease mixing performance a bit, 
but hey, which CPU can't handle 32 muted channels these days... :-)
-               if(chn.dwFlags[CHN_NOTEFADE] && 
(!(chn.nFadeOutVol|chn.leftVol|chn.rightVol)) && 
!m_playBehaviour[kFT2ProcessSilentChannels])
-               {
-                       chn.nLength = 0;
-                       chn.nROfs = chn.nLOfs = 0;
-               }
                // Increment age of NNA channels
                if(chn.nMasterChn && nChn < GetNumChannels() && 
chn.nnaChannelAge < Util::MaxValueOfType(chn.nnaChannelAge))
                        chn.nnaChannelAge++;
@@ -2477,6 +2469,18 @@
                        IncrementEnvelopePositions(chn);
                }
 
+               // FT2 Compatibility: Prevent notes to be stopped after a 
fadeout. This way, a portamento effect can pick up a faded instrument which is 
long enough.
+               // This occurs for example in the bassline (channel 11) of 
jt_burn.xm.
+               // Test case: FadeoutPickup.xm
+               // NB: This check used to be right at the top of the loop, but 
was moved down here to match the channel stop behaviour
+               // of Impulse Tracker for the kITCompatGxxCarryPortaWithIns 
play behaviour.
+               // Test case: CarryCompatGxxPortaWithIns.it
+               if(chn.dwFlags[CHN_NOTEFADE] && (!(chn.nFadeOutVol | 
chn.leftVol | chn.rightVol)) && !m_playBehaviour[kFT2ProcessSilentChannels])
+               {
+                       chn.nLength = 0;
+                       chn.nROfs = chn.nLOfs = 0;
+               }
+
                // Volume ramping
                chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol 
| chn.leftVol) != 0 && !chn.dwFlags[CHN_ADLIB]);
 
@@ -2601,6 +2605,12 @@
                        {
                                m_PlayState.ChnMix[m_nMixChannels++] = nChn;
                        }
+
+                       // Stop NNA channel processing if the channel is 
effectively muted.
+                       // Required for Carry Envelope quirk to stop processing 
envelopes of affected channels.
+                       // Test case: CarryCompatGxxPortaWithIns.it
+                       if(nChn >= GetNumChannels() && !(chn.nVolume && 
chn.nGlobalVol && chn.nInsVol))
+                               chn.nLength = 0;
                } else
                {
                        chn.rightVol = chn.leftVol = 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/UpgradeModule.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/UpgradeModule.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/UpgradeModule.cpp   
2025-10-17 13:43:13.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/UpgradeModule.cpp   
2026-07-12 16:10:37.000000000 +0200
@@ -606,6 +606,7 @@
                        { kITNoteCutWithPorta,            MPT_V("1.32.01.02") },
                        { kITVolColNoSlidePropagation,    MPT_V("1.32.02.03") },
                        { kITStoppedFilterEnvAtStart,     MPT_V("1.32.03.04") },
+                       { kITCompatGxxCarryPortaWithIns,  MPT_V("1.32.10.02") },
                };
 
                for(const auto &b : behaviours)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/WAVTools.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/WAVTools.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/WAVTools.cpp        
2025-03-17 21:24:16.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/WAVTools.cpp        
2026-08-18 23:03:51.000000000 +0200
@@ -107,6 +107,25 @@
        // "fact" chunk should contain sample length of compressed samples.
        sampleLength = chunks.GetChunk(RIFFChunk::idfact).ReadUint32LE();
 
+       if((formatInfo.format == WAVFormatChunk::fmtIMA_ADPCM) && 
(GetBlockAlign() > 0))
+       {
+               FileReader::pos_type dataLength = sampleData.GetLength();
+               std::size_t blocks = 
mpt::align_up<FileReader::pos_type>(dataLength, GetBlockAlign()) / 
GetBlockAlign();
+               std::size_t framesPerBlock = ((GetBlockAlign() - (4 * 
formatInfo.numChannels)) * 2 / formatInfo.numChannels) + 1;
+               FileReader::pos_type expectedLengthFrames = blocks * 
framesPerBlock;
+               if(sampleLength == 0)
+               {
+                       sampleLength = expectedLengthFrames;
+               } else if((sampleLength > 0) && (formatInfo.numChannels == 2))
+               {
+                       // libsndfile divides the sample length by 2 for stereo 
samples (it gets confused about frames vs samples)
+                       if((sampleLength * 2) <= expectedLengthFrames)
+                       {
+                               sampleLength *= 2;
+                       }
+               }
+       }
+
        if((formatInfo.format != WAVFormatChunk::fmtIMA_ADPCM || sampleLength 
== 0) && GetSampleSize() != 0)
        {
                if((GetBlockAlign() == 0) || (GetBlockAlign() / 
GetNumChannels() >= 2 * GetSampleSize()))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/soundlib/tuning.cpp 
new/libopenmpt-0.8.9+release.autotools/soundlib/tuning.cpp
--- old/libopenmpt-0.8.7+release.autotools/soundlib/tuning.cpp  2025-03-29 
07:32:10.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/soundlib/tuning.cpp  2026-08-18 
22:46:00.000000000 +0200
@@ -809,14 +809,17 @@
        {
                // 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<NOTEINDEXTYPE>(m_RatioTable.size()) >= 
m_GroupSize - m_NoteMin)
+               NOTEINDEXTYPE baseNote = 0;
+               baseNote = std::clamp(baseNote, GetNoteRange().first, 
GetNoteRange().last);
+               if(IsValidNote(baseNote) && 
IsValidNote(mpt::saturate_cast<NOTEINDEXTYPE>(baseNote + 
static_cast<NOTEINDEXTYPE>(GetGroupSize()) - 1)))
                {
                        std::vector<RATIOTYPE> ratios;
-                       for(NOTEINDEXTYPE n = 0; n < m_GroupSize; ++n)
+                       for(NOTEINDEXTYPE note = baseNote; note < m_GroupSize + 
baseNote; ++note)
                        {
-                               ratios.push_back(m_RatioTable[n - m_NoteMin]);
+                               ratios.push_back(GetRatio(note));
                        }
-                       CreateGroupGeometric(ratios, m_GroupRatio, 
GetNoteRange(), 0);
+                       // ignore return value, keep as geometric if conversion 
fails
+                       CreateGroupGeometric(ratios, m_GroupRatio, 
GetNoteRange(), baseNote);
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/src/mpt/base/detect_libc.hpp 
new/libopenmpt-0.8.9+release.autotools/src/mpt/base/detect_libc.hpp
--- old/libopenmpt-0.8.7+release.autotools/src/mpt/base/detect_libc.hpp 
2024-12-31 09:57:00.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/src/mpt/base/detect_libc.hpp 
2026-08-12 10:53:28.000000000 +0200
@@ -9,6 +9,16 @@
 #include "mpt/base/detect_os.hpp"
 
 #include <cstddef>
+#include <cstdint>
+// Work-around MSYS2/CLANG64 bug:
+// With Clang, including <stddef.h> includes only the generic
+// Clang-provided header which does not result in trnansitively including
+// the MinGW-specific version which would ultimately include <_mingw.h>.
+// However, <_mingw.h> is required to properly identify the version of the
+// C Runtime (i.e. if undefined, it defines _UCRT, __MSVCRT_VERSION__, or
+// __CRTDLL__ and related macros, which are necessary for proper detection
+// of the C library).
+#include <cstdlib>
 
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/src/mpt/base/math.hpp 
new/libopenmpt-0.8.9+release.autotools/src/mpt/base/math.hpp
--- old/libopenmpt-0.8.7+release.autotools/src/mpt/base/math.hpp        
2024-12-18 16:07:45.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/src/mpt/base/math.hpp        
2026-05-20 18:32:04.000000000 +0200
@@ -13,6 +13,10 @@
 
 #include <cmath>
 
+#if MPT_OS_DJGPP
+#include <math.h>
+#endif // MPT_OS_DJGPP
+
 
 
 namespace mpt {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/src/mpt/check/libc.hpp 
new/libopenmpt-0.8.9+release.autotools/src/mpt/check/libc.hpp
--- old/libopenmpt-0.8.7+release.autotools/src/mpt/check/libc.hpp       
2025-01-02 23:12:01.000000000 +0100
+++ new/libopenmpt-0.8.9+release.autotools/src/mpt/check/libc.hpp       
2026-08-11 19:40:02.000000000 +0200
@@ -11,10 +11,14 @@
 #ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_MTRT
 #if MPT_PLATFORM_MULTITHREADED
 #if MPT_LIBC_MINGW
+#if MPT_LIBC_MS_UCRT
+// nothing
+#else /* MPT_LIBC_MS_MSVCRT || MPT_LIBC_MS_CRTDLL */
 // MinGW only has `#define _MT` in header files instead of `#define _MT 1`.
 #if !defined(_MT)
 MPT_WARNING("C stdlib is not multi-threaded.")
 #endif
+#endif
 #elif MPT_LIBC_MS
 #if defined(_MT)
 #if (_MT != 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/src/mpt/io_read/filedata_base.hpp 
new/libopenmpt-0.8.9+release.autotools/src/mpt/io_read/filedata_base.hpp
--- old/libopenmpt-0.8.7+release.autotools/src/mpt/io_read/filedata_base.hpp    
2024-07-24 11:11:45.000000000 +0200
+++ new/libopenmpt-0.8.9+release.autotools/src/mpt/io_read/filedata_base.hpp    
2026-08-12 08:34:24.000000000 +0200
@@ -55,6 +55,14 @@
 };
 
 
+
+#if MPT_GCC_AT_LEAST(16, 0, 0) && MPT_GCC_BEFORE(17, 1, 0)
+// Work-around confused GCC 16 optimizer which generates bogus -Warray-bounds 
in multiple call chains and member functions.
+#pragma GCC push_options
+#if defined(__OPTIMIZE__)
+#pragma GCC optimize("O1")
+#endif
+#endif
 class FileDataWindow : public IFileData {
 private:
        std::shared_ptr<const IFileData> data;
@@ -104,6 +112,9 @@
                return std::min(length, dataLength - pos);
        }
 };
+#if MPT_GCC_AT_LEAST(16, 0, 0) && MPT_GCC_BEFORE(17, 1, 0)
+#pragma GCC pop_options
+#endif
 
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libopenmpt-0.8.7+release.autotools/src/openmpt/soundbase/SampleConvert.hpp 
new/libopenmpt-0.8.9+release.autotools/src/openmpt/soundbase/SampleConvert.hpp
--- 
old/libopenmpt-0.8.7+release.autotools/src/openmpt/soundbase/SampleConvert.hpp  
    2026-04-25 08:54:48.000000000 +0200
+++ 
new/libopenmpt-0.8.9+release.autotools/src/openmpt/soundbase/SampleConvert.hpp  
    2026-05-20 17:12:41.000000000 +0200
@@ -172,7 +172,7 @@
        using output_t = uint8;
        MPT_FORCEINLINE output_t operator()(input_t val)
        {
-               val = std::clamp(val, -1.0, 1.0);
+               val = mpt::safe_clamp(val, -1.0, 1.0);
                val *= 128.0;
                return 
static_cast<uint8>(mpt::saturate_cast<int8>(static_cast<int>(SC::fastround(val)))
 + 0x80);
        }
@@ -253,7 +253,7 @@
        using output_t = int8;
        MPT_FORCEINLINE output_t operator()(input_t val)
        {
-               val = std::clamp(val, -1.0, 1.0);
+               val = mpt::safe_clamp(val, -1.0, 1.0);
                val *= 128.0;
                return 
mpt::saturate_cast<int8>(static_cast<int>(SC::fastround(val)));
        }
@@ -334,7 +334,7 @@
        using output_t = int16;
        MPT_FORCEINLINE output_t operator()(input_t val)
        {
-               val = std::clamp(val, -1.0, 1.0);
+               val = mpt::safe_clamp(val, -1.0, 1.0);
                val *= 32768.0;
                return 
mpt::saturate_cast<int16>(static_cast<int>(SC::fastround(val)));
        }
@@ -415,7 +415,7 @@
        using output_t = int24;
        MPT_FORCEINLINE output_t operator()(input_t val)
        {
-               val = std::clamp(val, -1.0, 1.0);
+               val = mpt::safe_clamp(val, -1.0, 1.0);
                val *= 2147483648.0;
                return 
static_cast<int24>(mpt::rshift_signed(mpt::saturate_cast<int32>(static_cast<int64>(SC::fastround(val))),
 8));
        }
@@ -496,7 +496,7 @@
        using output_t = int32;
        MPT_FORCEINLINE output_t operator()(input_t val)
        {
-               val = std::clamp(val, -1.0, 1.0);
+               val = mpt::safe_clamp(val, -1.0, 1.0);
                val *= 2147483648.0;
                return 
mpt::saturate_cast<int32>(static_cast<int64>(SC::fastround(val)));
        }
@@ -577,7 +577,7 @@
        using output_t = int64;
        MPT_FORCEINLINE output_t operator()(input_t val)
        {
-               val = std::clamp(val, -1.0, 1.0);
+               val = mpt::safe_clamp(val, -1.0, 1.0);
                val *= static_cast<double>(uint64(1) << 63);
                return mpt::saturate_trunc<int64>(SC::fastround(val));
        }

Reply via email to