Skip to content

Commit

Permalink
[midi] Various fixes and improvement to midi timing in execution
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Feb 3, 2024
1 parent b6edaae commit 784d713
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 49 deletions.
2 changes: 1 addition & 1 deletion 3rdparty/libremidi
6 changes: 3 additions & 3 deletions src/ossia/dataflow/execution/pull_visitors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ struct global_pull_visitor
auto it = state.m_receivedMidi.find(midi);
if(it != state.m_receivedMidi.end())
{
for(const libremidi::message& v : it->second.second)
for(const libremidi::message& v : it->second.messages)
{
val.messages.push_back(v);
}
Expand Down Expand Up @@ -165,14 +165,14 @@ struct global_pull_node_visitor
{
if(channel == -1)
{
for(const libremidi::message& v : it->second.second)
for(const libremidi::message& v : it->second.messages)
{
val.messages.push_back(v);
}
}
else
{
for(const libremidi::message& v : it->second.second)
for(const libremidi::message& v : it->second.messages)
{
if(v.get_channel() == channel)
val.messages.push_back(v);
Expand Down
59 changes: 53 additions & 6 deletions src/ossia/dataflow/execution_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <ossia/dataflow/token_request.hpp>
#include <ossia/network/base/message_queue.hpp>

#include <libremidi/libremidi.hpp>
namespace ossia
{
execution_state::execution_state() {
Expand Down Expand Up @@ -106,11 +107,11 @@ void execution_state::register_midi_parameter(net::midi::midi_protocol& p)
auto it = m_receivedMidi.find(&p);
if(it == m_receivedMidi.end())
{
m_receivedMidi.insert({&p, {0, {}}});
m_receivedMidi.insert({&p, received_midi_state{.messages = {}, .count = 1}});
}
else
{
it->second.first++;
it->second.count++;
}
#endif
}
Expand All @@ -121,8 +122,8 @@ void execution_state::unregister_midi_parameter(net::midi::midi_protocol& p)
auto it = m_receivedMidi.find(&p);
if(it != m_receivedMidi.end())
{
it->second.first--;
if(it->second.first <= 0)
it->second.count--;
if(it->second.count <= 0)
{
m_receivedMidi.erase(it);
// TODO p.disable_registration();
Expand All @@ -145,8 +146,40 @@ void execution_state::get_new_values()

for(auto it = m_receivedMidi.begin(), end = m_receivedMidi.end(); it != end; ++it)
{
it->second.second.clear();
it->first->clone_value(it->second.second);
auto& [proto, state] = *it;

auto midi = proto->midi_in();
auto& input_messages = proto->messages;
auto& port = state.messages;
libremidi::message msg;
port.clear();
port.reserve(input_messages.size_approx());
if(midi->get_current_api() == libremidi::API::JACK_MIDI)
{
// sample-accurate, see MIDIDevice.cpp in score
while(input_messages.try_dequeue(msg))
{
port.push_back(msg);
}
}
else
{
// Adapt the Absolute timestamps
const auto cur_t = state.current_buffer_start;
const auto prev_t = state.last_buffer_start;
const double timestamp_mult = cur_t > prev_t ? 1. / (cur_t - prev_t) : 0.;

while(input_messages.try_dequeue(msg))
{
msg.timestamp = this->bufferSize * ((msg.timestamp - prev_t) * timestamp_mult);
if(msg.timestamp >= bufferSize)
msg.timestamp = this->bufferSize - 1;
if(msg.timestamp < 0)
msg.timestamp = 0;

port.push_back(msg);
}
}
}
}

Expand Down Expand Up @@ -328,8 +361,22 @@ void execution_state::apply_device_changes()
}
}
}

void execution_state::init_midi_timings()
{
for(auto& [proto, state] : m_receivedMidi)
{
if(auto m = proto->midi_in())
{
state.last_buffer_start = state.current_buffer_start;
state.current_buffer_start = m->absolute_timestamp();
}
}
}

void execution_state::begin_tick()
{
init_midi_timings();
m_policy->clear_local_state();
get_new_values();
apply_device_changes();
Expand Down
14 changes: 11 additions & 3 deletions src/ossia/dataflow/execution_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ struct OSSIA_EXPORT execution_state : public Nano::Observer
#if !defined(OSSIA_TESTING)
private:
#endif
void init_midi_timings();
void get_new_values();

void register_parameter(ossia::net::parameter_base& p);
Expand All @@ -131,9 +132,16 @@ struct OSSIA_EXPORT execution_state : public Nano::Observer

ossia::ptr_map<ossia::net::parameter_base*, value_vector<ossia::value>>
m_receivedValues;
ossia::ptr_map<
ossia::net::midi::midi_protocol*, std::pair<int, value_vector<libremidi::message>>>
m_receivedMidi;

struct received_midi_state
{
value_vector<libremidi::message> messages;
int64_t last_buffer_start{};
int64_t current_buffer_start{};
int count{};
};

ossia::ptr_map<ossia::net::midi::midi_protocol*, received_midi_state> m_receivedMidi;

friend struct local_pull_visitor;
friend struct global_pull_visitor;
Expand Down
65 changes: 41 additions & 24 deletions src/ossia/protocols/midi/midi_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,58 @@ static constexpr auto midi_api(libremidi::API api)
return (api == libremidi::API::UNSPECIFIED) ? libremidi::midi1::default_api() : api;
}
midi_protocol::midi_protocol(
ossia::net::network_context_ptr ctx, std::string device_name, libremidi::API api)
ossia::net::network_context_ptr ctx, std::string device_name,
libremidi::input_configuration& conf, std::any api)
: protocol_base{flags{}}
, m_context{ctx}
// , m_input{std::make_unique<libremidi::midi_in>(midi_api(api), device_name)}
// , m_output{std::make_unique<libremidi::midi_out>(midi_api(api), device_name)}
{
api = midi_api(api);
conf.on_message = [this](const libremidi::message& m) { midi_callback(m); };

libremidi::input_configuration input_conf;
input_conf.on_message = [this](const libremidi::message& m) { midi_callback(m); };
m_input = std::make_unique<libremidi::midi_in>(
input_conf, libremidi::midi_in_configuration_for(api));
m_input = std::make_unique<libremidi::midi_in>(conf, api);
}

libremidi::output_configuration output_conf;
m_output = std::make_unique<libremidi::midi_out>(
output_conf, libremidi::midi_out_configuration_for(api));
midi_protocol::midi_protocol(
ossia::net::network_context_ptr ctx, std::string device_name,
libremidi::output_configuration& conf, std::any api)
: protocol_base{flags{}}
, m_context{ctx}
{
m_output = std::make_unique<libremidi::midi_out>(conf, api);
}

/*
midi_protocol::midi_protocol(
ossia::net::network_context_ptr ctx, midi_info m, libremidi::API api)
: midi_protocol{std::move(ctx), m.handle.display_name, api}
{
set_info(m);
}
*/

midi_protocol::~midi_protocol()
{
try
if(m_input)
{
m_input->close_port();
}
catch(...)
{
logger().error("midi_protocol::~midi_protocol() error");
}
try
{
m_output->close_port();
try
{
m_input->close_port();
}
catch(...)
{
logger().error("midi_protocol::~midi_protocol() error");
}
}
catch(...)

if(m_output)
{
logger().error("midi_protocol::~midi_protocol() error");
try
{
m_output->close_port();
}
catch(...)
{
logger().error("midi_protocol::~midi_protocol() error");
}
}
}

Expand Down Expand Up @@ -126,6 +136,13 @@ midi_info midi_protocol::get_info() const
return m_info;
}

int64_t midi_protocol::get_timestamp() const noexcept
{
if(m_info.type != midi_info::Type::Input || !m_input)
return 0;
return m_input->absolute_timestamp();
}

bool midi_protocol::pull(parameter_base& address)
{
midi_parameter& adrs = dynamic_cast<midi_parameter&>(address);
Expand Down Expand Up @@ -304,10 +321,10 @@ bool midi_protocol::push_raw(const full_parameter_data& parameter_base)

bool midi_protocol::observe(parameter_base& address, bool enable)
{
midi_parameter& adrs = dynamic_cast<midi_parameter&>(address);
if(m_info.type != midi_info::Type::Input)
return false;

midi_parameter& adrs = dynamic_cast<midi_parameter&>(address);
auto& adrinfo = adrs.info();
switch(adrinfo.type)
{
Expand Down
25 changes: 13 additions & 12 deletions src/ossia/protocols/midi/midi_protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
#include <libremidi/message.hpp>
#include <libremidi/observer_configuration.hpp>

#include <any>
#include <array>
#include <atomic>
#include <cassert>
namespace libremidi
{
class midi_in;
class midi_out;
struct input_configuration;
struct output_configuration;
struct message;
}
namespace ossia::net::midi
Expand Down Expand Up @@ -66,7 +69,10 @@ class OSSIA_EXPORT midi_protocol final
public:
explicit midi_protocol(
ossia::net::network_context_ptr, std::string device_name,
libremidi::API api = libremidi::API::UNSPECIFIED);
libremidi::input_configuration&, std::any midi_api);
explicit midi_protocol(
ossia::net::network_context_ptr, std::string device_name,
libremidi::output_configuration&, std::any midi_api);
explicit midi_protocol(
ossia::net::network_context_ptr, midi_info,
libremidi::API api = libremidi::API::UNSPECIFIED);
Expand All @@ -75,30 +81,25 @@ class OSSIA_EXPORT midi_protocol final
bool set_info(midi_info);
midi_info get_info() const;

int64_t get_timestamp() const noexcept;

static std::string
get_midi_port_name(ossia::net::device_base* dev, const midi_info& info);

static std::vector<midi_info> scan(libremidi::API = libremidi::API::UNSPECIFIED);

void push_value(const libremidi::message&);

template <typename T>
void clone_value(T& port)
{
typename T::value_type mess;
while(messages.try_dequeue(mess))
{
port.push_back(mess);
}
}

void enable_registration();

bool learning() const;
void set_learning(bool);

private:
libremidi::midi_in* midi_in() const noexcept { return m_input.get(); }

ossia::spsc_queue<libremidi::message> messages;

private:
ossia::net::network_context_ptr m_context;
std::unique_ptr<libremidi::midi_in> m_input;
std::unique_ptr<libremidi::midi_out> m_output;
Expand Down

0 comments on commit 784d713

Please sign in to comment.