Skip to content

Commit

Permalink
Merge pull request #290 from microsoft/pete-dev
Browse files Browse the repository at this point in the history
Most of the remaining (non-docs) updates for Dev Preview 5
  • Loading branch information
Psychlist1972 authored Mar 6, 2024
2 parents a857b8e + 574fe9a commit 3517650
Show file tree
Hide file tree
Showing 269 changed files with 7,799 additions and 5,688 deletions.
4 changes: 2 additions & 2 deletions build/build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ Task("BuildSettingsApp")
// TODO: Update nuget ref in console app to the new version
Information("\nBuilding MIDI settings app for " + plat.ToString());
Information("\nBuilding MIDI settings app for " + plat.ToString() + " " + configuration);
// update nuget packages for the entire solution. This is important for API/SDK NuGet in particular
Expand Down Expand Up @@ -596,7 +596,7 @@ Task("BuildInstaller")
.IsDependentOn("BuildServiceAndAPI")
.IsDependentOn("BuildApiActivationRegEntriesCSharp")
/* .IsDependentOn("BuildSDK") */
/* .IsDependentOn("BuildSettingsApp") */
.IsDependentOn("BuildSettingsApp")
.IsDependentOn("BuildConsoleApp")
.DoesForEach(platformTargets, plat =>
{
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified build/dependencies/vc_redist.x64.exe
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 0 additions & 2 deletions build/staging/reg/WinRTActivationEntries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ class RegistryEntries
new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiStreamMessageBuilderStatics", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiMessageConverter", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiMessageConverterStatics", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiMessageTranslator", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiMessageTranslatorStatics", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiMessageUtility", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiMessageUtilityStatics", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiFunctionBlock", ActivationType=0, Threading=0, TrustLevel=0 },
Expand Down
10 changes: 0 additions & 10 deletions build/staging/reg/WinRTActivationEntries.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,6 @@
threading="Both"
trustLevel="Base"
/>
<class
activatableClassId="Windows.Devices.Midi2.MidiMessageTranslator"
threading="Both"
trustLevel="Base"
/>
<class
activatableClassId="Windows.Devices.Midi2.IMidiMessageTranslatorStatics"
threading="Both"
trustLevel="Base"
/>
<class
activatableClassId="Windows.Devices.Midi2.MidiMessageUtility"
threading="Both"
Expand Down
2 changes: 1 addition & 1 deletion build/staging/version/BundleInfo.wxi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Include>
<?define SetupVersionName="Developer Preview 5" ?>
<?define SetupVersionNumber="1.0.24054.2210" ?>
<?define SetupVersionNumber="1.0.24066.0034" ?>
</Include>
49 changes: 26 additions & 23 deletions docs/config-json.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ JSON is typically case-sensitive for all keys. The Windows.Data.Json parser used

```json
"{26FA740D-469C-4D33-BEB1-3885DE7D6DF1}":
{
"_comment": "KS MIDI (USB etc.)"
}
{
"_comment": "KS MIDI (USB etc.)"
}
```
and
```json
"{26fa740d-469c-4d33-beb1-3885de7d6df1}":
{
"_comment": "KS MIDI (USB etc.)"
}
{
"_comment": "KS MIDI (USB etc.)"
}
```

## Schema
Expand Down Expand Up @@ -71,41 +71,44 @@ Here's an example of a bare-bones file, with sections for three different transp

### Endpoint Properties

The basics of this are identical for each transport. We'll use KS (USB) as an example
> NOTE: This section is in flux, as we're changing how devices are identified, and how properties are set.
The basics of this are identical for each transport. We'll use KS (USB) as an example as it has the most complex lookup mechanisms to attempt to identify devices, even when they are moved from port to port.

```json
"{26FA740D-469C-4D33-BEB1-3885DE7D6DF1}":
{
"_comment": "KS MIDI (USB etc.)"
"SWD: \\\\?\\SWD#MIDISRV#MIDIU_KS_BIDI_6799286025327820155_OUTPIN.0_INPIN.2#{e7cce071-3c03-423f-88d3-f1045d02552b}":
{
"userSuppliedName" : "Pete's Kontrol S61",
"userSuppliedDescription" : "This is my most favorite MIDI 2.0 controller in the whole world!"
}

"_comment": "KS MIDI (USB etc.)",
"update":
[
{
"match":
{
"SWD": "\\\\?\\swd#midisrv#midiu_ks_bidi_6051189816177518400_outpin.0_inpin.2#{e7cce071-3c03-423f-88d3-f1045d02552b}"
},
"_comment" : "Roland A88 mk2",
"userSuppliedName" : "Pete's A88",
"userSuppliedDescription" : "The A88 is the giant MIDI 2.0 piano-action keyboard here in my studio."
},
{
...
}
]
},
```

Of those, the identification method `SWD` is the most important. This controls how we identify a matching device. In cases where the manufacturer doesn't supply a unique iSerialNumber in USB, unplugging your device from one USB port and plugging it into another can result in a new Id. Similarly, if you have two or more of the same device, and they do not have unique serial numbers, it can be impossible for Windows to distinguish between them.

Valid values for the identification method prefix

- `SWD: ` : (The colon and trailing space are required). Use the full Windows Endpoint Device Interface Id. For example `\\\\?\\SWD#MIDISRV#MIDIU_KS_BIDI_16024944077548273316_OUTPIN.0_INPIN.2#{e7cce071-3c03-423f-88d3-f1045d02552b}`. (Note how the backslashes have to be escaped with additional backslashes.) If the device has an `iSerialNumber` or you never move it between USB ports, this tends to work fine.
- (other methods to be added here when we implement them)

Valid properties you can set across all supported endpoints

| Property | Type | Description |
| -------- | ---- | ----------- |
| `userSuppliedName` | Quoted Text | The name you want to use for the endpoint. This will override the name displayed in correctly-coded applications, but won't necessarily change what you see in Device Manager. These names should be relatively short so they display fully in all/most applications, but meaningful to you. |
| `userSuppliedDescription` | Quoted Text | A text description and/or notes about the endpoint. Applications may or may not use this data |
| `forceSingleClientOnly` | Boolean true/false (no quotes) | Most endpoints are multi-client (more than one application can use them simultaneously) by default. This setting is for forcing an endpoint to be single-client only (a value of true). It's unusual to need this, but a typical use may be to disable multi-client for a device which has a custom driver which doesn't gracefully handle multiple client applications at the same time. |

## Plugin-specific settings

This is not an exhaustive list, because the transport and processing plugins may be created by anyone.

### Virtual MIDI
### Loopback MIDI

TODO: Provide examples for this

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Currently, in the implementation behind the scenes, the service receives each ti
| Property | Description |
| -------- | ----------- |
| `ConnectionId` | The generated GUID which uniquely identifes this connection instance. This is what is provided to the `MidiSession` when disconnecting an endpoint |
| `EndpointDeviceId` | The system-wide identifier for the device connection. This is returned through enumeration calls. |
| `EndpointDeviceId` | The system-wide identifier for the endpoint device. This is returned through enumeration calls. |
| `Tag` | You may use this `Tag` property to hold any additional information you wish to have associated with the connection. |
| `IsOpen` | True if this connection is currently open. When first created, the connection is not open until the consuming code calls the `Open` method |
| `Settings` | Settings used to create this connection. Treat this as read-only. |
Expand All @@ -43,14 +43,14 @@ Currently, in the implementation behind the scenes, the service receives each ti

| Function | Description |
| -------- | ----------- |
| `SendMessagePacket(message)` | Send an `IMidiUniversalPacket`-implementing type such as `MidiMessage64` or a strongly-typed message class. |
| `SendMessageStruct(timestamp, message, wordCount)` | Send a fixed-sized `MidiMessageStruct` containing `wordCount` valid words. Additional words are ignored. |
| `SendMessageWordArray(timestamp, words, startIndex, wordCount)` | Note: Some projections will send the entire array as a copy, so this may not be the most effecient way to send messages from your language. |
| `SendMessageWords(timestamp, word0)` | Send a single 32-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendMessageWords(timestamp, word0, word1)` | Send a single 64-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendMessageWords(timestamp, word0, word1, word2)` | Send a single 96-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendMessageWords(timestamp, word0, word1, word2, word3)` | Send a single 128-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendMessageBuffer(timestamp, buffer, byteOffset, byteCount)` | Send a single Universal MIDI Packet as bytes from a buffer. The number of bytes sent must match the size read from the first 4 bits of the data starting at the specified offset, and must be laid out correctly with the first byte corresponding to the MSB of the first word of the UMP (the word which contains hte message type). If you want to manage a chunk of buffer memory, the `IMemoryBuffer` type is the acceptable WinRT approach, and is as close as you get to sending a pointer into a buffer. |
| `SendSingleMessagePacket(message)` | Send an `IMidiUniversalPacket`-implementing type such as `MidiMessage64` or a strongly-typed message class. |
| `SendSingleMessageStruct(timestamp, message, wordCount)` | Send a fixed-sized `MidiMessageStruct` containing `wordCount` valid words. Additional words are ignored. |
| `SendSingleMessageWordArray(timestamp, startIndex, wordCount, words)` | Send an array of words for a single message. Note: Some projections will send the entire array as a copy, so this may not be the most effecient way to send messages from your language. |
| `SendSingleMessageWords(timestamp, word0)` | Send a single 32-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendSingleMessageWords(timestamp, word0, word1)` | Send a single 64-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendSingleMessageWords(timestamp, word0, word1, word2)` | Send a single 96-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendSingleMessageWords(timestamp, word0, word1, word2, word3)` | Send a single 128-bit Universal MIDI Packet as 32-bit words. This is often the most efficient way to send this type of message |
| `SendSingleMessageBuffer(timestamp, byteOffset, byteCount, buffer)` | Send a single Universal MIDI Packet as bytes from a buffer. The number of bytes sent must match the size read from the first 4 bits of the data starting at the specified offset, and must be laid out correctly with the first byte corresponding to the MSB of the first word of the UMP (the word which contains hte message type). If you want to manage a chunk of buffer memory, the `IMemoryBuffer` type is the acceptable WinRT approach, and is as close as you get to sending a pointer into a buffer. |

> Tip: In all the functions which accept a timestamp to schedule the message, **you can send a timestamp of 0 (zero) to bypass the scheduler and send the message immediately** or use the `MidiClock::TimestampConstantSendImmediately` static property. Otherwise, the provided timestamp is treated as an absolute time for when the message should be sent from the service. Note that the service-based scheduler (currently based on a `std::priority_queue`) gets less efficient when there are thousands of messages in it, so it's recommended that you not schedule too many messages at a time or too far out into the future.
Expand All @@ -64,9 +64,16 @@ Finally, there's a practical limit to how many messages can be scheduled ahead o

| Function | Description |
| -------- | ----------- |
| `SendMultipleMessagesWordList(timestamp,words)` | When supplied an `IIterable` of 32 bit unsigned integers, this sends more than one message with the same timestamp. Message words must be ordered contiguously from word-0 to word-n for each message, and the message types must be valid for the number of words for each message. If an error is encountered when sending messages, the function stops processing the list at that point and returns a failure code, even if some messages were sent successfully. |
| `SendMultipleMessagesWordList(timestamp, words)` | When supplied an `IIterable` of 32 bit unsigned integers, this sends more than one message with the same timestamp. Message words must be ordered contiguously from word-0 to word-n for each message, and the message types must be valid for the number of words for each message. All messages are sent with the same timestamp.|
| `SendMultipleMessagesWordArray(timestamp, startIndex, wordCount, words)` | Similar to the WordList approach, this will send multiple messages from an array, starting at the zero-based `startIndex` and continuing for `wordCount` words. The messages within that range must be valid and complete. All messages are sent with the same timestamp.|
| `SendMultipleMessagesPacketList(messages)` | Send an `IIterable` of `IMidiUniversalPacket` messages, each with their own timestamp. |
| `SendMultipleMessagesBuffer(buffer, byteOffset, byteCount)` | Send multiple messages using the `IMemoryBuffer` approach and a single timestamp. |
| `SendMultipleMessagesStructList(timestamp, messages)` | Send an `IIterable` of `MidiMessageStruct` messages. All messages are sent with the same timestamp|
| `SendMultipleMessagesStructArray(timestamp, startIndex, messageCount, messages)` | Send an an array of `MidiMessageStruct` messages, starting at `startIndex` and continuing for `messageCount` messages. All messages are sent with the same timestamp|
| `SendMultipleMessagesBuffer(timestamp, byteOffset, byteCount, buffer)` | Send multiple messages using the `IMemoryBuffer` approach and a single timestamp. |

When sending multiple messages, there is no implied all-or-nothing transaction. If an error is encountered when sending messages, the function stops processing the list at that point and returns a failure code, even if some messages were sent successfully.

> Tip: To learn more about how collections are handled in WinRT, and how they may convert to constructs like `std::vector`, see the [Collections with C++/WinRT](https://learn.microsoft.com/windows/uwp/cpp-and-winrt-apis/collections) page in our documentation.
## Other Functions

Expand Down
23 changes: 22 additions & 1 deletion docs/developer-docs/midi2-implementation-details.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,28 @@ Of course, the full source code for Windows MIDI Services, including the USB MID

Windows MIDI Services supports only the UMP-based Endpoint Discovery and Protocol Negotiation. We do not implement the deprecated MIDI-CI equivalents.

In addition, declaring the use of JR Timestamps in a USB MIDI 2.0 Group Terminal Block does not enable JR Timestamps in Windows MIDI Services. Instead, these must be negotiated using UMP-based Endpoint Discovery end Protocol Negotiation
## JR Timestamps

The hardware manufacturers, AMEI, and the MIDI Association agreed that JR Timestamp handling in the operating systems is not needed at this time (and may not be needed for many years). As a result, we do not have JR Timestamp handling built in.

When (if) it is needed, we will implement clock generation, incoming timestamp generation, and outgoing timestamp/clock message creation in the service itself. Client applications should not send JR timestamps now or in the future.

## Message Translation

Internally in Windows MIDI Services, all messages are UMP messages, whether they come from a MIDI 1.0 byte stream data format device, a UMP-native device or API, or a MIDI 1.0 classic API. This requires Windows to perform message data format translation in some cases.

Windows MIDI Services performs required message data format translation in these cases:

- When a classic (WinMM or WinRT) MIDI 1.0 API accesses a UMP device. In those cases, the UMP messages need to be translated to MIDI 1.0 protocol and MIDI 1.0 byte stream data format when being sent to the classic API. The reverse is also true here. This is done in the Windows Service.
- When a MIDI 1.0 byte stream data format device is connected to the new combined MIDI 1.0/MIDI 2.0 class driver, we will translate between MIDI 1.0 protocol UMP data format and MIDI 1.0 protocol byte stream data format as required to communicate with the device. This is done in the new driver.
- When a MIDI 1.0 driver is used with a MIDI 1.0 device (third-party USB, BLE MIDI 1.0, etc.), we translate data coming from and going to that driver in the Windows service.

Windows MIDI Services does not translate messages in these scenarios:

- When a native UMP device is connected to the new driver, all messages are simply a pass-through to the API. The service does not do any translation between MIDI 1.0 protocol in UMP and MIDI 2.0 protocol in UMP, regardless of the negotiated stream protocol. Applications should instead inspect the properties of the device using our enumeration classes, and constrain sent messages appropriately.
- When a native UMP endpoint is connected to the service through another transport (like Network MIDI 2.0), we do not translate the protocol of any messages.

> If a UMP-native device does not properly participate in discovery and protocol negotiation, we report it as a MIDI 2.0 protocol device.
## UMP Endpoint Names for native MIDI 2.0 UMP format devices

Expand Down
1 change: 1 addition & 0 deletions samples/csharp-net/app-to-app-midi-cs/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ private void OpenConnection()

if (_connection != null)
{
System.Diagnostics.Debug.WriteLine("Created endpoint id: " + _connection.EndpointDeviceId);
System.Diagnostics.Debug.WriteLine("Connection created. Wiring up MessageReceived event");

_connection.MessageReceived += _connection_MessageReceived;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26031-preview" />
<PackageReference Include="Windows.Devices.Midi2" Version="1.0.0-preview.5-0172" />
<PackageReference Include="WinUIEx" Version="2.3.3" />
<PackageReference Include="Windows.Devices.Midi2" Version="1.0.0-preview.5-0184" />
<PackageReference Include="WinUIEx" Version="2.3.4" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>

Expand Down
Loading

0 comments on commit 3517650

Please sign in to comment.