diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 55ab3be36..d08d610e7 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -25,21 +25,13 @@ "type": "string", "enum": [ "BuildAndPublishAll", - "T_BuildAndPackageElectronProjection", - "T_BuildAndPackAllAppSDKs", - "T_BuildAppSdkRuntimeAndToolsInstaller", - "T_BuildAppSDKToolsAndTests", - "T_BuildConsoleApp", - "T_BuildCppSamples", - "T_BuildCSharpSamples", - "T_BuildPowerShellProjection", - "T_BuildSettingsApp", - "T_BuildUserToolsSharedComponents", - "T_CopySharedDesignAssets", + "T_BuildServiceAndPlugins", + "T_BuildServiceAndPluginsInstaller", "T_CreateVersionIncludes", "T_Prerequisites", "T_ZipPowershellDevUtilities", - "T_ZipSamples" + "T_ZipServicePdbs", + "T_ZipWdmaud2" ] }, "Verbosity": { diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index 73b94e473..1a39f9494 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,5 +1,5 @@ - - - + + + diff --git a/build/staging/version/WindowsMidiServicesVersion.cs b/build/staging/version/WindowsMidiServicesVersion.cs index 7522e02a8..205a9fa67 100644 --- a/build/staging/version/WindowsMidiServicesVersion.cs +++ b/build/staging/version/WindowsMidiServicesVersion.cs @@ -9,15 +9,15 @@ public static class MidiNuGetBuildInformation { public const bool IsPreview = true; public const string Source = "GitHub Preview"; - public const string BuildDate = "2026-01-18"; - public const string Name = "SDK Release Candidate 1"; - public const string BuildFullVersion = "1.0.14-rc.1.213"; + public const string BuildDate = "2026-01-19"; + public const string Name = "Service Preview 14"; + public const string BuildFullVersion = "1.0.15-preview.14.73"; public const ushort VersionMajor = 1; public const ushort VersionMinor = 0; - public const ushort VersionPatch = 14; - public const ushort VersionBuildNumber = 213; - public const string Preview = "rc.1.213"; - public const string AssemblyFullVersion = "1.0.14.213"; - public const string FileFullVersion = "1.0.14.213"; + public const ushort VersionPatch = 15; + public const ushort VersionBuildNumber = 73; + public const string Preview = "preview.14.73"; + public const string AssemblyFullVersion = "1.0.15.73"; + public const string FileFullVersion = "1.0.15.73"; } } diff --git a/build/staging/version/WindowsMidiServicesVersion.h b/build/staging/version/WindowsMidiServicesVersion.h index 498434dd4..99b233470 100644 --- a/build/staging/version/WindowsMidiServicesVersion.h +++ b/build/staging/version/WindowsMidiServicesVersion.h @@ -7,14 +7,14 @@ #define WINDOWS_MIDI_SERVICES_NUGET_BUILD_IS_PREVIEW true #define WINDOWS_MIDI_SERVICES_NUGET_BUILD_SOURCE L"GitHub Preview" -#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_DATE L"2026-01-18" -#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_NAME L"SDK Release Candidate 1" -#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_FULL L"1.0.14-rc.1.213" +#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_DATE L"2026-01-19" +#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_NAME L"Service Preview 14" +#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_FULL L"1.0.15-preview.14.73" #define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_MAJOR 1 #define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_MINOR 0 -#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_PATCH 14 -#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_BUILD_NUMBER 213 -#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_PREVIEW L"rc.1.213" -#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_FILE L"1.0.14.213" +#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_PATCH 15 +#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_BUILD_NUMBER 73 +#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_PREVIEW L"preview.14.73" +#define WINDOWS_MIDI_SERVICES_NUGET_BUILD_VERSION_FILE L"1.0.15.73" #endif diff --git a/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.cpp b/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.cpp index 4c9975153..1dd5f8e96 100644 --- a/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.cpp +++ b/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.cpp @@ -315,4 +315,42 @@ void UmpIteratorTests::TestCopyWordsToVector() VERIFY_ARE_EQUAL(words[i], destination[i]); } +} + + +void UmpIteratorTests::TestGetMessageWordsByIndex() +{ + std::vector destination{}; + + uint32_t words[] = + { + 0x20000001, + 0x40000001, 0x01234567, + 0xF0000001, 0x18675309, 0x01010101, 0x02020202, + 0x40000001, 0x01234567, + 0x10000001, + 0x00000000, + }; + + std::vector readWords{}; + + UmpBufferIterator bufferIterator(words, ARRAYSIZE(words)); + for (auto it = bufferIterator.begin(); it < bufferIterator.end(); ++it) + { + uint8_t currentMessageWordCount = it.CurrentMessageWordCount(); + + for (uint8_t i = 0; i < currentMessageWordCount; i++) + { + readWords.push_back(it.GetCurrentMessageWord(i)); + } + } + + VERIFY_ARE_EQUAL(ARRAYSIZE(words), readWords.size()); + + // now, check values + for (uint32_t i = 0; i < ARRAYSIZE(words); i++) + { + VERIFY_ARE_EQUAL(words[i], readWords[i]); + } + } \ No newline at end of file diff --git a/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.h b/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.h index 10eb68d58..97fee07d7 100644 --- a/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.h +++ b/src/api/Test/Midi2.SharedIncludes.unittests/UmpIteratorTests.h @@ -36,6 +36,8 @@ class UmpIteratorTests TEST_METHOD(TestValidateCompleteBufferHasCompleteUmps); TEST_METHOD(TestGetMessageType); TEST_METHOD(TestCopyWordsToVector); + TEST_METHOD(TestGetMessageWordsByIndex); + private: diff --git a/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.cpp b/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.cpp index ec1f95bac..8f06f641a 100644 --- a/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.cpp +++ b/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.cpp @@ -20,7 +20,8 @@ _Use_decl_annotations_ void MidiUMPToBSTransformTests::InternalTestMessages( std::vector const words, - std::vector const expectedBytes + std::vector const expectedBytes, + std::vector const expectedGroups ) { wil::com_ptr_nothrow transformLib; @@ -48,13 +49,38 @@ void MidiUMPToBSTransformTests::InternalTestMessages( // set the callback - m_MidiInCallback = [&](PVOID payload, UINT payloadSize, LONGLONG /*payloadPosition*/, LONGLONG) + byte lastGroupIndex{ 127 }; // an invalid group index + int32_t currentGroupArrayIndex{ -1 }; + + m_MidiInCallback = [&](PVOID payload, UINT payloadSize, LONGLONG /*payloadPosition*/, LONGLONG context) { //std::cout << "callback" << std::endl; auto receivedBytes = static_cast(payload); - std::cout << "message received:" << std::endl; + std::cout << "message received for group " << context << ":" << std::endl; + + // validate the group index + if (expectedGroups.size() > 0) + { + // group index has changed + if (lastGroupIndex != context) + { + currentGroupArrayIndex++; + + // make sure we're not out of range on the group indexes + if (currentGroupArrayIndex >= expectedGroups.size()) + { + std::cout << "More group indexes reported than expected" << std::endl; + VERIFY_FAIL(); + } + + lastGroupIndex = expectedGroups[currentGroupArrayIndex]; + } + + VERIFY_ARE_EQUAL(context, lastGroupIndex); + } + for (uint32_t i = 0; i < payloadSize; i++) { @@ -68,8 +94,9 @@ void MidiUMPToBSTransformTests::InternalTestMessages( else { std::cout + << "rec: 0x" << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)receivedBytes[i] - << "(" << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)(expectedBytes[expectedBytesIndex]) << ") "; + << " (exp: 0x" << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)(expectedBytes[expectedBytesIndex]) << ") "; VERIFY_ARE_EQUAL(expectedBytes[expectedBytesIndex], receivedBytes[i]); @@ -178,5 +205,167 @@ void MidiUMPToBSTransformTests::TestChannelVoiceMessages() 0xCF, 0x1F, }; - InternalTestMessages(input, expectedOutput); + std::vector expectedGroups = + { + 0 + }; + + InternalTestMessages(input, expectedOutput, expectedGroups); +} + +void MidiUMPToBSTransformTests::TestMixedGroupMessages() +{ + std::vector input = + { + 0x20E01230, + 0x20E11231, + 0x22E21232, + 0x22E31233, + 0x20E41234, + 0x20E51235, + 0x22E61236, + 0x22E71237, + 0x24E81238, + 0x24E91239, + 0x22EA123A, + 0x22EB123B, + 0x29EC123C, + 0x20ED123D, + 0x29EE123E, + 0x21EF123F, + }; + + std::vector expectedOutput = + { + 0xE0, 0x12, 0x30, + 0xE1, 0x12, 0x31, + + 0xE2, 0x12, 0x32, + 0xE3, 0x12, 0x33, + + 0xE4, 0x12, 0x34, + 0xE5, 0x12, 0x35, + + 0xE6, 0x12, 0x36, + 0xE7, 0x12, 0x37, + + 0xE8, 0x12, 0x38, + 0xE9, 0x12, 0x39, + + 0xEA, 0x12, 0x3A, + 0xEB, 0x12, 0x3B, + + 0xEC, 0x12, 0x3C, + + 0xED, 0x12, 0x3D, + + 0xEE, 0x12, 0x3E, + + 0xEF, 0x12, 0x3F, + }; + + + // only needs to contain the index when the group index changes + std::vector expectedGroups = + { + 0, + 2, + 0, + 2, + 4, + 2, + 9, + 0, + 9, + 1 + }; + + InternalTestMessages(input, expectedOutput, expectedGroups); +} + + + +void MidiUMPToBSTransformTests::ValidateGithubIssue822() +{ + + // figure out the 14 bit value from the 32 bit value + + uint32_t data32_1 = 0x10000000; + uint16_t fourteenBit1 = (uint16_t)(data32_1 >> 18); + uint8_t fourteenBit1MSB = (uint8_t)((fourteenBit1 >> 7) & 0x7F); + uint8_t fourteenBit1LSB = (uint8_t)(fourteenBit1 & 0x7F); + + uint32_t data32_2 = 0x90000000; + uint16_t fourteenBit2 = (uint16_t)(data32_2 >> 18); + uint8_t fourteenBit2MSB = (uint8_t)((fourteenBit2 >> 7) & 0x7F); + uint8_t fourteenBit2LSB = (uint8_t)(fourteenBit2 & 0x7F); + + + std::vector input = + { + 0x40201020, data32_1, // group 0 MSB 10, LSB 20, Data 1 + 0x43201020, data32_2, // group 3 with same RPN MSB 10, LSB 20 + + 0x40201020, data32_1, // group 0 MSB 10, LSB 20 + 0x40201020, data32_2, // group 0 with same RPN MSB 10, and LSB 20 + + 0x40201020, data32_1, // group 0 MSB 10, LSB 20 + 0x43201121, data32_2, // group 3 MSB 11, LSB 21 + + 0x43201121, data32_1, // group 3 MSB 11, LSB 21 + }; + + + std::vector expectedOutput = + { + // group 0 + 0xb0, 0x65, 0x10, // NRPN MSB + 0xb0, 0x64, 0x20, // NRPN LSB + 0xb0, 0x06, fourteenBit1MSB, // NRPN data 1 (value / MSB) + 0xb0, 0x26, fourteenBit1LSB, // NRPN data 2 (fine adjustment / LSB) + + // group 3 + 0xb0, 0x65, 0x10, // NRPN MSB + 0xb0, 0x64, 0x20, // NRPN LSB + 0xb0, 0x06, fourteenBit2MSB, // NRPN data 1 (value / MSB) + 0xb0, 0x26, fourteenBit2LSB, // NRPN data 2 (fine adjustment / LSB) + + // group 0 + 0xb0, 0x65, 0x10, // NRPN MSB + 0xb0, 0x64, 0x20, // NRPN LSB + 0xb0, 0x06, fourteenBit1MSB, // NRPN data 1 (value / MSB) + 0xb0, 0x26, fourteenBit1LSB, // NRPN data 2 (fine adjustment / LSB) + + // group 0 again with same MSB/LSB for NRPN, so just data comes through + 0xb0, 0x06, fourteenBit2MSB, // NRPN data 1 (value / MSB) + 0xb0, 0x26, fourteenBit2LSB, // NRPN data 2 (fine adjustment / LSB) + + // group 0 again + 0xb0, 0x06, fourteenBit1MSB, // NRPN data 1 (value / MSB) + 0xb0, 0x26, fourteenBit1LSB, // NRPN data 2 (fine adjustment / LSB) + + // group 3, but different NRPN LSB/MSB + 0xb0, 0x65, 0x11, // NRPN MSB + 0xb0, 0x64, 0x21, // NRPN LSB + 0xb0, 0x06, fourteenBit2MSB, // NRPN data 1 (value / MSB) + 0xb0, 0x26, fourteenBit2LSB, // NRPN data 2 (fine adjustment / LSB) + + // group 3 again, same NRPN LSB/MSB + 0xb0, 0x06, fourteenBit1MSB, // NRPN data 1 (value / MSB) + 0xb0, 0x26, fourteenBit1LSB, // NRPN data 2 (fine adjustment / LSB) + + }; + + + // only needs to contain the index when the group index changes + std::vector expectedGroups = + { + 0, + 3, + 0, + 3, + }; + + InternalTestMessages(input, expectedOutput, expectedGroups); + } diff --git a/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.h b/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.h index 7a791b10f..d3c41c8d7 100644 --- a/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.h +++ b/src/api/Test/Midi2.Transform.unittests/MidiUMPToBSTransformTests.h @@ -29,10 +29,13 @@ class MidiUMPToBSTransformTests //TEST_METHOD_CLEANUP(TestCleanup); TEST_METHOD(TestChannelVoiceMessages); + TEST_METHOD(TestMixedGroupMessages); + TEST_METHOD(ValidateGithubIssue822); void InternalTestMessages( _In_ std::vector words, - _In_ std::vector const expectedBytes); + _In_ std::vector const expectedBytes, + _In_ std::vector const expectedGroups); STDMETHOD(Callback)(_In_ MessageOptionFlags, _In_ PVOID Data, _In_ UINT Size, _In_ LONGLONG Position, LONGLONG Context) { diff --git a/src/api/Transform/UMPToByteStream/Midi2.UMP2BSMidiTransform.cpp b/src/api/Transform/UMPToByteStream/Midi2.UMP2BSMidiTransform.cpp index 355791c4b..f36683405 100644 --- a/src/api/Transform/UMPToByteStream/Midi2.UMP2BSMidiTransform.cpp +++ b/src/api/Transform/UMPToByteStream/Midi2.UMP2BSMidiTransform.cpp @@ -4,6 +4,8 @@ #include "midi2.UMP2BSTransform.h" +#include "ump_iterator.h" + _Use_decl_annotations_ HRESULT CMidi2UMP2BSMidiTransform::Initialize( @@ -83,29 +85,83 @@ CMidi2UMP2BSMidiTransform::SendMidiMessage( ); #endif + RETURN_HR_IF(E_INVALIDARG, length < sizeof(uint32_t)); + // can only transform 1 set of messages at a time auto lock = m_SendLock.lock(); + WindowsMidiServicesInternal::UmpBufferIterator bufferIterator(static_cast(inputData), length / sizeof(uint32_t)); // we can keep this as a local because of how the callback works std::vector translatedBytes{}; translatedBytes.reserve(length); // as an approximation of output data size, this is reasonable + byte currentTranslatedBytesGroupIndex = 127; - // Send the UMP(s) to the parser - uint32_t *data = (uint32_t *)inputData; - for (UINT i = 0; i < (length / sizeof(uint32_t)); i++) - { - m_UMP2BS.UMPStreamParse(data[i]); + auto it = bufferIterator.begin(); - // retrieve the bytestream message from the parser and add to our translated data - while (m_UMP2BS.availableBS()) + while (it < bufferIterator.end()) + { + if (it.CurrentMessageSeemsComplete()) + { + auto currentMessageWordCount = it.CurrentMessageWordCount(); + + if (it.CurrentMessageGroupIndex() != currentTranslatedBytesGroupIndex) + { + // send the translated version of everything we've received in this call + if (translatedBytes.size() > 0) + { + // For transforms, by convention the context contains the group index. + auto hr = m_Callback->Callback( + (MessageOptionFlags)(optionFlags | MessageOptionFlags_ContextContainsGroupIndex), + static_cast(translatedBytes.data()), + static_cast(translatedBytes.size()), + position, + currentTranslatedBytesGroupIndex); + + if (FAILED(hr)) + { + m_UMP2BS.resetBuffer(); + RETURN_IF_FAILED(hr); + } + + // clear the transmitted bytes + translatedBytes.clear(); + } + + // workaround because UMP2BS short-circuits RPN/NRPN msb/lsb across groups + //uint8_t gr = m_UMP2BS.group; + m_UMP2BS.resetBuffer(); + //m_UMP2BS.group = gr; + // end workaround + + currentTranslatedBytesGroupIndex = it.CurrentMessageGroupIndex(); + } + + // parse entire single message + for (uint8_t i = 0; i < currentMessageWordCount; i++) + { + m_UMP2BS.UMPStreamParse(it.GetCurrentMessageWord(i)); + } + + while (m_UMP2BS.availableBS()) + { + translatedBytes.push_back(m_UMP2BS.readBS()); + } + } + else { - translatedBytes.push_back(m_UMP2BS.readBS()); + // incomplete UMP + m_UMP2BS.resetBuffer(); + RETURN_IF_FAILED(E_INVALIDARG); } + + ++it; // moves to next message, not next word } - // send the translated version of everything we've received in this call + + + // get anything from the last spin round if (translatedBytes.size() > 0) { // For transforms, by convention the context contains the group index. diff --git a/src/app-sdk/tools/midicheckservice/midicheckservice_main.cpp b/src/app-sdk/tools/midicheckservice/midicheckservice_main.cpp index 1b6ccc1ca..73c4cf101 100644 --- a/src/app-sdk/tools/midicheckservice/midicheckservice_main.cpp +++ b/src/app-sdk/tools/midicheckservice/midicheckservice_main.cpp @@ -274,6 +274,16 @@ bool VerifyWdmaud2Registry() +void PauseIfAppropriate() +{ + if (!m_optionQuiet) + { + system("pause"); + } + +} + + int __cdecl wmain(_In_ int argc, _In_ WCHAR* argv[]) { @@ -285,6 +295,7 @@ int __cdecl wmain(_In_ int argc, _In_ WCHAR* argv[]) if (!ParseCommandLine(argc, argv)) { // user picked help or some other option which skips evaluation + PauseIfAppropriate(); return static_cast(MIDISRV_CHECK_RETURN_VALUE_CHECK_SKIPPED); } @@ -314,6 +325,8 @@ int __cdecl wmain(_In_ int argc, _In_ WCHAR* argv[]) else { WriteInfo("wdmaud2.drv is not present in registry in values midi-midi9. Most likely, the feature has not yet been enabled on this PC."); + + PauseIfAppropriate(); return static_cast(MIDISRV_CHECK_RETURN_VALUE_NOT_ENABLED_IN_REGISTRY); } @@ -328,6 +341,7 @@ int __cdecl wmain(_In_ int argc, _In_ WCHAR* argv[]) { WriteInfo("Successfully tested connectivity to service: MIDI Service is available and running from System32."); + PauseIfAppropriate(); return static_cast(MIDISRV_CHECK_RETURN_VALUE_SUCCESS); } else @@ -336,6 +350,7 @@ int __cdecl wmain(_In_ int argc, _In_ WCHAR* argv[]) WriteInfo("However, this appears to be a development build, and so may not have the right"); WriteInfo("connections to the MIDI 1.0 APIs, the MIDI 2.0 class driver, etc."); + PauseIfAppropriate(); return static_cast(MIDISRV_CHECK_RETURN_VALUE_SUCCESS_DEV_BUILD); } } @@ -350,6 +365,7 @@ int __cdecl wmain(_In_ int argc, _In_ WCHAR* argv[]) ShutdownMidisrv(); } + PauseIfAppropriate(); return static_cast(MIDISRV_CHECK_RETURN_VALUE_NOT_ENABLED_OR_NOT_STARTED); } @@ -362,6 +378,7 @@ int __cdecl wmain(_In_ int argc, _In_ WCHAR* argv[]) ShutdownMidisrv(); // service transport is not available. + PauseIfAppropriate(); return static_cast(MIDISRV_CHECK_RETURN_VALUE_NOT_INSTALLED); } }