Skip to content

Commit

Permalink
Merge pull request #279 from microsoft/pete-dev
Browse files Browse the repository at this point in the history
Sync up to main
  • Loading branch information
Psychlist1972 authored Feb 19, 2024
2 parents acfa43b + d64a161 commit d4725af
Show file tree
Hide file tree
Showing 77 changed files with 4,082 additions and 1,448 deletions.
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.24045.0225" ?>
<?define SetupVersionNumber="1.0.24049.1933" ?>
</Include>
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,43 @@ Currently, in the implementation behind the scenes, the service receives each ti
| `SendMessageSucceeded(sendResult)` | Helper function to decipher the return result of a message sending function to tell if it succeeded. |
| `SendMessageFailed(sendResult)` | Helper function to decipher the return result of a message sending function to tell if it failed. |

## Functions
## Single-Message Sender Functions

| Function | Description |
| -------- | ----------- |
| `Open()` | Open the connection and start receiving messages. Wire up the message event handler before calling this method. |
| `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, byteLength)` | 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. |
| `SendMessagesWordList(timestamp,words)` | 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. |
| `SendMessagesWordArray(timestamp,words)` | 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.|
| `AddEndpointProcessingPlugin(plugin)` | Add an endpoint processing plugin to this connection |
| `RemoveEndpointProcessingPlugin(id)` | Remove an endpoint processing plugin from this connection |
| `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. |

> 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.
## Multiple-Message Sender Functions

Currently, the service accepts a single message at a time, so these iterate over the messages internally, using the UMP type in the case of words, or just the packets themselves in the case of packets. However, these are present to enable us to optimize sending multiple messages to the service in the future, without you needing to change any of your code.

In methods where there's a single timestamp, messages are sent as quickly as possible on that timestamp. If you send many messages, some other MIDI 1.0 devices may have buffer overflows on the devices themselves. This is not a condition we can detect. In those cases, we recommend using the single message sending functions with a delay between each message.

Finally, there's a practical limit to how many messages can be scheduled ahead of time. Performance degrades as the priority queue size increases, so do not send thousands of messages scheduled at a future time. Sending thousands of messages to be sent immediately is perfectly fine.

| 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. |
| `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. |

## Other Functions

| Function | Description |
| -------- | ----------- |
| `Open()` | Open the connection and start receiving messages. Wire up the message event handler before calling this method. |
| `AddEndpointProcessingPlugin(plugin)` | Add an endpoint processing plugin to this connection |
| `RemoveEndpointProcessingPlugin(id)` | Remove an endpoint processing plugin from this connection |

## Events

| Event | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,41 @@ has_children: false

# MidiMessageConverter

(In progress)
This class provides support for representing MIDI 1.0 messages in the Universal MIDI Packet format.

## Functions
## Generic MIDI 1.0 Conversion Functions

| Function | Description |
| --------------- | ----------- |
| | |
| | |
| | |
| | |
| `ConvertMidi1Message(timestamp, group, statusByte)` | Convert MIDI 1.0 raw data into a `MidiMessage32` message |
| `ConvertMidi1Message(timestamp, group, statusByte, dataByte1)` | Convert MIDI 1.0 raw data into a `MidiMessage32` message |
| `ConvertMidi1Message(timestamp, group, statusByte, dataByte1, dataByte2)` | Convert MIDI 1.0 raw data into a `MidiMessage32` message |

## WinRT MIDI 1.0 System Message Conversion Functions

| Function | Description |
| --------------- | ----------- |
| `ConvertMidi1TimeCodeMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiTimeCodeMessage` message to `MidiMessage32`|
| `ConvertMidi1SongPositionPointerMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiSongPositionPointerMessage` message to `MidiMessage32`|
| `ConvertMidi1SongSelectMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiSongSelectMessage` message to `MidiMessage32`|
| `ConvertMidi1TuneRequestMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiTuneRequestMessage` message to `MidiMessage32`|
| `ConvertMidi1TimingClockMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiTimingClockMessage` message to `MidiMessage32`|
| `ConvertMidi1StartMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiStartMessage` message to `MidiMessage32`|
| `ConvertMidi1ContinueMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiContinueMessage` message to `MidiMessage32`|
| `ConvertMidi1StopMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiStopMessage` message to `MidiMessage32`|
| `ConvertMidi1ActiveSensingMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiActiveSensingMessage` message to `MidiMessage32`|
| `ConvertMidi1SystemResetMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiSystemResetMessage` message to `MidiMessage32`|

## WinRT MIDI 1.0 Channel Voice Message Conversion Functions

| Function | Description |
| --------------- | ----------- |
| `ConvertMidi1ChannelPressureMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiChannelPressureMessage` message to `MidiMessage32`|
| `ConvertMidi1NoteOffMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiNoteOffMessage` message to `MidiMessage32`|
| `ConvertMidi1NoteOnMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiNoteOnMessage` message to `MidiMessage32`|
| `ConvertMidi1PitchBendChangeMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiPitchBendChangeMessage` message to `MidiMessage32`|
| `ConvertMidi1PolyphonicKeyPressureMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiPolyphonicKeyPressureMessage` message to `MidiMessage32`|
| `ConvertMidi1ProgramChangeMessage(timestamp, group, originalMessage)` | Converts a WinRT MIDI 1.0 `MidiProgramChangeMessage` message to `MidiMessage32`|

## IDL

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,59 @@ The `MidiVirtualEndpointDeviceDefinition` class specifies, in an easy to use for
| `MidiVirtualEndpointDeviceDefinition()` | Construct a new device definition |


