Skip to content

Commit d7802db

Browse files
Merge pull request #351 from microsoft/pete-dev
Virtual device and docs updates
2 parents 7100a16 + 74176d9 commit d7802db

File tree

901 files changed

+15867
-4270
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

901 files changed

+15867
-4270
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
0 Bytes
Binary file not shown.

build/staging/version/BundleInfo.wxi

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<Include>
22
<?define SetupVersionName="Developer Preview 6 x64" ?>
3-
<?define SetupVersionNumber="1.0.24171.1742" ?>
3+
<?define SetupVersionNumber="1.0.24173.1522" ?>
44
</Include>

docs/developer-how-to/how-to-check-for-windows-midi-services.md

+11-13
Original file line numberDiff line numberDiff line change
@@ -46,37 +46,36 @@ else
4646

4747
### How this works
4848

49-
The Windows Service is triggered to start via a specific call, which triggers an ETL event. The ETL event is what the service watches for to spin up. Typically, this takes only a second or two to happen.
49+
The Windows Service is triggered to start via `EnsureServiceAvailable()`, which calls the service interface and triggers an ETL event. The ETL event is what the service watches for to spin up. Typically, this takes only a second or two to happen.
5050

51-
Once the service is started, all the devices it is responsible for begin to be enumerated.
51+
Once the service hass started, all the devices it is responsible for begin to be enumerated. It will remain running until manually shut down or the PC is rebooted.
5252

53-
Once the service is demand-started, it remains running until manually shut down or the next reboot.
53+
> Musicians may want to set the service to auto-start with Windows. It adds a little bit of time to Windows startup, but the devices will be available when needed.
5454
5555
## Bootstrap the Windows MIDI Services SDK runtime (Desktop apps only)
5656

5757
For desktop apps, other than the initializer, the rest of the SDK is installed centrally on the PC rather than shipped with applications. This enables bug and security fixes without asking each software developer to ship a new version of their application.
5858

5959
Because the SDK is shipped out-of-band from Windows itself, and is restricted to specific versions of Windows and devices (no current support on Xbox and Hololens, for example) the SDK must be bootstrapped/initialized so the application can find the WinRT types contained within.
6060

61-
Internally, the initializer uses a combination of Registration-free WinRT and the Detours library to hook into type resolution. To support the use of the initializer, the application must include a manifest file, named `AppName.exe.manifest` where "AppName" is the name of the executable.
61+
Internally, the initializer uses a combination of Registration-free WinRT and the Detours library to hook into type resolution and activation. To support the use of the initializer, the application must include an entry in the application manifest file, named `AppName.exe.manifest` where "AppName" is the name of the executable.
6262

6363
Manifest contents:
6464

6565
```xml
6666
<?xml version="1.0" encoding="utf-8"?>
67-
6867
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
6968
<assemblyIdentity version="1.0.0.0" name="MyWindowsMidiServicesApplication.app"/>
69+
<!-- This is the only manifest entry needed for desktop apps using MIDI -->
7070

71-
<!-- This is the only manifest entry needed for desktop apps -->
7271
<!-- This specific DLL is shipped with the app itself -->
7372
<file name="Microsoft.Windows.Devices.Midi2.Initialization.dll">
7473
<activatableClass
7574
name="Microsoft.Windows.Devices.Midi2.Initialization.IMidiServicesInitializerStatics"
7675
threadingModel="both"
7776
xmlns="urn:schemas-microsoft-com:winrt.v1" />
7877
</file>
79-
78+
<!-- Other manifest entries are not impacted by this entry -->
8079
</assembly>
8180
```
8281

