very, very simple WP with midi CC out

This commit is contained in:
Sebastian
2024-10-09 02:01:23 +02:00
commit 3d6fc151f1
126 changed files with 19539 additions and 0 deletions
@@ -0,0 +1,9 @@
#pragma once
#include <libremidi/backends/coremidi/config.hpp>
namespace libremidi::coremidi_ump
{
using input_configuration = coremidi_input_configuration;
using output_configuration = coremidi_output_configuration;
using observer_configuration = coremidi_observer_configuration;
}
@@ -0,0 +1,2 @@
#pragma once
#include <libremidi/backends/coremidi/helpers.hpp>
@@ -0,0 +1,183 @@
#pragma once
#include <libremidi/backends/coremidi_ump/config.hpp>
#include <libremidi/backends/coremidi_ump/helpers.hpp>
#include <libremidi/detail/midi_in.hpp>
namespace libremidi::coremidi_ump
{
class midi_in_impl final
: public midi2::in_api
, private coremidi_data
, public error_handler
{
public:
struct
: ump_input_configuration
, coremidi_ump::input_configuration
{
} configuration;
midi_in_impl(ump_input_configuration&& conf, coremidi_input_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
if (auto result = init_client(configuration); result != noErr)
{
error<driver_error>(
this->configuration,
"midi_in_core: error creating MIDI client object: " + std::to_string(result));
return;
}
}
~midi_in_impl() override
{
// Close a connection if it exists.
close_port();
if (this->endpoint)
MIDIEndpointDispose(this->endpoint);
close_client();
}
void close_client()
{
if (!configuration.context)
MIDIClientDispose(this->client);
}
void set_client_name(std::string_view) override
{
warning(configuration, "midi_in_core: set_client_name unsupported");
}
void set_port_name(std::string_view) override
{
warning(configuration, "midi_in_core: set_port_name unsupported");
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::COREMIDI_UMP; }
bool open_port(const input_port& info, std::string_view portName) override
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
auto source = locate_object(*this, info, kMIDIObjectType_Source);
if (source == 0)
return false;
// Create our local sink
MIDIPortRef port;
OSStatus result = MIDIInputPortCreateWithProtocol(
this->client, toCFString(portName).get(), kMIDIProtocol_2_0, &port,
^(const MIDIEventList* evtlist, void* __nullable srcConnRefCon) {
this->midiInputCallback(evtlist, srcConnRefCon);
});
if (result != noErr)
{
close_client();
error<driver_error>(
this->configuration, "midi_in_core::open_port: error creating macOS MIDI input port: "
+ std::to_string(result));
return false;
}
// Make the connection.
if (result = MIDIPortConnectSource(port, source, nullptr); result != noErr)
{
MIDIPortDispose(port);
close_client();
error<driver_error>(
this->configuration, "midi_in_core::open_port: error connecting macOS MIDI input port.");
return false;
}
// Save our api-specific port information.
this->port = port;
return true;
}
bool open_virtual_port(std::string_view portName) override
{
// Create a virtual MIDI input destination.
MIDIEndpointRef endpoint;
OSStatus result = MIDIDestinationCreateWithProtocol(
this->client, toCFString(portName).get(), kMIDIProtocol_2_0, &endpoint,
^(const MIDIEventList* evtlist, void* __nullable srcConnRefCon) {
this->midiInputCallback(evtlist, srcConnRefCon);
});
if (result != noErr)
{
error<driver_error>(
this->configuration,
"midi_in_core::open_virtual_port: error creating virtual macOS MIDI "
"destination.");
return false;
}
// Save our api-specific connection information.
this->endpoint = endpoint;
return true;
}
void close_port() override
{
if (this->endpoint)
{
MIDIEndpointDispose(this->endpoint);
this->endpoint = 0;
}
if (this->port)
{
MIDIPortDispose(this->port);
this->port = 0;
}
}
void set_timestamp(
[[maybe_unused]] const MIDIEventPacket& packet,
[[maybe_unused]] libremidi::ump& msg) noexcept
{
}
timestamp absolute_timestamp() const noexcept override
{
return coremidi_data::time_in_nanos(LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME());
}
void midiInputCallback(const MIDIEventList* list, void* /*srcRef*/)
{
unsigned short nBytes{};
const MIDIEventPacket* packet = &list->packet[0];
for (unsigned int i = 0; i < list->numPackets; ++i)
{
nBytes = packet->wordCount;
if (nBytes == 0)
{
packet = MIDIEventPacketNext(packet);
continue;
}
libremidi::ump msg;
coremidi_data::set_timestamp(*this, packet->timeStamp, msg.timestamp);
if (packet->wordCount <= 4)
{
std::copy_n(packet->words, packet->wordCount, msg.data);
configuration.on_message(std::move(msg));
}
last_time = time_in_nanos(packet->timeStamp);
packet = MIDIEventPacketNext(packet);
}
}
unsigned long long last_time{};
};
}
@@ -0,0 +1,185 @@
#pragma once
#include <libremidi/backends/coremidi_ump/config.hpp>
#include <libremidi/backends/coremidi_ump/helpers.hpp>
#include <libremidi/cmidi2.hpp>
#include <libremidi/detail/midi_out.hpp>
#include <libremidi/detail/ump_stream.hpp>
namespace libremidi::coremidi_ump
{
class midi_out_impl final
: public midi2::out_api
, private coremidi_data
, public error_handler
{
public:
struct
: libremidi::output_configuration
, coremidi_ump::output_configuration
{
} configuration;
midi_out_impl(
libremidi::output_configuration&& conf, coremidi_ump::output_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
if (auto result = init_client(configuration); result != noErr)
{
error<driver_error>(
this->configuration,
"midi_out_impl: error creating MIDI client object: " + std::to_string(result));
return;
}
}
~midi_out_impl()
{
midi_out_impl::close_port();
if (this->endpoint)
MIDIEndpointDispose(this->endpoint);
close_client();
}
void close_client()
{
if (!configuration.context)
MIDIClientDispose(this->client);
}
void set_client_name(std::string_view) override
{
warning(configuration, "midi_out_impl: set_client_name unsupported");
}
void set_port_name(std::string_view) override
{
warning(configuration, "midi_out_impl: set_port_name unsupported");
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::COREMIDI_UMP; }
bool open_port(const output_port& info, std::string_view portName) override
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
// Find where we want to send
auto destination = locate_object(*this, info, kMIDIObjectType_Destination);
if (destination == 0)
return false;
// Create our local source
MIDIPortRef port;
OSStatus result = MIDIOutputPortCreate(this->client, toCFString(portName).get(), &port);
if (result != noErr)
{
close_client();
error<driver_error>(
this->configuration, "midi_out_impl::open_port: error creating macOS MIDI output port.");
return false;
}
// Save our api-specific connection information.
this->port = port;
this->destinationId = destination;
return true;
}
bool open_virtual_port(std::string_view portName) override
{
if (this->endpoint)
{
warning(
configuration,
"midi_out_impl::open_virtual_port: a virtual output port already "
"exists!");
return false;
}
// Create a virtual MIDI output source.
OSStatus result = MIDISourceCreateWithProtocol(
this->client, toCFString(portName).get(), kMIDIProtocol_2_0, &this->endpoint);
if (result != noErr)
{
this->endpoint = 0;
error<driver_error>(
this->configuration,
"midi_out_impl::initialize: error creating macOS virtual MIDI source.");
return false;
}
return true;
}
void close_port() override
{
if (this->endpoint)
{
MIDIEndpointDispose(this->endpoint);
this->endpoint = 0;
}
if (this->port)
{
MIDIPortDispose(this->port);
this->port = 0;
}
}
void send_ump(const uint32_t* ump_stream, std::size_t count) override
{
MIDIEventList* eventList = reinterpret_cast<MIDIEventList*>(m_eventListBuffer);
MIDIEventPacket* packet = MIDIEventListInit(eventList, kMIDIProtocol_2_0);
const MIDITimeStamp ts = LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME();
auto write_fun = [ts, &packet, &eventList](const uint32_t* ump, int bytes) {
packet = MIDIEventListAdd(eventList, event_list_max_size, packet, ts, bytes / 4, ump);
if (packet)
return segmentation_error::no_error;
else
return segmentation_error::need_space;
};
auto realloc_fun = [this, &packet, &eventList] {
push_event_list(eventList);
packet = MIDIEventListInit(eventList, kMIDIProtocol_2_0);
};
segment_ump_stream(ump_stream, count, write_fun, realloc_fun);
push_event_list(eventList);
}
void push_event_list(MIDIEventList* eventList)
{
if (this->endpoint)
{
auto result = MIDIReceivedEventList(this->endpoint, eventList);
if (result != noErr)
{
warning(
this->configuration,
"midi_out_core::send_message: error sending MIDI to virtual "
"destinations.");
}
}
if (this->destinationId != 0)
{
auto result = MIDISendEventList(this->port, this->destinationId, eventList);
if (result != noErr)
{
warning(
this->configuration,
"midi_out_core::send_message: error sending MIDI message to port.");
}
}
}
MIDIEndpointRef destinationId{};
static constexpr int event_list_max_size = 65535;
unsigned char m_eventListBuffer[sizeof(MIDIEventList) + event_list_max_size];
};
}
@@ -0,0 +1,13 @@
#pragma once
#include <libremidi/backends/coremidi/observer.hpp>
namespace libremidi::coremidi_ump
{
class observer_impl final : public libremidi::observer_core
{
using observer_core::observer_core;
libremidi::API get_current_api() const noexcept override { return libremidi::API::COREMIDI_UMP; }
};
}