## Example

```cs
var deviceDefinition = new Windows.Devices.Midi2.MidiVirtualEndpointDeviceDefinition();

// Define the first function block
deviceDefinition.FunctionBlocks.Add(new Windows.Devices.Midi2.MidiFunctionBlock()
{
Number = 0,
IsActive = true,
Name = "Pads Output",
UIHint = midi2.MidiFunctionBlockUIHint.Sender,
FirstGroupIndex = 0,
GroupCount = 1,
Direction = midi2.MidiFunctionBlockDirection.Bidirectional,
Midi10Connection = midi2.MidiFunctionBlockMidi10.Not10,
MaxSystemExclusive8Streams = 0,
MidiCIMessageVersionFormat = 0
});

// Define the section function block
deviceDefinition.FunctionBlocks.Add(new Windows.Devices.Midi2.MidiFunctionBlock()
{
Number = 1,
IsActive = true,
Name = "Controller",
UIHint = midi2.MidiFunctionBlockUIHint.Sender,
FirstGroupIndex = 1,
GroupCount = 1,
Direction = midi2.MidiFunctionBlockDirection.Bidirectional,
Midi10Connection = midi2.MidiFunctionBlockMidi10.Not10,
MaxSystemExclusive8Streams = 0,
MidiCIMessageVersionFormat = 0
});

deviceDefinition.AreFunctionBlocksStatic = true;
deviceDefinition.EndpointName = "Pad Controller App";
deviceDefinition.EndpointProductInstanceId = "PMB_APP2_3263827";
deviceDefinition.SupportsMidi2ProtocolMessages = true;
deviceDefinition.SupportsMidi1ProtocolMessages = true;
deviceDefinition.SupportsReceivingJRTimestamps = false;
deviceDefinition.SupportsSendingJRTimestamps = false;

// ...
// Create the device and return the connection to that device. Once you
// call Open() on the connection, the client-visible endpoint will be
// enumerated and available for use.
_connection = _session.CreateVirtualDeviceAndConnection(deviceDefinition);
```

[Full Example](https://github.com/microsoft/MIDI/tree/main/samples/csharp-net/app-to-app-midi-cs)

## IDL

[MidiVirtualEndpointDeviceDefinition IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiVirtualEndpointDeviceDefinition.idl)
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ Fully-featured app-to-app MIDI in a MIDI 2.0 world involves connections to a vir

During that conversation, the service will also handle discovery and protocol negotiation with the virtual device just like it would any physical device.

In addition to the service component, it is implemented in the client API as a type of Client-SIde Processing Plugin
In addition to the service component, it is implemented in the client API as a type of Client-Side Processing Plugin

## Sample

[Full C# Sample](https://github.com/microsoft/MIDI/tree/main/samples/csharp-net/app-to-app-midi-cs)

3 changes: 1 addition & 2 deletions docs/endpoints/endpoint-ids.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ Example for one of the built-in loopback endpoints:
| ----- | -- |
| `SWD` | Software device. This is any device that is not a physical device connected to the PC, and which is created using the Software Device APIs. All MIDI endpoints are software devices and may or may not have a physical connected device as a parent. |
| `MIDISRV` | The name of the enumerator. For Windows MIDI Services, this is the MidiSrv Windows Service |
| `MIDIU` | Indicates a MIDI UMP interface. |
| `MIDI` or `MIDIU` | Indicates a MIDI interface. |
| `DIAG` | Mnemonic for the transport which created this device interface. |
| `LOOPBACK_A` | Arbitrary unique identification string provided by the transport. Typically includes a unique identifier like a serial number. It may also contain other information like the pin pairs used to provide the bidirectional communication. |
| `MIDIU_DIAG_LOOPBACK_A` | The entire string here is controlled by the transport. By convention it breaks down into the fields mentioned above, but that is not something you should count on. In general, parsing these strings is not recommended. |
| `GUID` | The interface Id. For Windows MIDI Services, every interface is a bidirectional interface, even if the connected device is MIDI 1.0 with a single unidirectional interface. For MIDI 1.0 devices, you can look at the group terminal blocks to identify active groups/directions. For MIDI 2.0 devices, you can look at the function blocks for the same information and more.|


If you look at the device in Device Manager, and look at Details/Device Instance Path, you'll see all of the information here except for the interface Id. When you enumerate devices through `Windows::Devices::Enumeration` or through Windows MIDI Services, the interface Id is included and required.

> Tip: Although it was required in the past, we don't recommend parsing these strings. If there's information you need about the device which is not contained in the enumerated properties, please let us know and we'll look into whether or not we can create a custom property to hold that.
16 changes: 16 additions & 0 deletions samples/cpp-winrt/api-client-send-speed/PropertySheet.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.33711.374
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-client-send-speed-cpp", "api-client-send-speed.vcxproj", "{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Debug|x64.ActiveCfg = Debug|x64
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Debug|x64.Build.0 = Debug|x64
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Debug|x86.ActiveCfg = Debug|x64
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Debug|x86.Build.0 = Debug|x64
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Release|x64.ActiveCfg = Release|x64
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Release|x64.Build.0 = Release|x64
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Release|x86.ActiveCfg = Release|x64
{FB9D8E00-E6E5-42FA-93B5-EFE40DC66C04}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D03217C3-EF96-4030-B340-09EFBF5961D4}
EndGlobalSection
EndGlobal
Loading

0 comments on commit d4725af

Please sign in to comment.