Files
viseq/include/libremidi/backends/coremidi/midi_out.hpp
T

179 lines
5.1 KiB
C++
Raw Normal View History

2024-10-09 02:01:23 +02:00
#pragma once
#include <libremidi/backends/coremidi/config.hpp>
#include <libremidi/backends/coremidi/helpers.hpp>
#include <libremidi/detail/midi_out.hpp>
namespace libremidi
{
class midi_out_core final
: public midi1::out_api
2024-10-28 19:24:18 +01:00
, public coremidi_data
2024-10-09 02:01:23 +02:00
, public error_handler
{
public:
2024-10-28 19:24:18 +01:00
using midi_api::client_open_;
2024-10-09 02:01:23 +02:00
struct
: output_configuration
, coremidi_output_configuration
{
} configuration;
midi_out_core(output_configuration&& conf, coremidi_output_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
if (auto result = init_client(configuration); result != noErr)
{
2024-10-28 19:24:18 +01:00
libremidi_handle_error(
2024-10-09 02:01:23 +02:00
this->configuration,
2024-10-28 19:24:18 +01:00
"error creating MIDI client object: " + std::to_string(result));
client_open_ = from_osstatus(result);
2024-10-09 02:01:23 +02:00
return;
}
2024-10-28 19:24:18 +01:00
client_open_ = stdx::error{};
2024-10-09 02:01:23 +02:00
}
~midi_out_core()
{
midi_out_core::close_port();
if (this->endpoint)
MIDIEndpointDispose(this->endpoint);
2024-10-28 19:24:18 +01:00
close_client(*this);
2024-10-09 02:01:23 +02:00
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::COREMIDI; }
2024-10-28 19:24:18 +01:00
stdx::error open_port(const output_port& info, std::string_view portName) override
2024-10-09 02:01:23 +02:00
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
// Find where we want to send
auto destination = locate_object(*this, info, kMIDIObjectType_Destination);
if (destination == 0)
2024-10-28 19:24:18 +01:00
return std::errc::invalid_argument;
2024-10-09 02:01:23 +02:00
// Create our local source
MIDIPortRef port;
OSStatus result = MIDIOutputPortCreate(this->client, toCFString(portName).get(), &port);
if (result != noErr)
{
2024-10-28 19:24:18 +01:00
close_client(*this);
libremidi_handle_error(
this->configuration, "error creating macOS MIDI output port.");
return from_osstatus(result);
2024-10-09 02:01:23 +02:00
}
// Save our api-specific connection information.
this->port = port;
this->destinationId = destination;
2024-10-28 19:24:18 +01:00
return stdx::error{};
2024-10-09 02:01:23 +02:00
}
2024-10-28 19:24:18 +01:00
stdx::error open_virtual_port(std::string_view portName) override
2024-10-09 02:01:23 +02:00
{
// Create a virtual MIDI output source.
MIDIEndpointRef endpoint;
OSStatus result = MIDISourceCreate(this->client, toCFString(portName).get(), &endpoint);
if (result != noErr)
{
2024-10-28 19:24:18 +01:00
libremidi_handle_error(
2024-10-09 02:01:23 +02:00
this->configuration,
2024-10-28 19:24:18 +01:00
"error creating macOS virtual MIDI source.");
return from_osstatus(result);
2024-10-09 02:01:23 +02:00
}
// Save our api-specific connection information.
this->endpoint = endpoint;
2024-10-28 19:24:18 +01:00
return stdx::error{};
2024-10-09 02:01:23 +02:00
}
2024-10-28 19:24:18 +01:00
stdx::error close_port() override
2024-10-09 02:01:23 +02:00
{
2024-10-28 19:24:18 +01:00
return coremidi_data::close_port();
2024-10-09 02:01:23 +02:00
}
2024-10-28 19:24:18 +01:00
stdx::error send_message(const unsigned char* message, size_t size) override
2024-10-09 02:01:23 +02:00
{
unsigned int nBytes = static_cast<unsigned int>(size);
if (nBytes == 0)
{
2024-10-28 19:24:18 +01:00
libremidi_handle_warning(configuration, "no data in message argument!");
return std::errc::invalid_argument;
2024-10-09 02:01:23 +02:00
}
if (message[0] != 0xF0 && nBytes > 3)
{
2024-10-28 19:24:18 +01:00
libremidi_handle_warning(
2024-10-09 02:01:23 +02:00
configuration,
2024-10-28 19:24:18 +01:00
"message format problem ... not sysex but "
2024-10-09 02:01:23 +02:00
"> 3 bytes?");
2024-10-28 19:24:18 +01:00
return std::errc::bad_message;
2024-10-09 02:01:23 +02:00
}
const MIDITimeStamp timestamp = LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME();
const ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes;
Byte buffer[bufsize + 16]; // pad for other struct members
ByteCount listSize = sizeof(buffer);
MIDIPacketList* packetList = (MIDIPacketList*)buffer;
ByteCount remainingBytes = nBytes;
while (remainingBytes)
{
MIDIPacket* packet = MIDIPacketListInit(packetList);
// A MIDIPacketList can only contain a maximum of 64K of data, so if our message is longer,
// break it up into chunks of 64K or less and send out as a MIDIPacketList with only one
// MIDIPacket. Here, we reuse the memory allocated above on the stack for all.
ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes;
const Byte* dataStartPtr = (const Byte*)&message[nBytes - remainingBytes];
packet = MIDIPacketListAdd(
packetList, listSize, packet, timestamp, bytesForPacket, dataStartPtr);
remainingBytes -= bytesForPacket;
if (!packet)
{
2024-10-28 19:24:18 +01:00
libremidi_handle_error(
this->configuration, "could not allocate packet list");
return std::errc::message_size;
2024-10-09 02:01:23 +02:00
}
// Send to any destinations that may have connected to us.
if (this->endpoint)
{
auto result = MIDIReceived(this->endpoint, packetList);
if (result != noErr)
{
2024-10-28 19:24:18 +01:00
libremidi_handle_warning(
2024-10-09 02:01:23 +02:00
this->configuration,
2024-10-28 19:24:18 +01:00
"error sending MIDI to virtual "
2024-10-09 02:01:23 +02:00
"destinations.");
2024-10-28 19:24:18 +01:00
return std::errc::io_error;
2024-10-09 02:01:23 +02:00
}
}
// And send to an explicit destination port if we're connected.
if (this->destinationId != 0)
{
auto result = MIDISend(this->port, this->destinationId, packetList);
if (result != noErr)
{
2024-10-28 19:24:18 +01:00
libremidi_handle_warning(
2024-10-09 02:01:23 +02:00
this->configuration,
2024-10-28 19:24:18 +01:00
"error sending MIDI message to port.");
return std::errc::io_error;
2024-10-09 02:01:23 +02:00
}
}
}
2024-10-28 19:24:18 +01:00
return stdx::error{};
2024-10-09 02:01:23 +02:00
}
MIDIEndpointRef destinationId{};
};
}