@@ -89,18 +88,17 @@ In addition to the manifest, the application must include the `Microsoft.Windows
8988
If the runtime is not present, but the service is present, the application can either prompt the user to download and install the runtime. That is an application-specific decision.
9089

9190
```cpp
92-
auto uri = (MidiServicesInitializer::GetLatestRuntimeReleaseInstallerUri())
91+
auto uri = MidiServicesInitializer::GetLatestRuntimeReleaseInstallerUri();
9392
```
9493

95-
> Your desktop application installer can also include code to download and install the latest Windows MIDI Services runtime, rather than doing this after the application has already started.
94+
> Your desktop application's installer can also include code to download and install the latest Windows MIDI Services runtime, rather than doing this after the application has already started.
9695
9796
## Use the SDK from packaged (Store etc.) apps
9897

99-
If the app is packaged using MSIX, the bootstrapper is not used. Instead, the app must declare all Windows MIDI Services WinRT types in its manifest. It must also declare a dependency on ... TODO
100-
101-
102-
## Sample Code
98+
If the app is packaged using MSIX, the bootstrapper is not used. Instead, the app must declare all Windows MIDI Services WinRT types in its manifest. It must also declare a dependency on ...
10399

104100
TODO
105101

102+
## Sample Code
106103

104+
The existing samples are in the process of being updated with this new bootstrapping code.

docs/developer-how-to/how-to-create-loopback-endpoints.md

+5-13
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
---
22
layout: page
3-
title: Create Loopback Endpoints= Pairs
3+
title: Create Loopback Endpoint Pairs
44
parent: Developer How-to
55
has_children: false
66
---
77

88
# How to create simple Loopback Endpoint Pairs at runtime
99

10-
## How the Loopback Endpoint Pair works
11-
12-
13-
## How to create a Loopback Endpoint Pair
14-
1510
We'll assume here you've already initialized Windows MIDI Services and created a session.
1611

1712
First, you define the two sides of the loopback. Because UMP endpoints are bidirectional, the loopback works from either direction: Messages set out on A arrive in on B, and those sent out on B arrive in on A.
@@ -31,7 +26,7 @@ definitionB.Description = L"The second description is optional, but is displayed
3126
definitionB.UniqueId = L"3263827-OU812-5150"; // can be the same as the first one, but doesn't need to be.
3227
```
3328

34-
Next, create the transient (meaning they are not in the config file and recreated after a reboot) loopback endpoint pair using the above definitions
29+
Next, create the transient (meaning they are not in the config file and therefore are not recreated after a reboot) loopback endpoint pair using the above definitions
3530

3631
```cpp
3732
MidiLoopbackEndpointCreationConfig creationConfig(m_associationId, definitionA, definitionB);
@@ -40,7 +35,7 @@ auto response = MidiLoopbackEndpointManager::CreateTransientLoopbackEndpoints(cr
4035

4136
if (response.Success)
4237
{
43-
std::wcout << L"Endpoints created successfully" << std::endl << std::endl;
38+
std::cout << "Endpoints created successfully" << std::endl << std::endl;
4439

4540
std::cout
4641
<< "Loopback Endpoint A: " << std::endl
@@ -60,18 +55,15 @@ else
6055
// failed to create the loopback pair. It may be that the unique
6156
// Ids are already in use.
6257
}
63-
6458
```
6559
66-
One thing you may have noticed in the listing above is the use of an association Id. This identifier is just a GUID you generate to associate the endpoint pairs together. This is what establishes the relationship between them.
60+
One thing you may have noticed in the listing above is the use of an **association Id**. This identifier is a GUID you generate and then use to associate the endpoint pairs together. This is what establishes the relationship between the two endpoints.
6761
6862
```cpp
6963
winrt::guid m_associationId = winrt::Windows::Foundation::GuidHelper::CreateNewGuid();
7064
```
7165

72-
That's all that's needed. You can connect to either endpoint and use it as you would any other.
73-
74-
> Note: Loopback Endpoint Pairs are not currently visible to the WinMM MIDI 1.0 API. There are complexities with that API when devices are added and removed at runtime. It's possible these devices will never be visibile to WinMM MIDI 1.0. For full functionality, we recommend using the new Windows MIDI Services SDK.
66+
That's all that's needed. You can connect to and open either endpoint and use it as you would any other.
7567

7668
## Sample Code
7769

docs/developer-how-to/how-to-create-virtual-ump-device.md

+125-15
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,145 @@ parent: Developer How-to
55
has_children: false
66
---
77

8-
# How to Create Virtual UMP Devices at Runtime
8+
# How to Create Virtual Devices at Runtime
99

1010
If you develop an application which should appear as a new MIDI device to other applications on Windows, you want to create a Virtual UMP Device. Your app may be a controller app, a sound generator/synthesizer, or a bridge to accessibility or other controllers. Anything a hardware MIDI device can do is open to you here.
1111

