120 lines
3.4 KiB
C++
120 lines
3.4 KiB
C++
|
|
#pragma once
|
||
|
|
#include <libremidi/backends/jack/helpers.hpp>
|
||
|
|
#include <libremidi/backends/jack_ump/config.hpp>
|
||
|
|
#include <libremidi/detail/midi_in.hpp>
|
||
|
|
#include <libremidi/detail/midi_stream_decoder.hpp>
|
||
|
|
|
||
|
|
namespace libremidi::jack_ump
|
||
|
|
{
|
||
|
|
class midi_in_jack final
|
||
|
|
: public midi2::in_api
|
||
|
|
, public jack_helpers
|
||
|
|
, private jack_midi1
|
||
|
|
, public error_handler
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
using midi_api::client_open_;
|
||
|
|
struct
|
||
|
|
: libremidi::ump_input_configuration
|
||
|
|
, jack_ump::input_configuration
|
||
|
|
{
|
||
|
|
} configuration;
|
||
|
|
|
||
|
|
explicit midi_in_jack(
|
||
|
|
libremidi::ump_input_configuration&& conf, jack_ump::input_configuration&& apiconf)
|
||
|
|
: configuration{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;
|
||
|
|
}
|
||
|
|
|
||
|
|
client_open_ = stdx::error{};
|
||
|
|
}
|
||
|
|
|
||
|
|
~midi_in_jack() override
|
||
|
|
{
|
||
|
|
midi_in_jack::close_port();
|
||
|
|
|
||
|
|
disconnect(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_UMP; }
|
||
|
|
|
||
|
|
stdx::error open_port(const input_port& port, std::string_view portName) override
|
||
|
|
{
|
||
|
|
if (auto err
|
||
|
|
= create_local_port(*this, portName, port_type, JackPortFlags(JackPortIsInput | 0x20));
|
||
|
|
err != stdx::error{})
|
||
|
|
return err;
|
||
|
|
|
||
|
|
if (int err = jack_connect(this->client, port.port_name.c_str(), jack_port_name(this->port));
|
||
|
|
err != 0 && err != EEXIST)
|
||
|
|
{
|
||
|
|
libremidi_handle_error(
|
||
|
|
configuration,
|
||
|
|
"could not connect to port: " + port.port_name + " -> " + jack_port_name(this->port));
|
||
|
|
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(JackPortIsInput | 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);
|
||
|
|
}
|
||
|
|
|
||
|
|
timestamp absolute_timestamp() const noexcept override
|
||
|
|
{
|
||
|
|
return 1000 * jack_frames_to_time(client, jack_frame_time(client));
|
||
|
|
}
|
||
|
|
|
||
|
|
int process(jack_nframes_t nframes)
|
||
|
|
{
|
||
|
|
static constexpr timestamp_backend_info timestamp_info{
|
||
|
|
.has_absolute_timestamps = true,
|
||
|
|
.absolute_is_monotonic = true,
|
||
|
|
.has_samples = true,
|
||
|
|
};
|
||
|
|
void* buff = jack_port_get_buffer(this->port, nframes);
|
||
|
|
|
||
|
|
// Timing
|
||
|
|
jack_nframes_t current_frames{};
|
||
|
|
jack_time_t current_usecs{}; // roughly CLOCK_MONOTONIC
|
||
|
|
jack_time_t next_usecs{};
|
||
|
|
float period_usecs{};
|
||
|
|
jack_get_cycle_times(
|
||
|
|
this->client, ¤t_frames, ¤t_usecs, &next_usecs, &period_usecs);
|
||
|
|
|
||
|
|
// We have midi events in buffer
|
||
|
|
uint32_t ev_count = jack_midi_get_event_count(buff);
|
||
|
|
for (uint32_t j = 0; j < ev_count; j++)
|
||
|
|
{
|
||
|
|
jack_midi_event_t event{};
|
||
|
|
jack_midi_event_get(&event, buff, j);
|
||
|
|
const auto to_ns
|
||
|
|
= [=, this] { return 1000 * jack_frames_to_time(client, current_frames + event.time); };
|
||
|
|
|
||
|
|
m_processing.on_bytes(
|
||
|
|
{(uint32_t*)event.buffer, (uint32_t*)(event.buffer + event.size)},
|
||
|
|
m_processing.timestamp<timestamp_info>(to_ns, event.time));
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
midi2::input_state_machine m_processing{this->configuration};
|
||
|
|
};
|
||
|
|
}
|