185 lines
4.7 KiB
C++
185 lines
4.7 KiB
C++
|
|
#pragma once
|
||
|
|
#include <libremidi/backends/jack/helpers.hpp>
|
||
|
|
#include <libremidi/backends/jack_ump/config.hpp>
|
||
|
|
#include <libremidi/detail/midi_out.hpp>
|
||
|
|
|
||
|
|
namespace libremidi::jack_ump
|
||
|
|
{
|
||
|
|
class midi_out_jack
|
||
|
|
: public midi2::out_api
|
||
|
|
, public jack_helpers
|
||
|
|
, private jack_midi1
|
||
|
|
, public error_handler
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
using midi_api::client_open_;
|
||
|
|
struct
|
||
|
|
: libremidi::output_configuration
|
||
|
|
, jack_ump::output_configuration
|
||
|
|
{
|
||
|
|
} configuration;
|
||
|
|
|
||
|
|
midi_out_jack(libremidi::output_configuration&& conf, jack_ump::output_configuration&& apiconf)
|
||
|
|
: configuration{std::move(conf), std::move(apiconf)}
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
~midi_out_jack() override { }
|
||
|
|
|
||
|
|
libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_UMP; }
|
||
|
|
|
||
|
|
stdx::error open_port(const output_port& port, std::string_view portName) override
|
||
|
|
{
|
||
|
|
if (auto err
|
||
|
|
= create_local_port(*this, portName, port_type, JackPortFlags(JackPortIsOutput | 0x20));
|
||
|
|
err != stdx::error{})
|
||
|
|
return err;
|
||
|
|
|
||
|
|
// Connecting to the output
|
||
|
|
if (int err = jack_connect(this->client, jack_port_name(this->port), port.port_name.c_str());
|
||
|
|
err != 0 && err != EEXIST)
|
||
|
|
{
|
||
|
|
libremidi_handle_error(configuration, "could not connect to port" + port.port_name);
|
||
|
|
return from_errc(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
return stdx::error{};
|
||
|
|
}
|
||
|
|
|
||
|
|
stdx::error open_virtual_port(std::string_view portName) override
|
||
|
|
{
|
||
|
|
return create_local_port(*this, portName, port_type, JackPortFlags(JackPortIsOutput | 0x20));
|
||
|
|
}
|
||
|
|
|
||
|
|
stdx::error close_port() override { return do_close_port(); }
|
||
|
|
|
||
|
|
stdx::error set_port_name(std::string_view portName) override
|
||
|
|
{
|
||
|
|
int ret = jack_port_rename(this->client, this->port, portName.data());
|
||
|
|
return from_errc(ret);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
class midi_out_jack_queued final : public midi_out_jack
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
midi_out_jack_queued(
|
||
|
|
libremidi::output_configuration&& conf, jack_ump::output_configuration&& apiconf)
|
||
|
|
: midi_out_jack{std::move(conf), std::move(apiconf)}
|
||
|
|
, m_queue{configuration.ringbuffer_size}
|
||
|
|
{
|
||
|
|
auto status = connect(*this);
|
||
|
|
if (!this->client)
|
||
|
|
{
|
||
|
|
libremidi_handle_error(configuration, "Could not create JACK client");
|
||
|
|
client_open_ = from_jack_status(status);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
client_open_ = stdx::error{};
|
||
|
|
}
|
||
|
|
|
||
|
|
~midi_out_jack_queued() override
|
||
|
|
{
|
||
|
|
midi_out_jack::close_port();
|
||
|
|
|
||
|
|
disconnect(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
stdx::error send_ump(const uint32_t* message, std::size_t size) override
|
||
|
|
{
|
||
|
|
return m_queue.write((unsigned char*)message, size * sizeof(uint32_t));
|
||
|
|
}
|
||
|
|
|
||
|
|
int process(jack_nframes_t nframes)
|
||
|
|
{
|
||
|
|
void* buff = jack_port_get_buffer(this->port, nframes);
|
||
|
|
jack_midi_clear_buffer(buff);
|
||
|
|
|
||
|
|
this->m_queue.read(buff);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
libremidi::jack_queue m_queue;
|
||
|
|
};
|
||
|
|
|
||
|
|
class midi_out_jack_direct final : public midi_out_jack
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
midi_out_jack_direct(
|
||
|
|
libremidi::output_configuration&& conf, jack_ump::output_configuration&& apiconf)
|
||
|
|
: midi_out_jack{std::move(conf), std::move(apiconf)}
|
||
|
|
{
|
||
|
|
auto status = connect(*this);
|
||
|
|
if (!this->client)
|
||
|
|
{
|
||
|
|
libremidi_handle_error(configuration, "Could not create JACK client");
|
||
|
|
client_open_ = from_jack_status(status);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
buffer_size = jack_get_buffer_size(this->client);
|
||
|
|
client_open_ = stdx::error{};
|
||
|
|
}
|
||
|
|
|
||
|
|
~midi_out_jack_direct() override
|
||
|
|
{
|
||
|
|
midi_out_jack::close_port();
|
||
|
|
|
||
|
|
disconnect(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
int process(jack_nframes_t nframes)
|
||
|
|
{
|
||
|
|
void* buff = jack_port_get_buffer(this->port, nframes);
|
||
|
|
jack_midi_clear_buffer(buff);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
stdx::error send_ump(const uint32_t* message, std::size_t size) override
|
||
|
|
{
|
||
|
|
void* buff = jack_port_get_buffer(this->port, buffer_size);
|
||
|
|
int ret = jack_midi_event_write(buff, 0, (unsigned char*)message, size * sizeof(uint32_t));
|
||
|
|
return from_errc(ret);
|
||
|
|
}
|
||
|
|
|
||
|
|
int convert_timestamp(int64_t user) const noexcept
|
||
|
|
{
|
||
|
|
switch (configuration.timestamps)
|
||
|
|
{
|
||
|
|
case timestamp_mode::AudioFrame:
|
||
|
|
return static_cast<int>(user);
|
||
|
|
|
||
|
|
default:
|
||
|
|
// TODO
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
stdx::error schedule_ump(int64_t ts, const uint32_t* message, size_t size) override
|
||
|
|
{
|
||
|
|
void* buff = jack_port_get_buffer(this->port, buffer_size);
|
||
|
|
int ret = jack_midi_event_write(
|
||
|
|
buff, convert_timestamp(ts), (unsigned char*)message, size * sizeof(uint32_t));
|
||
|
|
return from_errc(ret);
|
||
|
|
}
|
||
|
|
|
||
|
|
int buffer_size{};
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace libremidi
|
||
|
|
{
|
||
|
|
template <>
|
||
|
|
inline std::unique_ptr<midi_out_api>
|
||
|
|
make<jack_ump::midi_out_jack>(output_configuration&& conf, jack_ump::output_configuration&& api)
|
||
|
|
{
|
||
|
|
if (api.direct)
|
||
|
|
return std::make_unique<jack_ump::midi_out_jack_direct>(std::move(conf), std::move(api));
|
||
|
|
else
|
||
|
|
return std::make_unique<jack_ump::midi_out_jack_queued>(std::move(conf), std::move(api));
|
||
|
|
}
|
||
|
|
}
|