Index: build/vs2017win7/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2017win7/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2017win7/OpenMPT-ANSI.vcxproj (working copy)
@@ -898,6 +898,9 @@
+
+
+
@@ -1363,6 +1366,8 @@
+
+
@@ -1370,7 +1375,6 @@
-
Index: build/vs2017win7/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2017win7/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2017win7/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017win7/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2017win7/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2017win7/OpenMPT-UTF8.vcxproj (working copy)
@@ -898,6 +898,9 @@
+
+
+
@@ -1363,6 +1366,8 @@
+
+
@@ -1370,7 +1375,6 @@
-
Index: build/vs2017win7/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2017win7/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2017win7/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017win7/OpenMPT.vcxproj
===================================================================
--- build/vs2017win7/OpenMPT.vcxproj (revision 25320)
+++ build/vs2017win7/OpenMPT.vcxproj (working copy)
@@ -898,6 +898,9 @@
+
+
+
@@ -1363,6 +1366,8 @@
+
+
@@ -1370,7 +1375,6 @@
-
Index: build/vs2017win7/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2017win7/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2017win7/OpenMPT.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxp/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2017winxp/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2017winxp/OpenMPT-ANSI.vcxproj (working copy)
@@ -547,6 +547,9 @@
+
+
+
@@ -1012,6 +1015,8 @@
+
+
@@ -1019,7 +1024,6 @@
-
Index: build/vs2017winxp/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2017winxp/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2017winxp/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxp/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2017winxp/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2017winxp/OpenMPT-UTF8.vcxproj (working copy)
@@ -547,6 +547,9 @@
+
+
+
@@ -1012,6 +1015,8 @@
+
+
@@ -1019,7 +1024,6 @@
-
Index: build/vs2017winxp/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2017winxp/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2017winxp/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxp/OpenMPT.vcxproj
===================================================================
--- build/vs2017winxp/OpenMPT.vcxproj (revision 25320)
+++ build/vs2017winxp/OpenMPT.vcxproj (working copy)
@@ -547,6 +547,9 @@
+
+
+
@@ -1012,6 +1015,8 @@
+
+
@@ -1019,7 +1024,6 @@
-
Index: build/vs2017winxp/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2017winxp/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2017winxp/OpenMPT.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxpansi/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2017winxpansi/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2017winxpansi/OpenMPT-ANSI.vcxproj (working copy)
@@ -547,6 +547,9 @@
+
+
+
@@ -1012,6 +1015,8 @@
+
+
@@ -1019,7 +1024,6 @@
-
Index: build/vs2017winxpansi/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2017winxpansi/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2017winxpansi/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxpansi/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2017winxpansi/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2017winxpansi/OpenMPT-UTF8.vcxproj (working copy)
@@ -547,6 +547,9 @@
+
+
+
@@ -1012,6 +1015,8 @@
+
+
@@ -1019,7 +1024,6 @@
-
Index: build/vs2017winxpansi/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2017winxpansi/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2017winxpansi/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxpansi/OpenMPT.vcxproj
===================================================================
--- build/vs2017winxpansi/OpenMPT.vcxproj (revision 25320)
+++ build/vs2017winxpansi/OpenMPT.vcxproj (working copy)
@@ -547,6 +547,9 @@
+
+
+
@@ -1012,6 +1015,8 @@
+
+
@@ -1019,7 +1024,6 @@
-
Index: build/vs2017winxpansi/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2017winxpansi/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2017winxpansi/OpenMPT.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxpx64/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2017winxpx64/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2017winxpx64/OpenMPT-ANSI.vcxproj (working copy)
@@ -934,6 +934,9 @@
+
+
+
@@ -1399,6 +1402,8 @@
+
+
@@ -1406,7 +1411,6 @@
-
Index: build/vs2017winxpx64/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2017winxpx64/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2017winxpx64/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxpx64/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2017winxpx64/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2017winxpx64/OpenMPT-UTF8.vcxproj (working copy)
@@ -934,6 +934,9 @@
+
+
+
@@ -1399,6 +1402,8 @@
+
+
@@ -1406,7 +1411,6 @@
-
Index: build/vs2017winxpx64/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2017winxpx64/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2017winxpx64/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2017winxpx64/OpenMPT.vcxproj
===================================================================
--- build/vs2017winxpx64/OpenMPT.vcxproj (revision 25320)
+++ build/vs2017winxpx64/OpenMPT.vcxproj (working copy)
@@ -934,6 +934,9 @@
+
+
+
@@ -1399,6 +1402,8 @@
+
+
@@ -1406,7 +1411,6 @@
-
Index: build/vs2017winxpx64/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2017winxpx64/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2017winxpx64/OpenMPT.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2019win7/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2019win7/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2019win7/OpenMPT-ANSI.vcxproj (working copy)
@@ -955,6 +955,9 @@
+
+
+
@@ -1420,6 +1423,8 @@
+
+
@@ -1427,7 +1432,6 @@
-
Index: build/vs2019win7/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2019win7/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2019win7/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2019win7/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2019win7/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2019win7/OpenMPT-UTF8.vcxproj (working copy)
@@ -955,6 +955,9 @@
+
+
+
@@ -1420,6 +1423,8 @@
+
+
@@ -1427,7 +1432,6 @@
-
Index: build/vs2019win7/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2019win7/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2019win7/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2019win7/OpenMPT.vcxproj
===================================================================
--- build/vs2019win7/OpenMPT.vcxproj (revision 25320)
+++ build/vs2019win7/OpenMPT.vcxproj (working copy)
@@ -955,6 +955,9 @@
+
+
+
@@ -1420,6 +1423,8 @@
+
+
@@ -1427,7 +1432,6 @@
-
Index: build/vs2019win7/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2019win7/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2019win7/OpenMPT.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win10/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2022win10/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2022win10/OpenMPT-ANSI.vcxproj (working copy)
@@ -1767,6 +1767,9 @@
+
+
+
@@ -2232,6 +2235,8 @@
+
+
@@ -2239,7 +2244,6 @@
-
Index: build/vs2022win10/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2022win10/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2022win10/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win10/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2022win10/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2022win10/OpenMPT-UTF8.vcxproj (working copy)
@@ -1767,6 +1767,9 @@
+
+
+
@@ -2232,6 +2235,8 @@
+
+
@@ -2239,7 +2244,6 @@
-
Index: build/vs2022win10/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2022win10/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2022win10/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win10/OpenMPT.vcxproj
===================================================================
--- build/vs2022win10/OpenMPT.vcxproj (revision 25320)
+++ build/vs2022win10/OpenMPT.vcxproj (working copy)
@@ -1767,6 +1767,9 @@
+
+
+
@@ -2232,6 +2235,8 @@
+
+
@@ -2239,7 +2244,6 @@
-
Index: build/vs2022win10/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2022win10/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2022win10/OpenMPT.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win11/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2022win11/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2022win11/OpenMPT-ANSI.vcxproj (working copy)
@@ -1769,6 +1769,9 @@
+
+
+
@@ -2234,6 +2237,8 @@
+
+
@@ -2241,7 +2246,6 @@
-
Index: build/vs2022win11/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2022win11/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2022win11/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win11/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2022win11/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2022win11/OpenMPT-UTF8.vcxproj (working copy)
@@ -1769,6 +1769,9 @@
+
+
+
@@ -2234,6 +2237,8 @@
+
+
@@ -2241,7 +2246,6 @@
-
Index: build/vs2022win11/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2022win11/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2022win11/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win11/OpenMPT.vcxproj
===================================================================
--- build/vs2022win11/OpenMPT.vcxproj (revision 25320)
+++ build/vs2022win11/OpenMPT.vcxproj (working copy)
@@ -1769,6 +1769,9 @@
+
+
+
@@ -2234,6 +2237,8 @@
+
+
@@ -2241,7 +2246,6 @@
-
Index: build/vs2022win11/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2022win11/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2022win11/OpenMPT.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win11clang/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2022win11clang/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2022win11clang/OpenMPT-ANSI.vcxproj (working copy)
@@ -1323,6 +1323,9 @@
+
+
+
@@ -1788,6 +1791,8 @@
+
+
@@ -1795,7 +1800,6 @@
-
Index: build/vs2022win11clang/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2022win11clang/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2022win11clang/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win11clang/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2022win11clang/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2022win11clang/OpenMPT-UTF8.vcxproj (working copy)
@@ -1323,6 +1323,9 @@
+
+
+
@@ -1788,6 +1791,8 @@
+
+
@@ -1795,7 +1800,6 @@
-
Index: build/vs2022win11clang/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2022win11clang/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2022win11clang/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win11clang/OpenMPT.vcxproj
===================================================================
--- build/vs2022win11clang/OpenMPT.vcxproj (revision 25320)
+++ build/vs2022win11clang/OpenMPT.vcxproj (working copy)
@@ -1323,6 +1323,9 @@
+
+
+
@@ -1788,6 +1791,8 @@
+
+
@@ -1795,7 +1800,6 @@
-
Index: build/vs2022win11clang/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2022win11clang/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2022win11clang/OpenMPT.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win7/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2022win7/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2022win7/OpenMPT-ANSI.vcxproj (working copy)
@@ -963,6 +963,9 @@
+
+
+
@@ -1428,6 +1431,8 @@
+
+
@@ -1435,7 +1440,6 @@
-
Index: build/vs2022win7/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2022win7/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2022win7/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win7/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2022win7/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2022win7/OpenMPT-UTF8.vcxproj (working copy)
@@ -963,6 +963,9 @@
+
+
+
@@ -1428,6 +1431,8 @@
+
+
@@ -1435,7 +1440,6 @@
-
Index: build/vs2022win7/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2022win7/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2022win7/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win7/OpenMPT.vcxproj
===================================================================
--- build/vs2022win7/OpenMPT.vcxproj (revision 25320)
+++ build/vs2022win7/OpenMPT.vcxproj (working copy)
@@ -963,6 +963,9 @@
+
+
+
@@ -1428,6 +1431,8 @@
+
+
@@ -1435,7 +1440,6 @@
-
Index: build/vs2022win7/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2022win7/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2022win7/OpenMPT.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win81/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2022win81/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2022win81/OpenMPT-ANSI.vcxproj (working copy)
@@ -1365,6 +1365,9 @@
+
+
+
@@ -1830,6 +1833,8 @@
+
+
@@ -1837,7 +1842,6 @@
-
Index: build/vs2022win81/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2022win81/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2022win81/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win81/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2022win81/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2022win81/OpenMPT-UTF8.vcxproj (working copy)
@@ -1365,6 +1365,9 @@
+
+
+
@@ -1830,6 +1833,8 @@
+
+
@@ -1837,7 +1842,6 @@
-
Index: build/vs2022win81/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2022win81/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2022win81/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win81/OpenMPT.vcxproj
===================================================================
--- build/vs2022win81/OpenMPT.vcxproj (revision 25320)
+++ build/vs2022win81/OpenMPT.vcxproj (working copy)
@@ -1365,6 +1365,9 @@
+
+
+
@@ -1830,6 +1833,8 @@
+
+
@@ -1837,7 +1842,6 @@
-
Index: build/vs2022win81/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2022win81/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2022win81/OpenMPT.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win8/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2022win8/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2022win8/OpenMPT-ANSI.vcxproj (working copy)
@@ -1365,6 +1365,9 @@
+
+
+
@@ -1830,6 +1833,8 @@
+
+
@@ -1837,7 +1842,6 @@
-
Index: build/vs2022win8/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2022win8/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2022win8/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win8/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2022win8/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2022win8/OpenMPT-UTF8.vcxproj (working copy)
@@ -1365,6 +1365,9 @@
+
+
+
@@ -1830,6 +1833,8 @@
+
+
@@ -1837,7 +1842,6 @@
-
Index: build/vs2022win8/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2022win8/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2022win8/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2022win8/OpenMPT.vcxproj
===================================================================
--- build/vs2022win8/OpenMPT.vcxproj (revision 25320)
+++ build/vs2022win8/OpenMPT.vcxproj (working copy)
@@ -1365,6 +1365,9 @@
+
+
+
@@ -1830,6 +1833,8 @@
+
+
@@ -1837,7 +1842,6 @@
-
Index: build/vs2022win8/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2022win8/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2022win8/OpenMPT.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2026win11/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2026win11/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2026win11/OpenMPT-ANSI.vcxproj (working copy)
@@ -1767,6 +1767,9 @@
+
+
+
@@ -2232,6 +2235,8 @@
+
+
@@ -2239,7 +2244,6 @@
-
Index: build/vs2026win11/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2026win11/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2026win11/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2026win11/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2026win11/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2026win11/OpenMPT-UTF8.vcxproj (working copy)
@@ -1767,6 +1767,9 @@
+
+
+
@@ -2232,6 +2235,8 @@
+
+
@@ -2239,7 +2244,6 @@
-
Index: build/vs2026win11/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2026win11/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2026win11/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2026win11/OpenMPT.vcxproj
===================================================================
--- build/vs2026win11/OpenMPT.vcxproj (revision 25320)
+++ build/vs2026win11/OpenMPT.vcxproj (working copy)
@@ -1767,6 +1767,9 @@
+
+
+
@@ -2232,6 +2235,8 @@
+
+
@@ -2239,7 +2244,6 @@
-
Index: build/vs2026win11/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2026win11/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2026win11/OpenMPT.vcxproj.filters (working copy)
@@ -750,6 +750,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2141,6 +2150,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2162,9 +2177,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2026win11clang/OpenMPT-ANSI.vcxproj
===================================================================
--- build/vs2026win11clang/OpenMPT-ANSI.vcxproj (revision 25320)
+++ build/vs2026win11clang/OpenMPT-ANSI.vcxproj (working copy)
@@ -1323,6 +1323,9 @@
+
+
+
@@ -1788,6 +1791,8 @@
+
+
@@ -1795,7 +1800,6 @@
-
Index: build/vs2026win11clang/OpenMPT-ANSI.vcxproj.filters
===================================================================
--- build/vs2026win11clang/OpenMPT-ANSI.vcxproj.filters (revision 25320)
+++ build/vs2026win11clang/OpenMPT-ANSI.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2026win11clang/OpenMPT-UTF8.vcxproj
===================================================================
--- build/vs2026win11clang/OpenMPT-UTF8.vcxproj (revision 25320)
+++ build/vs2026win11clang/OpenMPT-UTF8.vcxproj (working copy)
@@ -1323,6 +1323,9 @@
+
+
+
@@ -1788,6 +1791,8 @@
+
+
@@ -1795,7 +1800,6 @@
-
Index: build/vs2026win11clang/OpenMPT-UTF8.vcxproj.filters
===================================================================
--- build/vs2026win11clang/OpenMPT-UTF8.vcxproj.filters (revision 25320)
+++ build/vs2026win11clang/OpenMPT-UTF8.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: build/vs2026win11clang/OpenMPT.vcxproj
===================================================================
--- build/vs2026win11clang/OpenMPT.vcxproj (revision 25320)
+++ build/vs2026win11clang/OpenMPT.vcxproj (working copy)
@@ -1323,6 +1323,9 @@
+
+
+
@@ -1788,6 +1791,8 @@
+
+
@@ -1795,7 +1800,6 @@
-
Index: build/vs2026win11clang/OpenMPT.vcxproj.filters
===================================================================
--- build/vs2026win11clang/OpenMPT.vcxproj.filters (revision 25320)
+++ build/vs2026win11clang/OpenMPT.vcxproj.filters (working copy)
@@ -711,6 +711,15 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2102,6 +2111,12 @@
mptrack
+
+ mptrack
+
+
+ mptrack
+
mptrack
@@ -2123,9 +2138,6 @@
mptrack
-
- mptrack
-
mptrack
Index: mptrack/AbstractVstEditor.cpp
===================================================================
--- mptrack/AbstractVstEditor.cpp (revision 25320)
+++ mptrack/AbstractVstEditor.cpp (working copy)
@@ -90,7 +90,6 @@
ON_COMMAND(ID_VSTPRESETNAME, &CAbstractVstEditor::OnVSTPresetRename)
ON_COMMAND(ID_PLUGINTOINSTRUMENT, &CAbstractVstEditor::OnCreateInstrument)
ON_COMMAND_RANGE(ID_PRESET_SET, ID_PRESET_SET + PRESETS_PER_GROUP, &CAbstractVstEditor::OnSetPreset)
- ON_MESSAGE(WM_MOD_MIDIMSG, &CAbstractVstEditor::OnMidiMsg)
ON_MESSAGE(WM_MOD_KEYCOMMAND, &CAbstractVstEditor::OnCustomKeyMsg) //rewbs.customKeys
ON_COMMAND_RANGE(ID_PLUGSELECT, ID_PLUGSELECT + MAX_MIXPLUGINS, &CAbstractVstEditor::OnToggleEditor) //rewbs.patPlugName
ON_COMMAND_RANGE(ID_SELECTINST, ID_SELECTINST + MAX_INSTRUMENTS, &CAbstractVstEditor::OnSetInputInstrument) //rewbs.patPlugName
@@ -149,24 +148,29 @@
{
ResizableDialog::OnActivate(nState, pWndOther, bMinimized);
if(nState != WA_INACTIVE)
- {
- auto callback = [&plugin = m_VstPlugin](mpt::const_byte_span sysex) { plugin.MidiSend(sysex); };
- CMainFrame::GetMainFrame()->SetMidiRecordWnd(GetSafeHwnd(), callback);
- }
+ CMainFrame::GetMainFrame()->SetMidiRecordCallback(this, MidiTransformers::Transpose);
}
-LRESULT CAbstractVstEditor::OnMidiMsg(WPARAM midiData, LPARAM sender)
+bool CAbstractVstEditor::PreFilterMidiMessage(mpt::const_byte_span midiData)
{
+ return DefaultPreFilterMidiMessage(midiData, &m_VstPlugin.GetSoundFile(), kCtxVSTGUI);
+}
+
+
+void CAbstractVstEditor::OnMidiMessage(mpt::const_byte_span data)
+{
+ // Prevent MIDI feedback
+ if(data.empty() || CMainFrame::GetMainFrame()->GetCurrentMidiSender() == &m_VstPlugin)
+ return;
+
CModDoc *modDoc = m_VstPlugin.GetModDoc();
- if(modDoc != nullptr && sender != reinterpret_cast(&m_VstPlugin))
+ if(modDoc != nullptr)
{
if(!CheckInstrument(m_nInstrument))
m_nInstrument = GetBestInstrumentCandidate();
- modDoc->ProcessMIDI((uint32)midiData, 0, m_nInstrument, &m_VstPlugin, kCtxVSTGUI);
- return 1;
+ modDoc->ProcessMIDI(data, 0, m_nInstrument, &m_VstPlugin);
}
- return 0;
}
Index: mptrack/AbstractVstEditor.h
===================================================================
--- mptrack/AbstractVstEditor.h (revision 25320)
+++ mptrack/AbstractVstEditor.h (working copy)
@@ -12,6 +12,7 @@
#include "openmpt/all/BuildSettings.hpp"
+#include "MidiInputCallback.h"
#include "ResizableDialog.h"
#include "Moddoc.h"
#include "../soundlib/Snd_defs.h"
@@ -21,7 +22,7 @@
class IMixPlugin;
struct UpdateHint;
-class CAbstractVstEditor : public ResizableDialog
+class CAbstractVstEditor : public ResizableDialog, public IMidiInputCallback
{
protected:
CMenu m_Menu;
@@ -85,7 +86,6 @@
afx_msg void OnCreateInstrument();
afx_msg void OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hMenu);
afx_msg LRESULT OnCustomKeyMsg(WPARAM, LPARAM);
- afx_msg LRESULT OnMidiMsg(WPARAM, LPARAM);
afx_msg void OnDropFiles(HDROP hDropInfo);
afx_msg void OnMove(int x, int y);
afx_msg void OnClose() { DoClose(); }
@@ -94,6 +94,9 @@
void PostNcDestroy() override;
void OnOK() override { DoClose(); }
void OnCancel() override { DoClose(); }
+ BOOL PreTranslateMessage(MSG *msg) override;
+ bool PreFilterMidiMessage(mpt::const_byte_span midiData) override;
+ void OnMidiMessage(mpt::const_byte_span data) override;
virtual bool OpenEditor(CWnd *parent);
virtual void DoClose();
@@ -109,7 +112,6 @@
DECLARE_MESSAGE_MAP()
protected:
- BOOL PreTranslateMessage(MSG *msg) override;
bool HandleKeyMessage(MSG &msg, bool handleGlobal = false);
void UpdatePresetMenu(bool force = false);
void GeneratePresetMenu(int32 offset, CMenu &parent);
Index: mptrack/Childfrm.cpp
===================================================================
--- mptrack/Childfrm.cpp (revision 25320)
+++ mptrack/Childfrm.cpp (working copy)
@@ -147,7 +147,7 @@
if(bActivate && m_hWndView)
{
// Need this in addition to OnMDIActivate when switching from a non-MDI window such as a plugin editor
- CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWndView);
+ UpdateMidiRecordCallback();
}
if(m_hWndCtrl)
::SendMessage(m_hWndCtrl, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
@@ -166,7 +166,7 @@
{
MPT_ASSERT(pActivateWnd == this);
CMainFrame::GetMainFrame()->UpdateEffectKeys(static_cast(GetActiveDocument()));
- CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWndView);
+ UpdateMidiRecordCallback();
m_lastActiveFrame = this;
}
if(m_hWndCtrl)
@@ -244,20 +244,16 @@
{
CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
CWnd *pWnd;
- if (pViewClass->m_lpszClassName == m_currentViewClassName) return TRUE;
+ if(pViewClass->m_lpszClassName == m_currentViewClassName)
+ return TRUE;
if(!m_currentViewClassName.empty())
{
+ if(pMainFrm->GetMidiRecordCallback() == dynamic_cast(m_wndSplitter.GetPane(1, 0)))
+ pMainFrm->SetMidiRecordCallback(nullptr);
m_currentViewClassName.clear();
m_wndSplitter.DeleteView(1, 0);
}
- if ((m_hWndView) && (pMainFrm))
- {
- if (pMainFrm->GetMidiRecordWnd() == m_hWndView)
- {
- pMainFrm->SetMidiRecordWnd(NULL);
- }
- }
- m_hWndView = NULL;
+ m_hWndView = nullptr;
if (!m_wndSplitter.CreateView(1, 0, pViewClass, CSize(0, 0), pContext)) return FALSE;
// Get 2nd window handle
if ((pWnd = m_wndSplitter.GetPane(1, 0)) != NULL) m_hWndView = pWnd->m_hWnd;
@@ -267,7 +263,7 @@
{
::PostMessage(m_hWndView, WM_MOD_VIEWMSG, VIEWMSG_SETCTRLWND, (LPARAM)m_hWndCtrl);
::PostMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_SETVIEWWND, (LPARAM)m_hWndView);
- pMainFrm->SetMidiRecordWnd(m_hWndView);
+ UpdateMidiRecordCallback();
}
return TRUE;
}
@@ -376,6 +372,13 @@
}
+void CChildFrame::UpdateMidiRecordCallback() const
+{
+ CWnd *viewWnd = m_wndSplitter.GetPane(1, 0);
+ CMainFrame::GetMainFrame()->SetMidiRecordCallback(dynamic_cast(viewWnd));
+}
+
+
/////////////////////////////////////////////////////////////////////////////
// CChildFrame message handlers
Index: mptrack/Childfrm.h
===================================================================
--- mptrack/Childfrm.h (revision 25320)
+++ mptrack/Childfrm.h (working copy)
@@ -98,8 +98,6 @@
static CChildFrame *m_lastActiveFrame;
static int glMdiOpenCount;
-// Attributes
-protected:
CSplitterWnd m_wndSplitter;
HWND m_hWndCtrl = nullptr, m_hWndView = nullptr;
GeneralViewState m_ViewGeneral;
@@ -112,7 +110,6 @@
bool m_maxWhenClosed = false;
bool m_initialActivation = true;
-// Operations
public:
CModControlView *GetModControlView() const { return reinterpret_cast(m_wndSplitter.GetPane(0, 0)); }
BOOL ChangeViewClass(CRuntimeClass *pNewViewClass, CCreateContext *pContext = nullptr);
@@ -142,18 +139,17 @@
static CChildFrame *LastActiveFrame() { return m_lastActiveFrame; }
-// Overrides
- // ClassWizard generated virtual function overrides
+public:
//{{AFX_VIRTUAL(CChildFrame)
-public:
- BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) override;
+ BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext) override;
BOOL PreCreateWindow(CREATESTRUCT& cs) override;
void ActivateFrame(int nCmdShow) override;
void OnUpdateFrameTitle(BOOL bAddToTitle) override;
//}}AFX_VIRTUAL
-// Generated message map functions
protected:
+ void UpdateMidiRecordCallback() const;
+
//{{AFX_MSG(CChildFrame)
afx_msg LRESULT OnDPIChangedAfterParent(WPARAM, LPARAM);
afx_msg void OnDestroy();
Index: mptrack/Globals.cpp
===================================================================
--- mptrack/Globals.cpp (revision 25320)
+++ mptrack/Globals.cpp (working copy)
@@ -738,10 +738,8 @@
pModDoc->SetNotifications(Notification::Default);
pModDoc->SetFollowWnd(NULL);
}
- if (pMainFrm->GetMidiRecordWnd() == m_hWnd)
- {
- pMainFrm->SetMidiRecordWnd(NULL);
- }
+ if(pMainFrm->GetMidiRecordCallback() == dynamic_cast(this))
+ pMainFrm->SetMidiRecordCallback(nullptr);
}
CScrollView::OnDestroy();
}
@@ -894,4 +892,23 @@
}
+bool DefaultPreFilterMidiMessage(mpt::const_byte_span midiData, CSoundFile *sndFile, InputTargetContext ctx)
+{
+ PLUGINDEX mappedIndex = 0;
+ PlugParamIndex paramIndex = 0;
+ uint16 paramValue = 0;
+ bool captured = sndFile ? sndFile->GetMIDIMapper().OnMIDImsg(midiData, mappedIndex, paramIndex, paramValue) : false;
+
+ // Handle MIDI messages assigned to shortcuts
+ CInputHandler *ih = CMainFrame::GetInputHandler();
+ if(ih->HandleMIDIMessage(ctx, midiData) != kcNull
+ || ih->HandleMIDIMessage(kCtxAllContexts, midiData) != kcNull)
+ {
+ // Mapped to a command, no need to pass message on.
+ captured = true;
+ }
+ return captured;
+}
+
+
OPENMPT_NAMESPACE_END
Index: mptrack/Globals.h
===================================================================
--- mptrack/Globals.h (revision 25320)
+++ mptrack/Globals.h (working copy)
@@ -24,6 +24,7 @@
class CSoundFile;
struct DRAGONDROP;
struct Notification;
+enum InputTargetContext : int8;
class CModControlBar: public CToolBarCtrl
{
@@ -253,4 +254,7 @@
};
+bool DefaultPreFilterMidiMessage(mpt::const_byte_span midiData, CSoundFile *sndFile, InputTargetContext ctx);
+
+
OPENMPT_NAMESPACE_END
Index: mptrack/InputHandler.cpp
===================================================================
--- mptrack/InputHandler.cpp (revision 25320)
+++ mptrack/InputHandler.cpp (working copy)
@@ -176,10 +176,13 @@
// Translate MIDI messages to shortcut commands
-CommandID CInputHandler::HandleMIDIMessage(InputTargetContext context, uint32 message)
+CommandID CInputHandler::HandleMIDIMessage(InputTargetContext context, mpt::const_byte_span message)
{
+ if(message.empty())
+ return kcNull;
KeyMapRange cmd = { m_keyMap.end(), m_keyMap.end() };
- auto byte1 = MIDIEvents::GetDataByte1FromEvent(message), byte2 = MIDIEvents::GetDataByte2FromEvent(message);
+ uint8 byte1 = MIDIEvents::GetDataByte1FromEvent(message);
+ uint8 byte2 = MIDIEvents::GetDataByte2FromEvent(message);
switch(MIDIEvents::GetTypeFromEvent(message))
{
case MIDIEvents::evControllerChange:
Index: mptrack/InputHandler.h
===================================================================
--- mptrack/InputHandler.h (revision 25320)
+++ mptrack/InputHandler.h (working copy)
@@ -47,7 +47,7 @@
static KeyEventType GetKeyEventType(const MSG &msg);
static KeyEventType GetKeyEventType(UINT nFlags);
bool IsKeyPressHandledByTextBox(DWORD wparam, HWND hWnd) const;
- CommandID HandleMIDIMessage(InputTargetContext context, uint32 message);
+ CommandID HandleMIDIMessage(InputTargetContext context, mpt::const_byte_span message);
int GetKeyListSize(CommandID cmd) const;
Index: mptrack/KeyConfigDlg.cpp
===================================================================
--- mptrack/KeyConfigDlg.cpp (revision 25320)
+++ mptrack/KeyConfigDlg.cpp (working copy)
@@ -34,16 +34,14 @@
BEGIN_MESSAGE_MAP(CCustEdit, CEdit)
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
- ON_MESSAGE(WM_MOD_MIDIMSG, &CCustEdit::OnMidiMsg)
END_MESSAGE_MAP()
-LRESULT CCustEdit::OnMidiMsg(WPARAM dwMidiDataParam, LPARAM)
+void CCustEdit::OnMidiMessage(mpt::const_byte_span midiData)
{
- if(!m_isFocussed)
- return 1;
+ if(midiData.empty())
+ return;
- uint32 midiData = static_cast(dwMidiDataParam);
const auto byte1 = MIDIEvents::GetDataByte1FromEvent(midiData), byte2 = MIDIEvents::GetDataByte2FromEvent(midiData);
switch(MIDIEvents::GetTypeFromEvent(midiData))
{
@@ -64,8 +62,6 @@
default:
break;
}
-
- return 1;
}
@@ -104,9 +100,7 @@
// Lock the input handler
CMainFrame::GetInputHandler()->Bypass(true);
// Accept MIDI input
- CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWnd);
-
- m_isFocussed = true;
+ CMainFrame::GetMainFrame()->SetMidiRecordCallback(this, MidiTransformers::None);
}
@@ -113,9 +107,9 @@
void CCustEdit::OnKillFocus(CWnd *pNewWnd)
{
CEdit::OnKillFocus(pNewWnd);
- //unlock the input handler
+ // Unlock the input handler
CMainFrame::GetInputHandler()->Bypass(false);
- m_isFocussed = false;
+ CMainFrame::GetMainFrame()->SetMidiRecordCallback(nullptr);
m_pOptKeyDlg->OnCancelKeyChoice(this);
}
Index: mptrack/KeyConfigDlg.h
===================================================================
--- mptrack/KeyConfigDlg.h (revision 25320)
+++ mptrack/KeyConfigDlg.h (working copy)
@@ -13,6 +13,7 @@
#include "openmpt/all/BuildSettings.hpp"
#include "CListCtrl.h"
#include "CommandSet.h"
+#include "MidiInputCallback.h"
OPENMPT_NAMESPACE_BEGIN
@@ -36,11 +37,10 @@
};
-class CCustEdit: public CEdit
+class CCustEdit: public CEdit, public IMidiInputCallback
{
protected:
COptionsKeyboard *m_pOptKeyDlg = nullptr;
- bool m_isFocussed = false;
bool m_bypassed = false;
public:
@@ -56,10 +56,10 @@
protected:
BOOL PreTranslateMessage(MSG *pMsg) override;
-
+ void OnMidiMessage(mpt::const_byte_span midiData) override;
+
afx_msg void OnSetFocus(CWnd *pOldWnd);
afx_msg void OnKillFocus(CWnd *pNewWnd);
- afx_msg LRESULT OnMidiMsg(WPARAM, LPARAM);
DECLARE_MESSAGE_MAP()
};
Index: mptrack/Mainbar.cpp
===================================================================
--- mptrack/Mainbar.cpp (revision 25320)
+++ mptrack/Mainbar.cpp (working copy)
@@ -14,6 +14,7 @@
#include "ImageLists.h"
#include "InputHandler.h"
#include "Mainfrm.h"
+#include "MidiDeviceManager.h"
#include "Moddoc.h"
#include "Mptrack.h"
#include "resource.h"
@@ -566,6 +567,9 @@
wsprintf(s, _T("Octave %d"), octave);
m_EditOctave.SetWindowText(s);
m_SpinOctave.SetPos(octave);
+
+ if(CMainFrame *mainFrm = CMainFrame::GetMainFrame())
+ mainFrm->UpdateMidiFilters();
}
@@ -852,19 +856,16 @@
// Show a list of MIDI devices
{
HMENU hMenu = ::CreatePopupMenu();
- MIDIINCAPS mic;
- UINT numDevs = midiInGetNumDevs();
- if(numDevs > MAX_MIDI_DEVICES) numDevs = MAX_MIDI_DEVICES;
+ auto ports = MidiDeviceManager::Instance().EnumerateInputPorts();
+ if(ports.size() > MAX_MIDI_DEVICES)
+ ports.resize(MAX_MIDI_DEVICES);
UINT current = TrackerSettings::Instance().GetCurrentMIDIDevice();
- for(UINT i = 0; i < numDevs; i++)
+ for(const auto &port : ports)
{
- mic.szPname[0] = 0;
- if(midiInGetDevCaps(i, &mic, sizeof(mic)) == MMSYSERR_NOERROR)
- {
- ::AppendMenu(hMenu, MF_STRING | (i == current ? MF_CHECKED : 0), ID_SELECT_MIDI_DEVICE + i, theApp.GetFriendlyMIDIPortName(mpt::String::ReadCStringBuf(mic.szPname), true));
- }
+ CString displayName = theApp.GetFriendlyMIDIPortName(mpt::ToCString(mpt::Charset::UTF8, port.name), true);
+ ::AppendMenu(hMenu, MF_STRING | (port.id == current ? MF_CHECKED : 0), ID_SELECT_MIDI_DEVICE + port.id, displayName);
}
- if(!numDevs)
+ if(ports.empty())
{
::AppendMenu(hMenu, MF_STRING | MF_GRAYED, 0, _T("No MIDI input devices found"));
}
@@ -880,9 +881,9 @@
void CMainToolBar::OnSelectMIDIDevice(UINT id)
{
- CMainFrame::GetMainFrame()->midiCloseDevice();
+ CMainFrame::GetMainFrame()->MidiCloseDevice();
TrackerSettings::Instance().SetMIDIDevice(id - ID_SELECT_MIDI_DEVICE);
- CMainFrame::GetMainFrame()->midiOpenDevice();
+ CMainFrame::GetMainFrame()->MidiOpenDevice();
}
Index: mptrack/MainFrm.cpp
===================================================================
--- mptrack/MainFrm.cpp (revision 25320)
+++ mptrack/MainFrm.cpp (working copy)
@@ -27,6 +27,7 @@
#include "InputHandler.h"
#include "IPCWindow.h"
#include "KeyConfigDlg.h"
+#include "MidiDeviceManager.h"
#include "Moddoc.h"
#include "ModDocTemplate.h"
#include "Mpdlgs.h"
@@ -121,6 +122,7 @@
ON_MESSAGE(WM_MOD_UPDATEPOSITION, &CMainFrame::OnUpdatePosition)
ON_MESSAGE(WM_MOD_INVALIDATEPATTERNS, &CMainFrame::OnInvalidatePatterns)
+ ON_MESSAGE(WM_MOD_MIDIMSG, &CMainFrame::OnMidiMsgReady)
ON_MESSAGE(WM_MOD_KEYCOMMAND, &CMainFrame::OnCustomKeyMsg)
ON_MESSAGE(WM_MOD_MIDIMAPPING, &CMainFrame::OnViewMIDIMapping)
ON_MESSAGE(WM_MOD_UPDATEVIEWS, &CMainFrame::OnUpdateViews)
@@ -328,7 +330,7 @@
UpdateColors();
if(TrackerSettings::Instance().midiSetup & MidiSetup::EnableMidiInOnStartup)
- midiOpenDevice(false);
+ MidiOpenDevice(false);
#if MPT_COMPILER_MSVC
#pragma warning(push)
@@ -425,8 +427,8 @@
KillTimer(m_nTimer);
m_nTimer = 0;
}
- if(midiInData.inHandle)
- midiCloseDevice();
+ if(m_midiInputHandle)
+ MidiCloseDevice();
// Delete bitmaps
delete bmpNotes;
bmpNotes = nullptr;
@@ -498,12 +500,11 @@
BOOL CMainFrame::OnDeviceChange(UINT nEventType, DWORD_PTR dwData)
{
- if(nEventType == DBT_DEVNODES_CHANGED && midiInData.inHandle)
+ if(nEventType == DBT_DEVNODES_CHANGED && m_midiInputHandle)
{
- // Calling this (or most other MIDI input related functions) makes the MIDI driver realize
- // that the connection to USB MIDI devices was lost and send a MIM_CLOSE message.
- // Otherwise, after disconnecting a USB MIDI device, the MIDI callback will stay alive forever but return no data.
- midiInGetNumDevs();
+ // Check if USB MIDI device was disconnected
+ if(!MidiDeviceManager::CheckDeviceStatus(*m_midiInputHandle))
+ SendMessage(WM_COMMAND, ID_MIDI_RECORD);
}
return CMDIFrameWnd::OnDeviceChange(nEventType, dwData);
}
@@ -782,6 +783,7 @@
void CMainFrame::SoundCallbackPreStart()
{
MPT_TRACE();
+ MidiDeviceManager::Instance().ResetSyncPoint();
m_SoundDeviceClock.SetResolution(1);
}
@@ -923,6 +925,7 @@
MPT_ASSERT(InAudioThread());
OPENMPT_PROFILE_FUNCTION(Profiler::Notify);
MPT_ASSERT((timeInfo.RenderStreamPositionAfter.Frames - timeInfo.RenderStreamPositionBefore.Frames) < std::numeric_limits::max());
+ MidiDeviceManager::Instance().UpdateSyncPoint(timeInfo.SyncPointStreamFrames, timeInfo.SyncPointSystemTimestamp, timeInfo.Speed, m_pSndFile->GetSampleRate());
samplecount_t framesRendered = static_cast(timeInfo.RenderStreamPositionAfter.Frames - timeInfo.RenderStreamPositionBefore.Frames);
int64 streamPosition = timeInfo.RenderStreamPositionAfter.Frames;
DoNotification(framesRendered, streamPosition);
@@ -1521,6 +1524,7 @@
// set mixing parameters in CSoundFile
UpdateAudioParameters(sndFile);
+ mpt::reconstruct(sndFile.m_TimingInfo);
SetPlaybackSoundFile(&sndFile);
@@ -1579,9 +1583,10 @@
SetElapsedTime(0);
GenerateStopNotification();
- m_pSndFile->ResetPlayPos();
- m_pSndFile->ResetChannels();
+ CSoundFile &sndFile = *m_pSndFile;
UnsetPlaybackSoundFile();
+ sndFile.ResetPlayPos();
+ sndFile.ResetChannels();
StopPlayback();
@@ -1955,20 +1960,32 @@
}
-void CMainFrame::SetupMidi(FlagSet d, UINT n)
+void CMainFrame::SetupMidi(FlagSet flags, UINT device)
{
- bool deviceChanged = (TrackerSettings::Instance().m_nMidiDevice != n);
- TrackerSettings::Instance().midiSetup = d;
- TrackerSettings::Instance().SetMIDIDevice(n);
- if(deviceChanged && midiInData.inHandle)
+ bool deviceChanged = (TrackerSettings::Instance().m_nMidiDevice != device);
+ TrackerSettings::Instance().midiSetup = flags;
+ TrackerSettings::Instance().SetMIDIDevice(device);
+ if(deviceChanged && m_midiInputHandle)
{
// Device has changed, close the old one.
- midiCloseDevice();
- midiOpenDevice();
+ MidiCloseDevice();
+ MidiOpenDevice();
}
+
+ UpdateMidiFilters();
}
+void CMainFrame::UpdateMidiFilters()
+{
+ FlagSet midiSetup = TrackerSettings::Instance().midiSetup;
+ m_volumeFilter.SetRecordVelocity(midiSetup[MidiSetup::RecordVelocity]);
+ m_volumeFilter.SetApplyChannelVolumeToVelocity(midiSetup[MidiSetup::ApplyChannelVolumeToVelocity]);
+ m_volumeFilter.SetVelocityAmplification(TrackerSettings::Instance().midiVelocityAmp);
+ m_transposeFilter.SetTranspose(midiSetup[MidiSetup::TransposeKeyboard] ? (GetBaseOctave() - 4) * 12 : 0);
+}
+
+
void CMainFrame::SetUserText(const TCHAR *text)
{
if(text[0] || !m_userText.IsEmpty())
@@ -2568,6 +2585,133 @@
}
+bool CMainFrame::MidiOpenDevice(bool showSettings)
+{
+ if(m_midiInputHandle)
+ return true;
+
+ UpdateMidiFilters();
+
+ MidiDeviceManager &manager = MidiDeviceManager::Instance();
+ MidiPortID port = TrackerSettings::Instance().GetCurrentMIDIDevice();
+ m_midiInputHandle = manager.OpenInput(port, this);
+
+ if(!m_midiInputHandle)
+ {
+ // Show MIDI configuration on fail.
+ if(showSettings)
+ {
+ CMainFrame::m_nLastOptionsPage = OPTIONS_PAGE_MIDI;
+ OnViewOptions();
+ }
+
+ // Let's see if the user updated the settings.
+ port = static_cast(TrackerSettings::Instance().GetCurrentMIDIDevice());
+ m_midiInputHandle = manager.OpenInput(port, this);
+
+ if(!m_midiInputHandle)
+ return false;
+ }
+
+ return true;
+}
+
+
+void CMainFrame::MidiCloseDevice()
+{
+ m_midiInputHandle.reset();
+}
+
+
+void CMainFrame::OnMidiRecord()
+{
+ if(m_midiInputHandle)
+ MidiCloseDevice();
+ else
+ MidiOpenDevice();
+}
+
+
+void CMainFrame::OnUpdateMidiRecord(CCmdUI *pCmdUI)
+{
+ if(pCmdUI)
+ pCmdUI->SetCheck(m_midiInputHandle ? TRUE : FALSE);
+}
+
+
+void CMainFrame::SetMidiRecordCallback(IMidiInputCallback *callback, FlagSet transformers)
+{
+ m_sustainFilter.SetBypassed(!transformers[MidiTransformers::Sustain]);
+ m_volumeFilter.SetBypassed(!transformers[MidiTransformers::Volume]);
+ m_transposeFilter.SetBypassed(!transformers[MidiTransformers::Transpose]);
+
+ m_midiRecordCallback = callback;
+ if(callback)
+ callback->SetDestroyNotifySink(this);
+}
+
+
+void CMainFrame::OnMidiInputCallbackDestroyed(void *callback)
+{
+ if(m_midiRecordCallback == callback)
+ m_midiRecordCallback = nullptr;
+}
+
+
+void CMainFrame::EnqueueMidiMessage(mpt::const_byte_span data, void *sender)
+{
+ {
+ mpt::lock_guard lock{m_midiInputMutex};
+ m_midiInputQueue.emplace_back(0, data, sender);
+ }
+ if(!m_midiMsgPending.exchange(true))
+ PostMessage(WM_MOD_MIDIMSG, 0, 0);
+}
+
+
+LRESULT CMainFrame::OnMidiMsgReady(WPARAM, LPARAM)
+{
+ m_midiMsgPending = false;
+
+ std::vector localQueue;
+ {
+ mpt::lock_guard lock{m_midiInputMutex};
+ std::swap(localQueue, m_midiInputQueue);
+ m_midiInputQueue.reserve(localQueue.capacity());
+ }
+
+ for(auto &msg : localQueue)
+ {
+ m_currentMidiSender = msg.m_sender;
+
+ if(m_midiRecordCallback)
+ {
+ if(m_midiRecordCallback->PreFilterMidiMessage(msg))
+ continue;
+
+ m_sustainFilter.SetNextCallback(&m_volumeFilter);
+ m_volumeFilter.SetNextCallback(&m_transposeFilter);
+ m_transposeFilter.SetNextCallback(m_midiRecordCallback);
+
+ m_sustainFilter.OnMidiMessage(msg);
+ } else
+ {
+ // No recording target, just handle keyboard shortcuts
+ GetInputHandler()->HandleMIDIMessage(kCtxAllContexts, msg);
+ }
+ }
+ m_currentMidiSender = nullptr;
+
+ return 0;
+}
+
+
+int CMainFrame::NoteVolumeFromMidi(mpt::const_byte_span midiData)
+{
+ return GetMainFrame()->m_volumeFilter.ApplyVolumeSettings(midiData);
+}
+
+
LRESULT CMainFrame::OnUpdatePosition(WPARAM, LPARAM lParam)
{
OPENMPT_PROFILE_FUNCTION(Profiler::GUI);
@@ -3367,18 +3511,16 @@
}
-HMENU CMainFrame::CreateFileMenu(const size_t maxCount, std::vector& paths, const mpt::PathString &folderName, const uint16 idRangeBegin)
+HMENU CMainFrame::CreateFileMenu(const size_t maxCount, std::vector &paths, const mpt::PathString &folderName, const uint16 idRangeBegin)
{
paths.clear();
- for(size_t i = 0; i < 2; i++) // 0: app items, 1: user items
+ std::vector basePaths{theApp.GetInstallPath()};
+ if(mpt::PathString configPath = theApp.GetConfigPath(); mpt::PathCompareNoCase(basePaths.front(), configPath))
+ basePaths.push_back(std::move(configPath));
+
+ for(mpt::PathString basePath : basePaths)
{
- // To avoid duplicates, check whether app path and config path are the same.
- if(i == 1 && mpt::PathCompareNoCase(theApp.GetInstallPath(), theApp.GetConfigPath()) == 0)
- break;
-
- mpt::PathString basePath;
- basePath = (i == 0) ? theApp.GetInstallPath() : theApp.GetConfigPath();
basePath += folderName;
if(!mpt::native_fs{}.is_directory(basePath))
continue;
Index: mptrack/Mainfrm.h
===================================================================
--- mptrack/Mainfrm.h (revision 25320)
+++ mptrack/Mainfrm.h (working copy)
@@ -13,6 +13,8 @@
#include "openmpt/all/BuildSettings.hpp"
#include "CImageListEx.h"
#include "Mainbar.h"
+#include "MidiInputCallback.h"
+#include "MidiTransformer.h"
#include "Notification.h"
#include "openmpt/soundbase/Dither.hpp"
#include "Settings.h"
@@ -27,6 +29,7 @@
#include
+#include
#include
OPENMPT_NAMESPACE_BEGIN
@@ -35,6 +38,8 @@
class CDLSBank;
class CInputHandler;
class CModDoc;
+class MidiInputHandle;
+class MidiMessage;
class QuickStartDlg;
struct UpdateCheckResult;
struct UpdateHint;
@@ -143,6 +148,8 @@
, public SoundDevice::CallbackBufferHandler
, public SoundDevice::IMessageReceiver
, public TfLanguageProfileNotifySink
+ , public IMidiInputCallbackDestroyedNotifySink
+ , public IMidiInputCallback
{
DECLARE_DYNAMIC(CMainFrame)
// static data
@@ -174,24 +181,7 @@
DWORD m_AudioThreadId = 0;
bool m_InNotifyHandler = false;
- // Midi Input
public:
- struct MidiInData
- {
- struct SysExBuffer
- {
- MIDIHDR header;
- std::vector data;
- };
-
- std::vector sysexBuffers;
- mpt::mutex dataMutex;
- HMIDIIN inHandle = nullptr;
- };
-
- MidiInData midiInData;
-
-public:
CImageListEx m_MiscIcons, m_MiscIconsDisabled; // Misc Icons
CImageListEx m_PatternIcons, m_PatternIconsDisabled; // Pattern icons (includes some from sample editor as well...)
CImageListEx m_EnvelopeIcons; // Instrument editor icons
@@ -202,8 +192,18 @@
CStatusBar m_wndStatusBar;
CMainToolBar m_wndToolBar;
CSoundFile *m_pSndFile = nullptr; // != NULL only when currently playing or rendering
- HWND m_hWndMidi = nullptr;
- std::function m_midiSysExCallback;
+
+ // MIDI Input
+ std::unique_ptr m_midiInputHandle;
+ IMidiInputCallback *m_midiRecordCallback = nullptr;
+ MidiTransposeFilter m_transposeFilter;
+ MidiVolumeFilter m_volumeFilter;
+ MidiSustainFilter m_sustainFilter;
+ mpt::mutex m_midiInputMutex; // Protects m_midiInputQueue
+ std::vector m_midiInputQueue;
+ std::atomic m_midiMsgPending = false;
+ void *m_currentMidiSender = nullptr; // Set during dispatch for feedback prevention
+
samplecount_t m_dwTimeSec = 0;
UINT_PTR m_nTimer = 0;
UINT m_nAvgMixChn = 0, m_nMixChn = 0;
@@ -268,23 +268,24 @@
bool IsAudioDeviceOpen() const;
bool DoNotification(DWORD dwSamplesRead, int64 streamPosition);
-// Midi Input Functions
public:
- bool midiOpenDevice(bool showSettings = true);
- void midiCloseDevice();
- void SetMidiRecordWnd(HWND hwnd, std::function sysExCallback = {})
- {
- m_hWndMidi = hwnd;
- m_midiSysExCallback = std::move(sysExCallback);
- }
- HWND GetMidiRecordWnd() const { return m_hWndMidi; }
- auto GetMidiSysexCallback() const { return m_midiSysExCallback; }
+ // MIDI Input Functions
+ bool MidiOpenDevice(bool showSettings = true);
+ void MidiCloseDevice();
+ void OnMidiMessage(mpt::const_byte_span midiData) override { EnqueueMidiMessage(midiData); }
+ void SetMidiRecordCallback(IMidiInputCallback *callback, FlagSet transformers = MidiTransformers::Default);
+ IMidiInputCallback *GetMidiRecordCallback() const { return m_midiRecordCallback; }
+ void OnMidiInputCallbackDestroyed(void *callback) override;
+ // Enqueue a received MIDI message for processing in UI thread
+ void EnqueueMidiMessage(mpt::const_byte_span data, void *sender = nullptr);
+ // Returns the sender of the MIDI message currently being dispatched (for feedback prevention)
+ void *GetCurrentMidiSender() const { return m_currentMidiSender; }
+ static int NoteVolumeFromMidi(mpt::const_byte_span midiData);
+
void LoadMetronomeSamples();
void UpdateMetronomeSamples();
void UpdateMetronomeVolume();
- static int ApplyVolumeRelatedSettings(const DWORD &dwParam1, const uint8 midivolume);
-
// static functions
public:
static CMainFrame *GetMainFrame() noexcept;
@@ -380,7 +381,9 @@
void SetupMiscOptions();
void SetupPlayer();
- void SetupMidi(FlagSet d, UINT n);
+ void SetupMidi(FlagSet flags, UINT device);
+ void UpdateMidiFilters();
+
HWND GetFollowSong() const;
HWND GetFollowSong(const CModDoc *pDoc) const { return (pDoc == GetModPlaying()) ? GetFollowSong() : nullptr; }
void ResetNotificationBuffer();
@@ -462,6 +465,7 @@
afx_msg void OnOpenMRUItem(UINT nId);
afx_msg void OnUpdateMRUItem(CCmdUI *cmd);
afx_msg LRESULT OnInvalidatePatterns(WPARAM, LPARAM);
+ afx_msg LRESULT OnMidiMsgReady(WPARAM, LPARAM);
afx_msg LRESULT OnCustomKeyMsg(WPARAM, LPARAM);
afx_msg void OnInternetUpdate();
afx_msg void OnUpdateAvailable();
Index: mptrack/MidiDeviceManager.cpp
===================================================================
--- mptrack/MidiDeviceManager.cpp (nonexistent)
+++ mptrack/MidiDeviceManager.cpp (working copy)
@@ -0,0 +1,517 @@
+/*
+ * MidiDeviceManager.cpp
+ * ---------------------
+ * Purpose: MIDI device management, port sharing, and MIDI output thread handling
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#include "stdafx.h"
+#include "MidiDeviceManager.h"
+#include "MidiInputCallback.h"
+#include "Mptrack.h"
+#include "TrackerSettings.h"
+#include "../misc/mptClock.h"
+#include "../soundlib/MIDIEvents.h"
+#include "../src/openmpt/sounddevice/SoundDeviceManager.hpp"
+#include "../src/openmpt/sounddevice/SoundDeviceUtilities.hpp"
+
+#include
+
+#include
+#include
+#include
+#include
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+static_assert(std::is_same_v);
+
+
+struct MidiDeviceManager::SharedInputPort
+{
+ RtMidiIn rtMidiIn;
+ mpt::mutex callbackMutex; // Guards callbacks vector
+ std::vector callbacks;
+ std::vector sysexBuffer;
+
+ bool HasReferences() const noexcept { return !callbacks.empty(); }
+};
+
+
+struct MidiDeviceManager::SharedOutputPort
+{
+ RtMidiOut rtMidiOut;
+ std::thread outputThread;
+ mpt::mutex mutex; // Guards queue vector
+ std::vector queue;
+ std::condition_variable_any cv;
+ std::atomic running = false;
+ int refCount = 0;
+
+ bool HasReferences() const noexcept { return refCount > 0; }
+};
+
+
+MidiDeviceManager &MidiDeviceManager::Instance()
+{
+ static MidiDeviceManager instance;
+ return instance;
+}
+
+
+MidiDeviceManager::~MidiDeviceManager()
+{
+ MPT_ASSERT(m_openInputs.empty());
+ MPT_ASSERT(m_openOutputs.empty());
+}
+
+
+std::vector MidiDeviceManager::EnumeratePorts(RtMidi &rtMidi, bool isInput)
+{
+ std::vector ports;
+ try
+ {
+ const MidiPortID count = rtMidi.getPortCount();
+ ports.reserve(count);
+ for(MidiPortID i = 0; i < count; i++)
+ {
+ try
+ {
+ std::string name = rtMidi.getPortName(i);
+ mpt::ustring friendlyName = theApp.GetFriendlyMIDIPortName(mpt::ToUnicode(mpt::Charset::UTF8, name), isInput, false);
+ ports.push_back({std::move(name), std::move(friendlyName), i});
+ } catch(const RtMidiError &)
+ {
+ }
+ }
+ } catch(const RtMidiError &)
+ {
+ }
+ return ports;
+}
+
+
+std::vector MidiDeviceManager::EnumerateInputPorts()
+{
+ RtMidiIn rtMidi;
+ return EnumeratePorts(rtMidi, true);
+}
+
+
+std::vector MidiDeviceManager::EnumerateOutputPorts()
+{
+ RtMidiOut rtMidi;
+ return EnumeratePorts(rtMidi, false);
+}
+
+
+MidiPortID MidiDeviceManager::FindPort(RtMidi &rtMidi, const MidiPortInfo &port, bool isInput)
+{
+ if(port.id == kNoMidiDevice)
+ return port.id;
+
+ MidiPortID candidate = port.id;
+ bool foundFriendly = false;
+ const MidiPortID numPorts = rtMidi.getPortCount();
+ for(MidiPortID i = 0; i < numPorts; i++)
+ {
+ try
+ {
+ std::string portName = rtMidi.getPortName(i);
+ bool deviceNameMatches = (portName == port.name);
+ if(!port.friendlyName.empty() && port.friendlyName == theApp.GetFriendlyMIDIPortName(mpt::ToUnicode(mpt::Charset::UTF8, portName), isInput, false))
+ {
+ candidate = i;
+ foundFriendly = true;
+ if(deviceNameMatches)
+ return candidate;
+ }
+ if(deviceNameMatches && !foundFriendly)
+ candidate = i;
+ } catch(const RtMidiError &)
+ {
+ }
+ }
+ return candidate;
+}
+
+
+MidiPortID MidiDeviceManager::FindInputPort(const MidiPortInfo &port)
+{
+ try
+ {
+ RtMidiIn rtMidi;
+ return FindPort(rtMidi, port, true);
+ } catch(const RtMidiError &)
+ {
+ return kNoMidiDevice;
+ }
+}
+
+
+MidiPortID MidiDeviceManager::FindOutputPort(const MidiPortInfo &port)
+{
+ try
+ {
+ RtMidiOut rtMidi;
+ return FindPort(rtMidi, port, false);
+ } catch(const RtMidiError &)
+ {
+ return kNoMidiDevice;
+ }
+}
+
+
+MidiPortID MidiDeviceManager::FindPort(const MidiPortInfo &port, bool asInputDevice)
+{
+ if(asInputDevice)
+ return FindInputPort(port);
+ else
+ return FindOutputPort(port);
+}
+
+
+MidiPortInfo MidiDeviceManager::GetPortInfo(const MidiInputHandle &handle) const
+{
+ SharedInputPort &port = handle.m_port;
+ auto it = std::find_if(m_openInputs.begin(), m_openInputs.end(), [&port](const auto &kv) { return &kv.second == &port; });
+ if(it == m_openInputs.end())
+ return {};
+
+ std::string name = port.rtMidiIn.getPortName(it->first);
+ mpt::ustring friendlyName = theApp.GetFriendlyMIDIPortName(mpt::ToUnicode(mpt::Charset::UTF8, name), false, false);
+ return {std::move(name), std::move(friendlyName), it->first};
+}
+
+
+MidiPortInfo MidiDeviceManager::GetPortInfo(const MidiOutputHandle &handle) const
+{
+ SharedOutputPort &port = handle.m_port;
+ auto it = std::find_if(m_openOutputs.begin(), m_openOutputs.end(), [&port](const auto &kv) { return &kv.second == &port; });
+ if(it == m_openOutputs.end())
+ return {};
+
+ std::string name = port.rtMidiOut.getPortName(it->first);
+ mpt::ustring friendlyName = theApp.GetFriendlyMIDIPortName(mpt::ToUnicode(mpt::Charset::UTF8, name), true, false);
+ return {std::move(name), std::move(friendlyName), it->first};
+}
+
+
+
+// RtMidi input callback that dispatches to all registered listeners
+void MidiDeviceManager::InputCallback(double /*deltatime*/, std::vector *message, void *userData)
+{
+ if(!message || message->empty() || !userData)
+ return;
+
+ auto &port = *static_cast(userData);
+ const auto span = mpt::byte_cast(mpt::as_span(*message));
+
+ std::vector callbacks;
+ {
+ // Make a copy, in case the user opens this input device in another MIDI I/O plugin instance at the same time
+ std::lock_guard lock{port.callbackMutex};
+ callbacks = port.callbacks;
+ }
+
+ if(!port.sysexBuffer.empty() && !(message->front() & 0x80))
+ {
+ // Continued SysEx message
+ port.sysexBuffer.insert(port.sysexBuffer.end(), span.begin(), span.end());
+ if(message->back() == MIDIEvents::sysExEnd)
+ {
+ for(auto *callback : callbacks)
+ callback->OnMidiMessage(port.sysexBuffer);
+ port.sysexBuffer.clear();
+ }
+ return;
+ } else if(message->front() == MIDIEvents::sysExStart && message->back() != MIDIEvents::sysExEnd)
+ {
+ // Start of SysEx but not complete yet (PortAudio backends are inconsistent in whether they buffer incomplete messages or not)
+ port.sysexBuffer.assign(span.begin(), span.end());
+ return;
+ }
+
+ for(auto *callback : callbacks)
+ callback->OnMidiMessage(span);
+}
+
+
+std::unique_ptr MidiDeviceManager::OpenInput(MidiPortID port, IMidiInputCallback *callback)
+{
+ if(port == kNoMidiDevice || !callback)
+ return nullptr;
+
+ mpt::lock_guard lock{m_managerMutex};
+
+ auto &shared = m_openInputs[port];
+ if(shared.rtMidiIn.isPortOpen())
+ {
+ // Port already open - add this callback as a listener
+ mpt::lock_guard callbackLock{shared.callbackMutex};
+ shared.callbacks.push_back(callback);
+ } else
+ {
+ // Need to open the port
+ try
+ {
+ shared.sysexBuffer.clear();
+ shared.rtMidiIn.openPort(port);
+ shared.rtMidiIn.ignoreTypes(false, true, true); // Accept SysEx, ignore timing / active sensing
+ shared.callbacks.push_back(callback);
+ shared.rtMidiIn.setCallback(InputCallback, &shared);
+ } catch(const RtMidiError &)
+ {
+ m_openInputs.erase(port);
+ return nullptr;
+ }
+ }
+
+ return std::unique_ptr(new MidiInputHandle{shared, *callback});
+}
+
+
+void MidiDeviceManager::CloseInput(MidiInputHandle &handle)
+{
+ mpt::lock_guard lock{m_managerMutex};
+ SharedInputPort &port = handle.m_port;
+
+ // Remove this specific callback
+ auto &cbs = port.callbacks;
+ cbs.erase(std::remove(cbs.begin(), cbs.end(), &handle.m_callback), cbs.end());
+
+ if(port.HasReferences())
+ return;
+
+ // This was the last listener, close the port
+ try
+ {
+ port.rtMidiIn.closePort();
+ } catch(const RtMidiError &)
+ {
+ }
+ auto it = std::find_if(m_openInputs.begin(), m_openInputs.end(), [&port](const auto &kv) { return &kv.second == &port; });
+ if(it != m_openInputs.end())
+ m_openInputs.erase(it);
+}
+
+
+MidiInputHandle::~MidiInputHandle()
+{
+ MidiDeviceManager::Instance().CloseInput(*this);
+}
+
+
+void MidiDeviceManager::OutputThreadFunc(SharedOutputPort &port)
+{
+ const auto *sdm = theApp.GetSoundDevicesManager();
+ const auto sysInfo = sdm->GetSysInfo();
+ const auto appInfo = sdm->GetAppInfo();
+ SoundDevice::CPriorityBooster priorityBooster{sysInfo, /*TrackerSettings::Instance().SoundBoostedThreadPriority*/ true, appInfo.BoostedThreadMMCSSClassVista, appInfo.BoostedThreadPriorityXP};
+ //SoundDevice::CPeriodicWaker periodicWaker{m_WakeupInterval};
+ Util::MultimediaClock clock;
+ clock.SetResolution(1);
+
+ static constexpr double MIN_WAIT_TIME_MS = 1.0;
+
+ auto waitTimeMs = [this, &clock](int64 eventStreamFrame)
+ {
+ SyncPoint sp = m_syncPoint;
+ // Sync point not valid yet? Wait some more.
+ if(sp.sampleRate == 0 || sp.speed <= 0.0)
+ return MIN_WAIT_TIME_MS;
+
+ const double srFactor = sp.speed * sp.sampleRate;
+ int64 actualCurrentFrame = mpt::saturate_round(
+ sp.streamFrames
+ + (static_cast(static_cast(clock.NowNanoseconds() - sp.systemTimestampNs)) * srFactor * (1.0 / 1'000'000'000.0)));
+
+ double frameDelta = static_cast(eventStreamFrame - actualCurrentFrame);
+ double waitMs = 1000.0 * frameDelta / srFactor;
+ return std::min(waitMs, 10.0); // In case of large buffer, only wait a smaller amount of time to decrease chance of overshooting
+ };
+
+ std::vector localQueue;
+
+ while(true)
+ {
+ {
+ std::unique_lock lock{port.mutex};
+ port.cv.wait(lock, [&]() { return !port.queue.empty() || !port.running; });
+ if(!port.running && port.queue.empty())
+ break;
+ localQueue.assign(std::make_move_iterator(port.queue.begin()), std::make_move_iterator(port.queue.end()));
+ port.queue.clear();
+ // If two I/O plugin instances with different latency settings use the same output device, the timestamps are not going to be ordered correctly
+ std::stable_sort(localQueue.begin(), localQueue.end());
+ }
+
+ for(size_t i = 0; i < localQueue.size(); i++)
+ {
+ double waitMs = waitTimeMs(localQueue[i].m_streamFrame);
+
+ // Wait until it's time to send this message (unless we're shutting down)
+ while(waitMs > 0.0 && port.running)
+ {
+ if(waitMs >= MIN_WAIT_TIME_MS)
+ {
+ //OutputDebugString(MPT_CFORMAT("sleep {}\n")(waitMs));
+ // Sleep until ~1ms before target, then spin-wait for more precise timing
+ std::unique_lock lock{port.mutex};
+ port.cv.wait_for(lock, std::chrono::nanoseconds{static_cast((waitMs - 1.0) * 1'000'000.0)});
+ if(!port.queue.empty())
+ {
+ // Move any newly queued messages into our local queue
+ localQueue.insert(localQueue.end(), std::make_move_iterator(port.queue.begin()), std::make_move_iterator(port.queue.end()));
+ port.queue.clear();
+ // If two I/O plugin instances with different latency settings use the same output device, the timestamps are not going to be ordered correctly.
+ std::stable_sort(localQueue.begin() + i, localQueue.end());
+ }
+ }
+ waitMs = waitTimeMs(localQueue[i].m_streamFrame);
+ }
+
+ // If we're draining messages, don't send any new note-on messages
+ if(MIDIEvents::GetTypeFromEvent(localQueue[i]) == MIDIEvents::evNoteOn && !port.running)
+ continue;
+
+ try
+ {
+ port.rtMidiOut.sendMessage(mpt::byte_cast(localQueue[i].m_message), localQueue[i].m_size);
+ } catch(const RtMidiError &)
+ {
+ }
+ }
+ }
+
+ clock.SetResolution(0);
+}
+
+
+std::unique_ptr MidiDeviceManager::OpenOutput(MidiPortID port)
+{
+ if(port == kNoMidiDevice)
+ return nullptr;
+
+ mpt::lock_guard lock{m_managerMutex};
+
+ auto &shared = m_openOutputs[port];
+ if(shared.rtMidiOut.isPortOpen())
+ {
+ // Port already open, share it
+ } else
+ {
+ // Need to open the port and start the output thread
+ try
+ {
+ shared.queue.reserve(256);
+ shared.rtMidiOut.openPort(port);
+ shared.running = true;
+ shared.outputThread = std::thread([this, &shared]() { OutputThreadFunc(shared); });
+ } catch(const RtMidiError &)
+ {
+ m_openOutputs.erase(port);
+ return nullptr;
+ }
+ }
+ shared.refCount++;
+
+ return std::unique_ptr(new MidiOutputHandle{shared});
+}
+
+
+void MidiDeviceManager::CloseOutput(MidiOutputHandle &handle)
+{
+ std::thread thread;
+ SharedOutputPort &port = handle.m_port;
+
+ mpt::lock_guard lock{m_managerMutex};
+
+ port.refCount--;
+ if(port.HasReferences())
+ return;
+
+ // This was the last user of the port, signal thread to drain remaining messages and close the port
+ port.running = false;
+ port.cv.notify_all();
+
+ if(port.outputThread.joinable())
+ port.outputThread.join();
+
+ try
+ {
+ port.rtMidiOut.closePort();
+ } catch(const RtMidiError &)
+ {
+ }
+ auto it = std::find_if(m_openOutputs.begin(), m_openOutputs.end(), [&port](const auto &kv) { return &kv.second == &port; });
+ if(it != m_openOutputs.end())
+ m_openOutputs.erase(it);
+}
+
+
+MidiOutputHandle::~MidiOutputHandle()
+{
+ MidiDeviceManager::Instance().CloseOutput(*this);
+}
+
+
+void MidiOutputHandle::Send(mpt::const_byte_span data, int64 streamFrame)
+{
+ // TODO: make lock-free in the common case (ring buffer)
+ {
+ mpt::lock_guard lock{m_port.mutex};
+ m_port.queue.emplace_back(streamFrame, data);
+ }
+ m_port.cv.notify_one();
+}
+
+
+void MidiDeviceManager::UpdateSyncPoint(int64 syncPointStreamFrames, uint64 syncPointSystemTimestamp, double speed, uint32 sampleRate)
+{
+ m_syncPoint = {syncPointStreamFrames, syncPointSystemTimestamp, speed, sampleRate};
+}
+
+
+bool MidiDeviceManager::CheckDeviceStatus(const MidiInputHandle &handle)
+{
+ // This is really dirty, but I don't think there is a neater way, short of rewriting RtMidi.
+ // WinMM does not report on its own if a MIDI device was disconnected.
+ // One possibility is to call any API such as midiInGetNumDevs, which will then trigger a MIM_CLOSE message.
+ // But RtMidi does not handle MIM_CLOSE, and even if it did, it has no callback mechanism to notify us of such an event.
+ // So our only other option really is to try to do something non-destructive with the MIDI In handle and see if it's still valid.
+ // midiInGetID is not enough (it will return MMSYSERR_NOERROR), so midiInMessage is our next-best bet.
+ class MidiApiEx : public MidiApi
+ {
+ public:
+ bool IsValid()
+ {
+ if(isPortOpen())
+ return false;
+ if(getCurrentApi() != RtMidi::WINDOWS_MM)
+ return true;
+ HMIDIIN handle = *static_cast(apiData_); // First member of WinMidiData
+ ULONG size = 0;
+ return midiInMessage(handle, 0x813 /*DRV_QUERYDEVICEINTERFACESIZE*/, reinterpret_cast(&size), 0) != MMSYSERR_NODRIVER;
+ }
+ };
+
+ class RtMidiInEx : public RtMidiIn
+ {
+ public:
+ bool IsValid() const
+ {
+ return static_cast(rtapi_)->IsValid();
+ }
+ };
+
+ return static_cast(handle.m_port.rtMidiIn).IsValid();
+}
+
+
+OPENMPT_NAMESPACE_END
Property changes on: mptrack/MidiDeviceManager.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/x-c++src
\ No newline at end of property
Index: mptrack/MidiDeviceManager.h
===================================================================
--- mptrack/MidiDeviceManager.h (nonexistent)
+++ mptrack/MidiDeviceManager.h (working copy)
@@ -0,0 +1,220 @@
+/*
+ * MidiDeviceManager.h
+ * -------------------
+ * Purpose: MIDI device management, port sharing, and MIDI output thread handling
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+#include "mpt/mutex/mutex.hpp"
+
+#include
+#include