12+
## How Virtual Devices work
13+
14+
A virtual device enables an application to appear as a UMP Endpoint to other applications.
15+
16+
[More information on how Virtual Devices work may be found here](../endpoints/virtual-device-app.md).
17+
1218
## Steps to Create a Virtual Device
1319

14-
1. Create a MIDI session
15-
2. Define the Virtual MIDI Device, its function blocks, and other properties
16-
3. Create the Device and get the `EndpointDeviceId` for the device application
17-
4. Connect to the Device like any other MIDI connection
18-
5. Wire up event handlers for message received and optionally for protocol negotiation
19-
6. Open the connection
20-
7. Respond to any protocol negotiation or message received events
21-
8. When the application no longer needs to expose the virtual device, close the connection.
20+
1. [Check for and bootstrap Windows MIDI Services](./how-to-check-for-windows-midi-services.md)
21+
2. Create a MIDI session
22+
3. Define the Virtual MIDI Device, its function blocks, and other properties
23+
4. Create the Device and get the `EndpointDeviceId` for the device-side application endpoint
24+
5. Connect to the Device like any other MIDI connection
25+
6. Wire up event handlers for message received and optionally for stream configuration
26+
7. Open the connection
27+
8. Respond to any protocol negotiation or message received events
28+
9. When the application no longer needs to expose the virtual device, close the connection.
2229

2330
On the service-side, the Virtual Device works like any other native UMP MIDI 2.0 device, including for endpoint metadata capture and protocol negotiation.
2431

25-
More details available in the Endpoints documentation.
32+
## Code
2633

27-
### Example
34+
We'll assume you've already performed the [Windows MIDI Services bootstrapping steps](./how-to-check-for-windows-midi-services.md).
2835

29-
TODO
36+
The first step is to define the virtual device by creating the different metadata declarations and then assemble them together using the `MidiVirtualDeviceCreationConfig` type.
3037

38+
This information is all required so that the virtual device responder can handle the MIDI 2.0 endpoint discovery and protocol negotiation messages on your behalf. This removes the complexity of message parsing and (in the case of names and ids) message assembly.
3139

40+
> When creating the device's software device id (SWD) only the first 32 characters of the `ProductInstanceId` are used. This must be unique among all **virtual UMP devices** currently running in Windows MIDI Services, or else the device creation will fail. One recommendation for uniqueness is to use a GUID with all non-alphanumeric characters removed. Another would be to use the app name and an internal index or differentiator.
3241
42+
```cpp
43+
// endpoint information returned from endpoint discovery
44+
midi2::MidiDeclaredEndpointInfo declaredEndpointInfo{ };
45+
declaredEndpointInfo.Name = endpointSuppliedName;
46+
declaredEndpointInfo.ProductInstanceId = L"PMB_APP2_3263827"; // must be unique
47+
declaredEndpointInfo.SpecificationVersionMajor = 1; // see latest MIDI 2 UMP spec
48+
declaredEndpointInfo.SpecificationVersionMinor = 1; // see latest MIDI 2 UMP spec
49+
declaredEndpointInfo.SupportsMidi10Protocol = true;
50+
declaredEndpointInfo.SupportsMidi20Protocol = true;
51+
declaredEndpointInfo.SupportsReceivingJitterReductionTimestamps = false;
52+
declaredEndpointInfo.SupportsSendingJitterReductionTimestamps = false;
53+
declaredEndpointInfo.HasStaticFunctionBlocks = true;
3354

34-
## Sample Code
55+
midi2::MidiDeclaredDeviceIdentity declaredDeviceIdentity{ };
56+
// todo: set any device identity values if you want. This is optional
57+
58+
midi2::MidiEndpointUserSuppliedInfo userSuppliedInfo{ };
59+
userSuppliedInfo.Name = userSuppliedName; // for names, this will bubble to the top in priority
60+
userSuppliedInfo.Description = userSuppliedDescription;
61+
62+
// create the config type to aggregate all this info
63+
virt::MidiVirtualDeviceCreationConfig config(
64+
transportSuppliedName, // this could be a different "transport-supplied" name value here
65+
transportSuppliedDescription, // transport-supplied description
66+
transportSuppliedManufacturerName, // transport-supplied company name
67+
declaredEndpointInfo, // for endpoint discovery
68+
declaredDeviceIdentity, // for endpoint discovery
69+
userSuppliedInfo
70+
);
71+
```
72+
73+
We're not quite done yet, however. The config type is also where you'll set function blocks. At least one function block is needed.
74+
75+
```cpp
76+
// Function blocks. The MIDI 2 UMP specification covers the meanings of these values
77+
midi2::MidiFunctionBlock block1{ };
78+
block1.Number(0);
79+
block1.Name(L"Pads Output");
80+
block1.IsActive(true);
81+
block1.UIHint(midi2::MidiFunctionBlockUIHint::Sender);
82+
block1.FirstGroupIndex(0);
83+
block1.GroupCount(1);
84+
block1.Direction(midi2::MidiFunctionBlockDirection::Bidirectional);
85+
block1.RepresentsMidi10Connection(midi2::MidiFunctionBlockRepresentsMidi10Connection::Not10);
86+
block1.MaxSystemExclusive8Streams(0);
87+
block1.MidiCIMessageVersionFormat(0);
88+
89+
config.FunctionBlocks().Append(block1);
90+
91+
midi2::MidiFunctionBlock block2{ };
92+
block2.Number(1);
93+
block2.Name(L"A Function Block");
94+
block2.IsActive(true);
95+
block2.UIHint(midi2::MidiFunctionBlockUIHint::Sender);
96+
block2.FirstGroupIndex(1);
97+
block2.GroupCount(2);
98+
block2.Direction(midi2::MidiFunctionBlockDirection::Bidirectional);
99+
block2.RepresentsMidi10Connection(midi2::MidiFunctionBlockRepresentsMidi10Connection::Not10);
100+
block2.MaxSystemExclusive8Streams(0);
101+
block2.MidiCIMessageVersionFormat(0);
102+
103+
config.FunctionBlocks().Append(block2);
104+
```
105+
106+
Now, the virtual device is fully defined. The next step is to open a session and then actually create the device in the service.
35107

