Skip to content

Instantly share code, notes, and snippets.

@trueroad
Last active August 27, 2022 10:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trueroad/2ffa736d0b206973a80c99e177273795 to your computer and use it in GitHub Desktop.
Save trueroad/2ffa736d0b206973a80c99e177273795 to your computer and use it in GitHub Desktop.
WinRT MIDI IN Timing with C++/WinRT
//
// WinRT MIDI IN Timing with C++/WinRT
// https://gist.github.com/trueroad/2ffa736d0b206973a80c99e177273795
//
// Copyright (C) 2022 Masamichi Hosoda.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// See
// https://gist.github.com/trueroad/9c5317af5f212b2de7c7012e76b9e66b
#include <chrono>
#include <future>
#include <mutex>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <windows.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Devices.Midi.h>
#include <winrt/Windows.Storage.Streams.h>
using namespace std::chrono_literals;
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Devices::Midi;
using namespace Windows::Storage::Streams;
class winrt_midi_in_timing
{
public:
struct port
{
std::wstring name;
std::wstring id;
std::wstring hex_id;
std::wstring display_name;
};
winrt_midi_in_timing()
{
std::lock_guard<std::mutex> lock(mtx_ctor_);
if (!event_)
{
event_.attach(::CreateEvent(nullptr, true, false, nullptr));
winrt::check_bool(static_cast<bool>(event_));
winrt::check_bool(::SetConsoleCtrlHandler(HandlerRoutine, true));
}
}
std::vector<port> list_ports(winrt::hstring device_selector)
{
const auto devs =
DeviceInformation::FindAllAsync(device_selector).get();
std::vector<port> retval;
for (const auto& d : devs)
{
port p;
p.name = d.Name();
p.id = d.Id();
p.display_name = d.Name();
std::wsmatch m;
if (std::regex_search(p.id, m, hex_id_pattern_))
{
p.hex_id = m[1];
std::wostringstream ss;
ss << p.name
<< L" [ "
<< p.hex_id
<< L" ]";
p.display_name = ss.str();
}
retval.push_back(p);
}
return retval;
}
IAsyncOperation<MidiInPort> select_midi_in_port()
{
in_ports_ = list_ports(MidiInPort::GetDeviceSelector());
std::wcerr
<< std::endl
<< L"MIDI IN ports"
<< std::endl << std::endl;
for (unsigned int i = 0; i < in_ports_.size(); i++)
{
std::wcerr
<< i
<< L": "
<< in_ports_[i].display_name
<< std::endl;
}
std::wcerr
<< std::endl
<< L"Select number > "
<< std::flush;
unsigned int choice;
std::wcin >> choice;
if (choice < in_ports_.size())
{
std::wcerr
<< std::endl
<< L"Selected: "
<< choice
<< L", "
<< in_ports_[choice].name
<< L", "
<< in_ports_[choice].id
<< L", "
<< in_ports_[choice].hex_id
<< L", "
<< in_ports_[choice].display_name
<< std::endl;
auto async = MidiInPort::FromIdAsync(in_ports_[choice].id);
if (async.wait_for(5s) == AsyncStatus::Completed)
co_return async.GetResults();
std::wcerr
<< L"Timeout"
<< std::endl;
co_return nullptr;
}
std::wcerr
<< L"Error: "
<< choice
<< std::endl;
co_return nullptr;
}
IAsyncAction receiver()
{
in_port_ = co_await select_midi_in_port();
if (!in_port_)
{
std::wcerr
<< L"Error: MIDI IN is not opened"
<< std::endl;
co_return;
}
std::wcerr
<< L"Callback starting..."
<< std::endl;
auto before_token = in_port_.MessageReceived({ this,
&winrt_midi_in_timing::midi_in_callback });
std::wcerr
<< L"Started"
<< std::endl;
co_await resume_on_signal(event_.get());
in_port_.MessageReceived(before_token);
}
private:
std::wstring message_to_str(const IMidiMessage& message)
{
std::wostringstream buff;
const auto raw_data = message.RawData();
const std::basic_string_view<uint8_t> raw_data_sv
{ raw_data.data(), raw_data.Length() };
buff << std::hex;
for (auto u : raw_data_sv)
{
buff << L" "
<< std::setw(2) << std::setfill(L'0')
<< u;
}
return buff.str();
}
void midi_in_callback(const MidiInPort& sender,
const MidiMessageReceivedEventArgs& e)
{
LARGE_INTEGER counter;
::QueryPerformanceCounter(&counter);
std::wcout
<< counter.QuadPart
<< L"\t"
<< e.Message().Timestamp().count()
<< L"\t"
<< message_to_str(e.Message())
<< std::endl;
}
static BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
{
if (dwCtrlType == CTRL_C_EVENT)
{
winrt::check_bool(::SetEvent(event_.get()));
std::wcerr
<< L"Interrupted"
<< std::endl;
return true;
}
return false;
}
MidiInPort in_port_ = nullptr;
std::vector<port> in_ports_;
static const std::wregex hex_id_pattern_;
static std::mutex mtx_ctor_;
static std::mutex mtx_show_message_;
static winrt::handle event_;
};
const std::wregex winrt_midi_in_timing::hex_id_pattern_
{ std::wregex(L"#MIDII_([0-9A-F]{8})\\..+#") };
std::mutex winrt_midi_in_timing::mtx_ctor_;
winrt::handle winrt_midi_in_timing::event_;
int main()
{
init_apartment();
std::wcerr
<< L"WinRT MIDI IN Timing with C++/WinRT"
<< std::endl << std::endl
<< L"https://gist.github.com/trueroad/"
L"2ffa736d0b206973a80c99e177273795"
<< std::endl << std::endl
<< L"Copyright (C) 2022 Masamichi Hosoda."
<< std::endl
<< L"All rights reserved."
<< std::endl << std::endl;
LARGE_INTEGER freq;
::QueryPerformanceFrequency(&freq);
std::wcout
<< freq.QuadPart
<< std::endl;
winrt_midi_in_timing wmt;
wmt.receiver().get();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment