very, very simple WP with midi CC out
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
#pragma once
|
||||
#include <libremidi/backends/winmm/config.hpp>
|
||||
#include <libremidi/backends/winmm/helpers.hpp>
|
||||
#include <libremidi/detail/observer.hpp>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <ranges>
|
||||
#include <thread>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
|
||||
class observer_winmm : public observer_api
|
||||
{
|
||||
public:
|
||||
struct
|
||||
: observer_configuration
|
||||
, winmm_observer_configuration
|
||||
{
|
||||
} configuration;
|
||||
|
||||
explicit observer_winmm(observer_configuration&& conf, winmm_observer_configuration&& apiconf)
|
||||
: configuration{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
if (!configuration.has_callbacks())
|
||||
return;
|
||||
|
||||
if (configuration.notify_in_constructor)
|
||||
check_new_ports<true>();
|
||||
else
|
||||
check_new_ports<false>();
|
||||
}
|
||||
|
||||
~observer_winmm() { }
|
||||
|
||||
libremidi::API get_current_api() const noexcept override { return libremidi::API::WINDOWS_MM; }
|
||||
|
||||
std::vector<input_port> get_input_ports() const noexcept override
|
||||
{
|
||||
return get_port_list<true>();
|
||||
}
|
||||
|
||||
std::vector<output_port> get_output_ports() const noexcept override
|
||||
{
|
||||
return get_port_list<false>();
|
||||
}
|
||||
|
||||
protected:
|
||||
template <bool Notify>
|
||||
void check_new_ports()
|
||||
{
|
||||
auto currInputPortList = get_port_list<true>();
|
||||
|
||||
if constexpr (Notify)
|
||||
{
|
||||
compare_port_lists_and_notify_clients(
|
||||
inputPortList, currInputPortList, configuration.input_added,
|
||||
configuration.input_removed);
|
||||
}
|
||||
inputPortList = std::move(currInputPortList);
|
||||
|
||||
auto currOutputPortList = get_port_list<false>();
|
||||
|
||||
if constexpr (Notify)
|
||||
{
|
||||
compare_port_lists_and_notify_clients(
|
||||
outputPortList, currOutputPortList, configuration.output_added,
|
||||
configuration.output_removed);
|
||||
}
|
||||
outputPortList = std::move(currOutputPortList);
|
||||
}
|
||||
|
||||
void compare_port_lists_and_notify_clients(
|
||||
const auto& prevList, const auto& currList, const auto& portAddedFunc,
|
||||
const auto& portRemovedFunc)
|
||||
{
|
||||
if (portAddedFunc)
|
||||
{
|
||||
for (const auto& port : currList)
|
||||
{
|
||||
auto iter
|
||||
= std::ranges::find(prevList, port.display_name, &port_information::display_name);
|
||||
if (iter == prevList.end())
|
||||
portAddedFunc(port);
|
||||
}
|
||||
}
|
||||
|
||||
if (portRemovedFunc)
|
||||
{
|
||||
for (const auto& port : prevList)
|
||||
{
|
||||
auto iter
|
||||
= std::ranges::find(currList, port.display_name, &port_information::display_name);
|
||||
if (iter == currList.end())
|
||||
portRemovedFunc(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input_port to_in_port_info(std::size_t index) const noexcept
|
||||
{
|
||||
MIDIINCAPS deviceCaps;
|
||||
midiInGetDevCaps(index, &deviceCaps, sizeof(MIDIINCAPS));
|
||||
|
||||
auto rawName = ConvertToUTF8(deviceCaps.szPname);
|
||||
auto portName = rawName;
|
||||
MakeUniqueInPortName(portName, index);
|
||||
return {
|
||||
{.client = 0,
|
||||
.port = index,
|
||||
.manufacturer = "",
|
||||
.device_name = "",
|
||||
.port_name = rawName,
|
||||
.display_name = portName}};
|
||||
}
|
||||
|
||||
output_port to_out_port_info(std::size_t index) const noexcept
|
||||
{
|
||||
MIDIOUTCAPS deviceCaps;
|
||||
midiOutGetDevCaps(index, &deviceCaps, sizeof(MIDIOUTCAPS));
|
||||
|
||||
auto rawName = ConvertToUTF8(deviceCaps.szPname);
|
||||
auto portName = rawName;
|
||||
MakeUniqueOutPortName(portName, index);
|
||||
return {
|
||||
{.client = 0,
|
||||
.port = index,
|
||||
.manufacturer = "",
|
||||
.device_name = "",
|
||||
.port_name = rawName,
|
||||
.display_name = portName}};
|
||||
}
|
||||
|
||||
template <bool Input>
|
||||
auto get_port_list() const noexcept
|
||||
-> std::vector<std::conditional_t<Input, input_port, output_port>>
|
||||
{
|
||||
std::vector<std::conditional_t<Input, input_port, output_port>> portList;
|
||||
|
||||
if constexpr (Input)
|
||||
{
|
||||
std::size_t nDevices = midiInGetNumDevs();
|
||||
for (std::size_t i = 0; i < nDevices; ++i)
|
||||
{
|
||||
portList.push_back(to_in_port_info(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::size_t nDevices = midiOutGetNumDevs();
|
||||
for (std::size_t i = 0; i < nDevices; ++i)
|
||||
{
|
||||
portList.push_back(to_out_port_info(i));
|
||||
}
|
||||
}
|
||||
return portList;
|
||||
}
|
||||
|
||||
static constexpr bool INPUT = true;
|
||||
static constexpr bool OUTPUT = false;
|
||||
|
||||
std::vector<input_port> inputPortList;
|
||||
std::vector<output_port> outputPortList;
|
||||
};
|
||||
}
|
||||
|
||||
#if __has_include(<stop_token>) && __cpp_lib_jthread >= 201911L
|
||||
#include <stop_token>
|
||||
namespace libremidi::winmm
|
||||
{
|
||||
class observer_threaded final : public observer_winmm
|
||||
{
|
||||
public:
|
||||
struct
|
||||
: observer_configuration
|
||||
, winmm_observer_configuration
|
||||
{
|
||||
} configuration;
|
||||
|
||||
explicit observer_threaded(observer_configuration&& conf, winmm_observer_configuration&& apiconf)
|
||||
: observer_winmm{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
thread = std::jthread([this](std::stop_token tk) {
|
||||
while (!tk.stop_requested())
|
||||
{
|
||||
check_new_ports<true>();
|
||||
std::this_thread::sleep_for(this->configuration.poll_period);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::jthread thread;
|
||||
};
|
||||
}
|
||||
#else
|
||||
#include <atomic>
|
||||
#include <semaphore>
|
||||
namespace libremidi::winmm
|
||||
{
|
||||
class observer_threaded final : public observer_winmm
|
||||
{
|
||||
public:
|
||||
struct
|
||||
: observer_configuration
|
||||
, winmm_observer_configuration
|
||||
{
|
||||
} configuration;
|
||||
|
||||
explicit observer_threaded(observer_configuration&& conf, winmm_observer_configuration&& apiconf)
|
||||
: observer_winmm{std::move(conf), std::move(apiconf)}
|
||||
, sema{0}
|
||||
{
|
||||
thread = std::thread([this] {
|
||||
while (!stop_flag.test(std::memory_order_acquire))
|
||||
{
|
||||
check_new_ports<true>();
|
||||
std::this_thread::sleep_for(this->configuration.poll_period);
|
||||
}
|
||||
sema.release();
|
||||
});
|
||||
}
|
||||
|
||||
~observer_threaded()
|
||||
{
|
||||
stop_flag.test_and_set();
|
||||
sema.acquire();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
private:
|
||||
std::thread thread;
|
||||
std::atomic_flag stop_flag = ATOMIC_FLAG_INIT;
|
||||
std::binary_semaphore sema;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace libremidi::winmm
|
||||
{
|
||||
class observer_manual final : public observer_winmm
|
||||
{
|
||||
public:
|
||||
struct
|
||||
: observer_configuration
|
||||
, winmm_observer_configuration
|
||||
{
|
||||
} configuration;
|
||||
|
||||
explicit observer_manual(observer_configuration&& conf, winmm_observer_configuration&& apiconf)
|
||||
: observer_winmm{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
this->configuration.manual_poll({.callback = [this] { this->check_new_ports<true>(); }});
|
||||
}
|
||||
|
||||
~observer_manual() { }
|
||||
};
|
||||
}
|
||||
namespace libremidi
|
||||
{
|
||||
template <>
|
||||
inline std::unique_ptr<observer_api> make<observer_winmm>(
|
||||
libremidi::observer_configuration&& conf, libremidi::winmm_observer_configuration&& api)
|
||||
{
|
||||
if (api.manual_poll)
|
||||
return std::make_unique<winmm::observer_manual>(std::move(conf), std::move(api));
|
||||
else
|
||||
return std::make_unique<winmm::observer_threaded>(std::move(conf), std::move(api));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user