36-
* [C# Sample](https://github.com/microsoft/MIDI/tree/main/samples/csharp-net/app-to-app-midi-cs)
108+
```cpp
109+
// create the session. The name here is just convenience.
110+
m_session = midi2::MidiSession::Create(config.Name());
37111

112+
if (m_session == nullptr) return; // return if unable to create session
113+
114+
// create the virtual device, so we can get the endpoint device id to connect to
115+
m_virtualDevice = virt::MidiVirtualDeviceManager::CreateVirtualDevice(config);
116+
117+
if (m_virtualDevice == nullptr) return; // return if unable to create virtual device
118+
119+
// create the endpoint connection to the device-side endpoint
120+
// to prevent confusion, this endpoint is not enumerated to
121+
// apps when using the standard set of enumeration filters
122+
m_connection = m_session.CreateEndpointConnection(
123+
m_virtualDevice.DeviceEndpointDeviceId());
124+
125+
// add the virtual device as a message processing plugin so it receives the messages
126+
m_connection.AddMessageProcessingPlugin(m_virtualDevice);
127+
128+
// wire up the stream configuration request received handler
129+
auto streamEventToken = m_virtualDevice.StreamConfigRequestReceived(
130+
{ this, &MainWindow::OnStreamConfigurationRequestReceived });
131+
132+
// wire up the message received handler on the connection itself
133+
auto messageEventToken = m_connection.MessageReceived(
134+
{ this, &MainWindow::OnMidiMessageReceived });
135+
136+
// the client-side endpoint will become visible to other apps once Open() completes
137+
m_connection.Open();
138+
```
139+
140+
From there, you may send and receive messages just like with any other endpoint.
141+
142+
## Troubleshooting
143+
144+
What can cause a failure in virtual device creation? Assuming the service is installed and working properly, the main thing to check will be to ensure that the unique Id provided is actually unique. The unique Id is used as the differentiator in the SWD Id, without any additional hashing or obfuscation, so it must be unique among all virtual devices currently running. When in doubt, one practice to ensure uniqueness is to use a GUID by formatting as string and removing all non alpha-numeric characters. The unique Id is just large enough to hold that string.
145+
146+
## Sample Code
38147
39-
> Note: Virtual UMP devices are not currently visible to the WinMM MIDI 1.0 API (the API used by most MIDI 1.0 apps on Windows). There are enumeration complexities with that API when devices are added and removed at runtime, which is part of why we needed to create a new API anyway. It's possible these devices will never be visibile to WinMM MIDI 1.0. For full functionality, we recommend using the new Windows MIDI Services SDK.
148+
* [C++ WinUI Sample](https://github.com/microsoft/MIDI/tree/main/samples/cpp-winrt/virtual-device-app-winui)
149+
* [C# WinUI Sample](https://github.com/microsoft/MIDI/tree/main/samples/csharp-net/virtual-device-app-winui)

docs/developer-how-to/how-to-enumerate-endpoints.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ That is the equivalent of passing in a sort order of the name, and a filter of t
2020
```cpp
2121
auto endpointList = MidiEndpointDeviceInformation::FindAll(
2222
MidiEndpointDeviceInformationSortOrder::Name,
23-
MidiEndpointDeviceInformationFilters::AllTypicalEndpoints
23+
MidiEndpointDeviceInformationFilters::AllStandardEndpoints
2424
);
2525
```
2626

@@ -30,7 +30,7 @@ The application may then iterate through the list, reading the properties as nee
3030

3131
Windows MIDI Services has a very rich set of properties available for a UMP Endpoint. This information includes hardware and other transport information, parent device information, user-supplied information, and in the case of a MIDI 2.0 UMP Endpoint, declared information from endpoint discovery and protocol negotiation carried out within the Windows service.
3232

33-
For more details, see the `MidiEndpointDeviceInformation` class documentation. You may also use the MIDI Console application to see all of the properties (including the raw property data if you choose to) for an endpoint.
33+
For more details, see the [`MidiEndpointDeviceInformation`](../sdk-winrt-core/enumeration/MidiEndpointDeviceInformation.md) class documentation. You may also use the [MIDI Console application](../console/midi-console.md) to see all of the properties (including the raw property data if you choose to) for an endpoint.
3434

3535
## Sample Code
3636

docs/developer-how-to/how-to-watch-endpoints.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ In Windows MIDI Services, we've provided a specialized version of the device wat
1515
1616
## Events
1717

18-
To use the `MidiEndpointDeviceWatcher`, first wire up handlers for the `Added`, `Removed`, and `Updated` events. Optionally, you may wire up handlers for the `EnumerationCompleted` event to be notified when initial enumeration has finished, and the `Stopped` event to know when the watcher has been stopped by a call to the `Stop` method.
18+
To use the [`MidiEndpointDeviceWatcher`](../sdk-winrt-core/enumeration/MidiEndpointDeviceWatcher.md), first wire up handlers for the `Added`, `Removed`, and `Updated` events. Optionally, you may wire up handlers for the `EnumerationCompleted` event to be notified when initial enumeration has finished, and the `Stopped` event to know when the watcher has been stopped by a call to the `Stop` method.
1919

2020
Once the event handlers have been wired up, create the watcher using the static `Create` function.
2121

@@ -26,16 +26,16 @@ auto watcher = MidiEndpointDeviceWatcher::Create();
2626
If you wish to use a filter list that differs from the default (the default is appropriate for most applications, as it filters out diagnostics and other endpoints not typically shown to end users) you may use the overloaded Create function. For example, to show only native UMP endpoints, not translated MIDI 1.0 devices, you would do this:
2727

2828
```cpp
29-
auto watcher = MidiEndpointDeviceWatcher::Create(MidiEndpointDeviceInformationFilters::IncludeClientUmpFormatNative);
29+
auto watcher = MidiEndpointDeviceWatcher::Create(MidiEndpointDeviceInformationFilters::StandardNativeUniversalMidiPacketFormat);
3030
```
3131

3232
And, conversely, to show only up-converted MIDI 1.0 byte format endpoints:
3333

3434
```cpp
35-
auto watcher = MidiEndpointDeviceWatcher::Create(MidiEndpointDeviceInformationFilters::IncludeClientByteFormatNative);
35+
auto watcher = MidiEndpointDeviceWatcher::Create(MidiEndpointDeviceInformationFilters::StandardNativeMidi1ByteFormat);
3636
```
3737

38-
The default is to include both, which is also represented by `MidiEndpointDeviceInformationFilters::AllTypicalEndpoints`.
38+
The default is to include both, which is also represented by `MidiEndpointDeviceInformationFilters::AllStandardEndpoints`.
3939

4040
## Accessing the list
4141

0 commit comments

Comments
 (0)