Skip to content

Commit

Permalink
Merge pull request #526 from microsoft/pete-dev
Browse files Browse the repository at this point in the history
Simple WinMM MIDI Input monitor
  • Loading branch information
Psychlist1972 authored Feb 8, 2025
2 parents e6f256d + af56d3e commit afc3ae4
Show file tree
Hide file tree
Showing 9 changed files with 1,565 additions and 2 deletions.
4 changes: 3 additions & 1 deletion src/api/Service/Exe/MidiDeviceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2529,13 +2529,15 @@ CMidiDeviceManager::SyncMidi1Ports(
friendlyName = baseFriendlyName;
}


// if the device is a new MIDI 2 device, we'll add the group number. Otherwise, we'll leave
// off the differentiator. This is in testing with community. If you still see this in
// after the Canary period ends, then this was preferred :)
//
// The next step if this doesn't work is to see if the name is already used, and then
// add the differentiator only in that case.
//
// May also be good to, even if it's a UMP device, not add the differentiator if there's
// only one active group. Example: the Iridium only lists Group 1

if (nativeDataFormat == MidiDataFormats::MidiDataFormats_UMP)
{
Expand Down
21 changes: 20 additions & 1 deletion src/app-sdk/app-sdk-tools-and-tests.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Service.integrationtests",
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Benchmarks", "tests\Benchmarks\Benchmarks.vcxproj", "{4DABE157-7DD5-422A-8C77-B83EAC9987D0}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "midiusbinfo", "midiusbinfo\midiusbinfo.vcxproj", "{0F688DE9-BE99-4A86-BE79-C5BE32BC471D}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "midiksinfo", "midiusbinfo\midiusbinfo.vcxproj", "{0F688DE9-BE99-4A86-BE79-C5BE32BC471D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SdkInitialization.unittests", "tests\SdkInitialization.unittests\SdkInitialization.unittests.vcxproj", "{9803D8E6-5CA5-420C-A02B-5E3327355041}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "midimdnsinfo", "midimdnsinfo\midimdnsinfo.vcxproj", "{C787073C-50F0-5CA0-D53C-12107196F2F0}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "midi1monitor", "midi1monitor\midi1monitor.vcxproj", "{357A35F3-D207-43C5-8F56-B3A374F9EBF9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -145,6 +147,22 @@ Global
{C787073C-50F0-5CA0-D53C-12107196F2F0}.Release|ARM64EC.Build.0 = Release|ARM64
{C787073C-50F0-5CA0-D53C-12107196F2F0}.Release|x64.ActiveCfg = Release|x64
{C787073C-50F0-5CA0-D53C-12107196F2F0}.Release|x64.Build.0 = Release|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|Any CPU.ActiveCfg = Debug|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|Any CPU.Build.0 = Debug|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|ARM64.Build.0 = Debug|ARM64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|ARM64EC.Build.0 = Debug|ARM64EC
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|x64.ActiveCfg = Debug|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Debug|x64.Build.0 = Debug|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|Any CPU.ActiveCfg = Release|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|Any CPU.Build.0 = Release|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|ARM64.ActiveCfg = Release|ARM64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|ARM64.Build.0 = Release|ARM64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|ARM64EC.ActiveCfg = Release|ARM64EC
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|ARM64EC.Build.0 = Release|ARM64EC
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|x64.ActiveCfg = Release|x64
{357A35F3-D207-43C5-8F56-B3A374F9EBF9}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -157,6 +175,7 @@ Global
{0F688DE9-BE99-4A86-BE79-C5BE32BC471D} = {C8B31D71-0226-4D2D-AC78-53D5C84F472F}
{9803D8E6-5CA5-420C-A02B-5E3327355041} = {900D72D2-91F0-4E6C-B694-192FD48393D2}
{C787073C-50F0-5CA0-D53C-12107196F2F0} = {C8B31D71-0226-4D2D-AC78-53D5C84F472F}
{357A35F3-D207-43C5-8F56-B3A374F9EBF9} = {C8B31D71-0226-4D2D-AC78-53D5C84F472F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AE1DF8B9-9FAB-4E20-8AC6-FF316882222F}
Expand Down
881 changes: 881 additions & 0 deletions src/app-sdk/midi1monitor/color.hpp

Large diffs are not rendered by default.

269 changes: 269 additions & 0 deletions src/app-sdk/midi1monitor/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License
// ============================================================================
// This is part of the Windows MIDI Services App SDK and should be used
// in your Windows application via an official binary distribution.
// Further information: https://aka.ms/midi
// ============================================================================


#pragma once

#include "pch.h"

#include "color.hpp"


void WriteInfo(std::string info)
{
std::cout << dye::aqua(info) << std::endl;
}


void WriteError(std::string error)
{
std::cout << dye::light_red(error) << std::endl;
}

void WriteBrightLabel(std::string label)
{
auto fullLabel = label + ":";
std::cout << std::left << std::setw(25) << std::setfill(' ') << dye::white(fullLabel);
}

void WriteLabel(std::string label)
{
auto fullLabel = label + ":";
std::cout << std::left << std::setw(25) << std::setfill(' ') << dye::grey(fullLabel);
}

std::map<uint16_t, MIDIINCAPSW> m_midiInputDevices;

void LoadWinMMDevices()
{
auto inputDeviceCount = midiInGetNumDevs();

for (uint16_t i = 0; i < inputDeviceCount; i++)
{
MIDIINCAPSW inputCaps{};

auto result = midiInGetDevCaps(i, &inputCaps, sizeof(inputCaps));

if (result == MMSYSERR_NOERROR)
{
m_midiInputDevices.insert_or_assign(i, inputCaps);
}
}
}

void DisplayAllWinMMInputs()
{
WriteInfo("Available Input Ports");;

for (auto const& capsEntry : m_midiInputDevices)
{
std::cout
<< std::setw(3) << dye::yellow(capsEntry.first)
<< dye::grey(" : ");

std::wcout
<< capsEntry.second.szPname
<< std::endl;
}
}

#define LINE_LENGTH 79

uint32_t m_countAllBytesReceived{ 0 };
uint32_t m_countStatusBytesReceived{ 0 };

void DisplayStatusByte(byte status)
{
if (status == MIDI_SYSEX)
{
std::cout << std::endl;
std::cout << std::hex << std::setw(2) << dye::yellow((uint16_t)status);
}
else if (status == MIDI_EOX)
{
std::cout << " ";
std::cout << std::hex << std::setw(2) << dye::yellow((uint16_t)status);
}
else
{
std::cout << std::endl;
std::cout << std::hex << std::setw(2) << dye::aqua((uint16_t)status);
}
}

void DisplayDataByte(byte data)
{
std::cout << " ";
std::cout << std::setfill('0') << std::hex << std::setw(2) << dye::grey((uint16_t)data);
}


void DisplayMidiMessage(DWORD dwMidiMessage, DWORD dwTimestamp)
{
// message format 0 | data 2 | data 1 | status

byte status = dwMidiMessage & 0x000000FF;
byte data1 = (dwMidiMessage & 0x0000FF00) >> 8;
byte data2 = (dwMidiMessage & 0x00FF0000) >> 16;

DisplayStatusByte(status);
m_countStatusBytesReceived++;
m_countAllBytesReceived++;

if (MIDI_MESSAGE_IS_TWO_BYTES(status) || MIDI_MESSAGE_IS_THREE_BYTES(status))
{
DisplayDataByte(data1);
m_countAllBytesReceived++;
}

if (MIDI_MESSAGE_IS_THREE_BYTES(status))
{
DisplayDataByte(data2);
m_countAllBytesReceived++;
}
}

void DisplayMidiLongMessage(LPMIDIHDR header, DWORD dwTimestamp)
{
if (header)
{
byte* current = (byte*)(header->lpData);

while (header->dwBytesRecorded-- && current != nullptr)
{
if (MIDI_BYTE_IS_STATUS_BYTE(*current))
{
DisplayStatusByte(*current);
m_countStatusBytesReceived++;
m_countAllBytesReceived++;
}
else
{
DisplayDataByte(*current);
m_countAllBytesReceived++;
}

current++;
m_countAllBytesReceived++;
}


}
else
{
WriteError("Header is null");
}
}

#define MIDI_BUFFER_SIZE 4096

byte m_buffer[MIDI_BUFFER_SIZE]{ 0 };
MIDIHDR m_header{ };
HMIDIIN m_hMidiIn{ };



void CALLBACK OnMidiMessageReceived(
HMIDIIN hMidiIn,
UINT wMsg,
DWORD dwInstance,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2
)
{
switch (wMsg)
{
case MIM_OPEN:
break;
case MIM_CLOSE:
break;
case MIM_DATA:
DisplayMidiMessage(dwParam1, dwParam2);
break;
case MIM_LONGDATA:
DisplayMidiLongMessage((LPMIDIHDR)dwParam1, dwParam2);
midiInAddBuffer(m_hMidiIn, &m_header, sizeof(MIDIHDR));
break;
case MIM_ERROR:
break;
case MIM_LONGERROR:
break;
case MIM_MOREDATA:
break;
default:
break;
}
}

int __cdecl main()
{
std::cout << dye::grey(std::string(LINE_LENGTH, '=')) << std::endl;
std::cout << dye::aqua(" Monitor a MIDI 1.0 port through WinMM/MME") << std::endl;
std::cout << dye::grey(std::string(LINE_LENGTH, '=')) << std::endl;

LoadWinMMDevices();

DisplayAllWinMMInputs();

// enter number to monitor

uint16_t portNumber{ 0 };

std::cout << std::endl;
WriteInfo("Enter port number to monitor:");
std::cin >> portNumber;

if (auto const& port = m_midiInputDevices.find(portNumber); port != m_midiInputDevices.end())
{
std::cout << dye::yellow("Monitoring ");
std::wcout << port->second.szPname;
std::cout << dye::yellow(" for input. Hit escape to cancel.");
std::cout << std::endl << std::endl;

}
else
{
WriteError("Invalid port number.");
}

if (midiInOpen(&m_hMidiIn, portNumber, (DWORD_PTR)&OnMidiMessageReceived, 0, CALLBACK_FUNCTION) == MMSYSERR_NOERROR)
{
m_header.lpData = (LPSTR)(m_buffer);
m_header.dwBufferLength = MIDI_BUFFER_SIZE;
m_header.dwFlags = 0;

midiInPrepareHeader(m_hMidiIn, &m_header, sizeof(MIDIHDR));
midiInAddBuffer(m_hMidiIn, &m_header, sizeof(MIDIHDR));

midiInStart(m_hMidiIn);
}
else
{
WriteError("Unable to open port for input.");
}

while (true)
{
if (GetAsyncKeyState(VK_ESCAPE) & 0x8000)
{
std::cout << std::endl;

WriteInfo("Closing");
break;
}
}

midiInUnprepareHeader(m_hMidiIn, &m_header, sizeof(MIDIHDR));

midiInStop(m_hMidiIn);

midiInClose(m_hMidiIn);

return 0;
}

Loading

0 comments on commit afc3ae4

Please sign in to comment.