working on win32 uwp! and libremidi update
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
// clang-format off
|
||||
#include <libremidi/config.hpp>
|
||||
// clang-format on
|
||||
|
||||
#include <libremidi/cmidi2.hpp>
|
||||
#include <libremidi/error.hpp>
|
||||
#include <libremidi/message.hpp>
|
||||
#include <libremidi/ump.hpp>
|
||||
namespace libremidi
|
||||
{
|
||||
struct midi1_to_midi2
|
||||
{
|
||||
stdx::error
|
||||
convert(const unsigned char* message, std::size_t size, int64_t timestamp, auto on_ump)
|
||||
{
|
||||
context.midi1 = const_cast<unsigned char*>(message);
|
||||
context.midi1_num_bytes = size;
|
||||
context.midi1_proceeded_bytes = 0;
|
||||
context.ump = ump;
|
||||
context.ump_num_bytes = sizeof(ump);
|
||||
context.ump_proceeded_bytes = 0;
|
||||
|
||||
if (auto res = cmidi2_convert_midi1_to_ump(&context); res != CMIDI2_CONVERSION_RESULT_OK)
|
||||
return std::errc::invalid_argument;
|
||||
|
||||
return on_ump(context.ump, context.ump_proceeded_bytes / 4, timestamp);
|
||||
}
|
||||
|
||||
cmidi2_midi_conversion_context context = [] {
|
||||
cmidi2_midi_conversion_context tmp;
|
||||
cmidi2_midi_conversion_context_initialize(&tmp);
|
||||
return tmp;
|
||||
}();
|
||||
uint32_t ump[65536 / 4];
|
||||
};
|
||||
|
||||
struct midi2_to_midi1
|
||||
{
|
||||
stdx::error
|
||||
convert(const uint32_t* message, std::size_t /* size */, int64_t timestamp, auto on_midi)
|
||||
{
|
||||
auto n
|
||||
= cmidi2_convert_single_ump_to_midi1(midi, sizeof(midi), const_cast<uint32_t*>(message));
|
||||
if (n > 0)
|
||||
return on_midi(midi, n, timestamp);
|
||||
else
|
||||
return std::errc::no_buffer_space;
|
||||
}
|
||||
|
||||
cmidi2_midi_conversion_context context = [] {
|
||||
cmidi2_midi_conversion_context tmp;
|
||||
cmidi2_midi_conversion_context_initialize(&tmp);
|
||||
return tmp;
|
||||
}();
|
||||
uint8_t midi[65536];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,61 +2,11 @@
|
||||
#include <libremidi/api.hpp>
|
||||
#include <libremidi/config.hpp>
|
||||
#include <libremidi/error.hpp>
|
||||
#include <libremidi/observer_configuration.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
struct error_handler
|
||||
{
|
||||
//! Error reporting function for libremidi classes. Throws.
|
||||
template <typename Error_T>
|
||||
void error(auto& configuration, std::string_view errorString) const
|
||||
{
|
||||
if (configuration.on_error)
|
||||
{
|
||||
if (first_error)
|
||||
return;
|
||||
|
||||
first_error = true;
|
||||
configuration.on_error(Error_T::code, errorString);
|
||||
first_error = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(__LIBREMIDI_DEBUG__)
|
||||
std::cerr << '\n' << errorString << "\n\n";
|
||||
#endif
|
||||
throw Error_T{errorString.data()};
|
||||
}
|
||||
}
|
||||
|
||||
//! Warning reporting function for libremidi classes.
|
||||
void warning(auto& configuration, std::string_view errorString) const
|
||||
{
|
||||
if (configuration.on_warning)
|
||||
{
|
||||
if (first_warning)
|
||||
return;
|
||||
|
||||
first_warning = true;
|
||||
configuration.on_warning(midi_error::WARNING, errorString);
|
||||
first_warning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__LIBREMIDI_DEBUG__)
|
||||
std::cerr << '\n' << errorString << "\n\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
// To prevent infinite error loops
|
||||
mutable bool first_error{};
|
||||
mutable bool first_warning{};
|
||||
};
|
||||
|
||||
class midi_api
|
||||
{
|
||||
public:
|
||||
@@ -69,18 +19,29 @@ public:
|
||||
|
||||
[[nodiscard]] virtual libremidi::API get_current_api() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual bool open_virtual_port(std::string_view) = 0;
|
||||
[[nodiscard]] virtual stdx::error open_virtual_port(std::string_view)
|
||||
{
|
||||
return std::errc::function_not_supported;
|
||||
}
|
||||
virtual stdx::error set_client_name(std::string_view)
|
||||
{
|
||||
return std::errc::function_not_supported;
|
||||
}
|
||||
virtual stdx::error set_port_name(std::string_view)
|
||||
{
|
||||
return std::errc::function_not_supported;
|
||||
}
|
||||
|
||||
virtual void close_port() = 0;
|
||||
virtual void set_client_name(std::string_view) = 0;
|
||||
virtual void set_port_name(std::string_view) = 0;
|
||||
virtual stdx::error close_port() = 0;
|
||||
|
||||
bool is_port_open() const noexcept { return bool(port_open_); }
|
||||
bool is_port_connected() const noexcept { return bool(connected_); }
|
||||
stdx::error is_client_open() const noexcept { return client_open_; }
|
||||
bool is_port_open() const noexcept { return port_open_; }
|
||||
bool is_port_connected() const noexcept { return connected_; }
|
||||
|
||||
protected:
|
||||
friend class midi_in;
|
||||
friend class midi_out;
|
||||
stdx::error client_open_{std::errc::not_connected};
|
||||
bool port_open_{};
|
||||
bool connected_{};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include <libremidi/detail/midi_api.hpp>
|
||||
#include <libremidi/observer_configuration.hpp>
|
||||
#include <libremidi/input_configuration.hpp>
|
||||
#include <libremidi/error_handler.hpp>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
@@ -14,7 +16,9 @@ public:
|
||||
midi_in_api& operator=(const midi_in_api&) = delete;
|
||||
midi_in_api& operator=(midi_in_api&&) = delete;
|
||||
|
||||
[[nodiscard]] virtual bool open_port(const input_port& pt, std::string_view local_port_name) = 0;
|
||||
[[nodiscard]] virtual stdx::error
|
||||
open_port(const input_port& pt, std::string_view local_port_name)
|
||||
= 0;
|
||||
[[nodiscard]] virtual timestamp absolute_timestamp() const noexcept = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <libremidi/cmidi2.hpp>
|
||||
#include <libremidi/detail/conversion.hpp>
|
||||
#include <libremidi/detail/midi_api.hpp>
|
||||
#include <libremidi/error_handler.hpp>
|
||||
#include <libremidi/output_configuration.hpp>
|
||||
|
||||
#include <string_view>
|
||||
@@ -18,19 +19,21 @@ public:
|
||||
midi_out_api& operator=(const midi_out_api&) = delete;
|
||||
midi_out_api& operator=(midi_out_api&&) = delete;
|
||||
|
||||
[[nodiscard]] virtual bool open_port(const output_port& pt, std::string_view local_port_name)
|
||||
[[nodiscard]] virtual stdx::error
|
||||
open_port(const output_port& pt, std::string_view local_port_name)
|
||||
= 0;
|
||||
|
||||
[[nodiscard]] virtual int64_t current_time() const noexcept { return 0; }
|
||||
|
||||
virtual void send_message(const unsigned char* message, std::size_t size) = 0;
|
||||
virtual void schedule_message(int64_t /*ts*/, const unsigned char* message, std::size_t size)
|
||||
virtual stdx::error send_message(const unsigned char* message, std::size_t size) = 0;
|
||||
virtual stdx::error
|
||||
schedule_message(int64_t /*ts*/, const unsigned char* message, std::size_t size)
|
||||
{
|
||||
return send_message(message, size);
|
||||
}
|
||||
|
||||
virtual void send_ump(const uint32_t* message, std::size_t size) = 0;
|
||||
virtual void schedule_ump(int64_t /*ts*/, const uint32_t* ump, std::size_t size)
|
||||
virtual stdx::error send_ump(const uint32_t* message, std::size_t size) = 0;
|
||||
virtual stdx::error schedule_ump(int64_t /*ts*/, const uint32_t* ump, std::size_t size)
|
||||
{
|
||||
return send_ump(ump, size);
|
||||
}
|
||||
@@ -45,14 +48,15 @@ class out_api : public midi_out_api
|
||||
public:
|
||||
using midi_out_api::midi_out_api;
|
||||
|
||||
void send_ump(const uint32_t* message, std::size_t /*size*/)
|
||||
stdx::error send_ump(const uint32_t* message, std::size_t size)
|
||||
{
|
||||
uint8_t midi[65536];
|
||||
const auto n
|
||||
= cmidi2_convert_single_ump_to_midi1(midi, sizeof(midi), const_cast<uint32_t*>(message));
|
||||
if (n > 0)
|
||||
send_message(midi, n);
|
||||
return converter.convert(
|
||||
message, size, 0, [this](const unsigned char* midi, std::size_t n, int64_t /* ts */) {
|
||||
return send_message(midi, n);
|
||||
});
|
||||
}
|
||||
|
||||
midi2_to_midi1 converter;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,25 +69,15 @@ class out_api : public midi_out_api
|
||||
public:
|
||||
using midi_out_api::midi_out_api;
|
||||
|
||||
void send_message(const unsigned char* message, std::size_t size)
|
||||
stdx::error send_message(const unsigned char* message, std::size_t size)
|
||||
{
|
||||
cmidi2_midi_conversion_context context{};
|
||||
cmidi2_midi_conversion_context_initialize(&context);
|
||||
|
||||
uint32_t ump[65536 / 4];
|
||||
|
||||
context.midi1 = const_cast<unsigned char*>(message);
|
||||
context.midi1_num_bytes = size;
|
||||
context.midi1_proceeded_bytes = 0;
|
||||
context.ump = ump;
|
||||
context.ump_num_bytes = sizeof(ump);
|
||||
context.ump_proceeded_bytes = 0;
|
||||
|
||||
if (auto res = cmidi2_convert_midi1_to_ump(&context); res != CMIDI2_CONVERSION_RESULT_OK)
|
||||
return;
|
||||
|
||||
send_ump(context.ump, context.ump_proceeded_bytes / 4);
|
||||
return converter.convert(
|
||||
message, size, 0, [this](const uint32_t* ump, std::size_t count, int64_t /* ts */) {
|
||||
return send_ump(ump, count);
|
||||
});
|
||||
}
|
||||
|
||||
midi1_to_midi2 converter;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <libremidi/detail/midi_in.hpp>
|
||||
|
||||
#include <libremidi/cmidi2.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cinttypes>
|
||||
@@ -28,16 +29,87 @@ struct timestamp_backend_info
|
||||
bool has_samples{};
|
||||
};
|
||||
|
||||
namespace midi1
|
||||
template<typename Configuration>
|
||||
struct input_state_machine_base
|
||||
{
|
||||
struct input_state_machine
|
||||
{
|
||||
const input_configuration& configuration;
|
||||
explicit input_state_machine(const input_configuration& conf)
|
||||
const Configuration& configuration;
|
||||
|
||||
explicit input_state_machine_base(const Configuration& conf)
|
||||
: configuration{conf}
|
||||
{
|
||||
}
|
||||
|
||||
template <timestamp_backend_info info>
|
||||
int64_t timestamp(auto to_ns, int64_t samples)
|
||||
{
|
||||
switch (configuration.timestamps)
|
||||
{
|
||||
default:
|
||||
case timestamp_mode::NoTimestamp:
|
||||
return 0;
|
||||
|
||||
case timestamp_mode::Relative: {
|
||||
int64_t time_ns;
|
||||
|
||||
if constexpr (info.has_absolute_timestamps)
|
||||
time_ns = to_ns();
|
||||
else
|
||||
time_ns = system_ns();
|
||||
|
||||
int64_t res;
|
||||
if (first_message)
|
||||
{
|
||||
first_message = false;
|
||||
res = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = time_ns - last_time_ns;
|
||||
}
|
||||
|
||||
last_time_ns = time_ns;
|
||||
return res;
|
||||
}
|
||||
|
||||
case timestamp_mode::Absolute:
|
||||
if constexpr (info.has_absolute_timestamps)
|
||||
return to_ns();
|
||||
else
|
||||
return system_ns();
|
||||
|
||||
case timestamp_mode::SystemMonotonic:
|
||||
if constexpr (info.absolute_is_monotonic)
|
||||
return to_ns();
|
||||
else
|
||||
return system_ns();
|
||||
|
||||
case timestamp_mode::AudioFrame:
|
||||
if constexpr (info.has_samples)
|
||||
return samples;
|
||||
else
|
||||
return 0;
|
||||
|
||||
case timestamp_mode::Custom:
|
||||
return configuration.get_timestamp(to_ns());
|
||||
}
|
||||
}
|
||||
int64_t last_time_ns = 0;
|
||||
bool first_message = true;
|
||||
};
|
||||
|
||||
namespace midi1
|
||||
{
|
||||
struct input_state_machine : input_state_machine_base<input_configuration>
|
||||
{
|
||||
using input_state_machine_base::input_state_machine_base;
|
||||
|
||||
void reset()
|
||||
{
|
||||
message.bytes.clear();
|
||||
message.timestamp = {};
|
||||
state = main;
|
||||
}
|
||||
|
||||
bool has_finished_sysex(std::span<const uint8_t> bytes) const noexcept
|
||||
{
|
||||
return (((bytes.front() == 0xF0) || (state == in_sysex)) && (bytes.back() == 0xF7));
|
||||
@@ -46,6 +118,26 @@ struct input_state_machine
|
||||
// Function to process a byte stream which may contain multiple successive
|
||||
// MIDI events (CoreMIDI, ALSA Sequencer can work like this)
|
||||
void on_bytes_multi(std::span<const uint8_t> bytes, int64_t timestamp)
|
||||
{
|
||||
if (this->configuration.on_message)
|
||||
on_bytes_multi_segmented(this->configuration.on_message, bytes, timestamp);
|
||||
if (this->configuration.on_raw_data)
|
||||
this->configuration.on_raw_data(bytes, timestamp);
|
||||
}
|
||||
|
||||
// Function to process bytes corresponding to at most one midi event
|
||||
// e.g. a midi channel event or a single sysex
|
||||
void on_bytes(std::span<const uint8_t> bytes, int64_t timestamp)
|
||||
{
|
||||
if (this->configuration.on_message)
|
||||
on_bytes_segmented(this->configuration.on_message, bytes, timestamp);
|
||||
if (this->configuration.on_raw_data)
|
||||
this->configuration.on_raw_data(bytes, timestamp);
|
||||
}
|
||||
|
||||
private:
|
||||
void on_bytes_multi_segmented(
|
||||
const message_callback& cb, std::span<const uint8_t> bytes, int64_t timestamp)
|
||||
{
|
||||
int64_t nBytes = bytes.size();
|
||||
int64_t iByte = 0;
|
||||
@@ -54,7 +146,7 @@ struct input_state_machine
|
||||
switch (state)
|
||||
{
|
||||
case in_sysex: {
|
||||
return on_continue_sysex(bytes, finished_sysex);
|
||||
return on_continue_sysex(cb, bytes, finished_sysex);
|
||||
}
|
||||
case main: {
|
||||
while (iByte < nBytes)
|
||||
@@ -150,7 +242,7 @@ struct input_state_machine
|
||||
message.assign(begin, begin + size);
|
||||
message.timestamp = timestamp;
|
||||
|
||||
this->configuration.on_message(std::move(message));
|
||||
cb(std::move(message));
|
||||
message.clear();
|
||||
|
||||
iByte += size;
|
||||
@@ -160,7 +252,8 @@ struct input_state_machine
|
||||
}
|
||||
}
|
||||
|
||||
void on_continue_sysex(std::span<const uint8_t> bytes, bool finished_sysex)
|
||||
void on_continue_sysex(
|
||||
const message_callback& cb, std::span<const uint8_t> bytes, bool finished_sysex)
|
||||
{
|
||||
if (finished_sysex)
|
||||
state = main;
|
||||
@@ -174,14 +267,16 @@ struct input_state_machine
|
||||
message.insert(message.end(), bytes.begin(), bytes.end());
|
||||
if (finished_sysex)
|
||||
{
|
||||
this->configuration.on_message(std::move(message));
|
||||
cb(std::move(message));
|
||||
message.clear();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void on_main(std::span<const uint8_t> bytes, int64_t timestamp, bool finished_sysex)
|
||||
void on_main(
|
||||
const message_callback& cb, std::span<const uint8_t> bytes, int64_t timestamp,
|
||||
bool finished_sysex)
|
||||
{
|
||||
switch (bytes[0])
|
||||
{
|
||||
@@ -196,7 +291,7 @@ struct input_state_machine
|
||||
message.timestamp = timestamp;
|
||||
if (finished_sysex)
|
||||
{
|
||||
this->configuration.on_message(std::move(message));
|
||||
cb(std::move(message));
|
||||
message.clear();
|
||||
}
|
||||
}
|
||||
@@ -222,13 +317,12 @@ struct input_state_machine
|
||||
message.assign(bytes.begin(), bytes.end());
|
||||
message.timestamp = timestamp;
|
||||
|
||||
this->configuration.on_message(std::move(message));
|
||||
cb(std::move(message));
|
||||
message.clear();
|
||||
}
|
||||
|
||||
// Function to process bytes corresponding to at most one midi event
|
||||
// e.g. a midi channel event or a single sysex
|
||||
void on_bytes(std::span<const uint8_t> bytes, int64_t timestamp)
|
||||
void
|
||||
on_bytes_segmented(const message_callback& cb, std::span<const uint8_t> bytes, int64_t timestamp)
|
||||
{
|
||||
if (bytes.empty())
|
||||
return;
|
||||
@@ -237,78 +331,135 @@ struct input_state_machine
|
||||
switch (state)
|
||||
{
|
||||
case in_sysex:
|
||||
return on_continue_sysex(bytes, finished_sysex);
|
||||
return on_continue_sysex(cb, bytes, finished_sysex);
|
||||
|
||||
case main:
|
||||
return on_main(bytes, timestamp, finished_sysex);
|
||||
}
|
||||
}
|
||||
|
||||
template <timestamp_backend_info info>
|
||||
int64_t timestamp(auto to_ns, int64_t samples)
|
||||
{
|
||||
switch (configuration.timestamps)
|
||||
{
|
||||
default:
|
||||
case timestamp_mode::NoTimestamp:
|
||||
return 0;
|
||||
|
||||
case timestamp_mode::Relative: {
|
||||
int64_t time_ns;
|
||||
|
||||
if constexpr (info.has_absolute_timestamps)
|
||||
time_ns = to_ns();
|
||||
else
|
||||
time_ns = system_ns();
|
||||
|
||||
int64_t res;
|
||||
if (first_message)
|
||||
{
|
||||
first_message = false;
|
||||
res = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = time_ns - last_time_ns;
|
||||
}
|
||||
|
||||
last_time_ns = time_ns;
|
||||
return res;
|
||||
}
|
||||
|
||||
case timestamp_mode::Absolute:
|
||||
if constexpr (info.has_absolute_timestamps)
|
||||
return to_ns();
|
||||
else
|
||||
return system_ns();
|
||||
|
||||
case timestamp_mode::SystemMonotonic:
|
||||
if constexpr (info.absolute_is_monotonic)
|
||||
return to_ns();
|
||||
else
|
||||
return system_ns();
|
||||
|
||||
case timestamp_mode::AudioFrame:
|
||||
if constexpr (info.has_samples)
|
||||
return samples;
|
||||
else
|
||||
return 0;
|
||||
|
||||
case timestamp_mode::Custom:
|
||||
return configuration.get_timestamp(to_ns());
|
||||
return on_main(cb, bytes, timestamp, finished_sysex);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
libremidi::message message;
|
||||
|
||||
int64_t last_time_ns = 0;
|
||||
private:
|
||||
enum
|
||||
{
|
||||
main,
|
||||
in_sysex
|
||||
} state{main};
|
||||
};
|
||||
}
|
||||
|
||||
bool first_message = true;
|
||||
namespace midi2
|
||||
{
|
||||
struct input_state_machine : input_state_machine_base<ump_input_configuration>
|
||||
{
|
||||
using input_state_machine_base::input_state_machine_base;
|
||||
|
||||
public:
|
||||
void on_bytes_multi(std::span<const unsigned char> bytes, int64_t timestamp)
|
||||
{
|
||||
auto ptr = reinterpret_cast<const uint32_t*>(bytes.data());
|
||||
auto sz = bytes.size() / 4;
|
||||
return on_bytes_multi({ptr, sz}, timestamp);
|
||||
}
|
||||
|
||||
void on_bytes_multi(std::span<const uint32_t> bytes, int64_t timestamp)
|
||||
{
|
||||
if (this->configuration.on_message)
|
||||
on_bytes_multi_segmented(this->configuration.on_message, bytes, timestamp);
|
||||
if (this->configuration.on_raw_data)
|
||||
this->configuration.on_raw_data(bytes, timestamp);
|
||||
}
|
||||
|
||||
void on_bytes(std::span<const uint32_t> bytes, int64_t timestamp)
|
||||
{
|
||||
if (this->configuration.on_message)
|
||||
on_bytes_segmented(this->configuration.on_message, bytes, timestamp);
|
||||
if (this->configuration.on_raw_data)
|
||||
this->configuration.on_raw_data(bytes, timestamp);
|
||||
}
|
||||
|
||||
private:
|
||||
// Function to process a byte stream which may contain multiple successive
|
||||
// MIDI events (CoreMIDI, ALSA Sequencer can work like this)
|
||||
void on_bytes_multi_segmented(
|
||||
const ump_callback& cb, std::span<const uint32_t> bytes, int64_t timestamp)
|
||||
{
|
||||
auto count = bytes.size();
|
||||
auto ump_stream = bytes.data();
|
||||
while (count > 0)
|
||||
{
|
||||
// Handle NOOP (or padding)
|
||||
while (count > 0 && ump_stream[0] == 0)
|
||||
{
|
||||
count--;
|
||||
ump_stream++;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
break;
|
||||
|
||||
const auto ump_uints = cmidi2_ump_get_num_bytes(ump_stream[0]) / 4;
|
||||
on_bytes_segmented(cb, {ump_stream, ump_stream + ump_uints}, timestamp);
|
||||
|
||||
ump_stream += ump_uints;
|
||||
count -= ump_uints;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to process bytes corresponding to at most one midi event
|
||||
void
|
||||
on_bytes_segmented(const ump_callback& cb, std::span<const uint32_t> bytes, int64_t timestamp)
|
||||
{
|
||||
// Filter according to message type
|
||||
switch(cmidi2_ump_get_message_type(bytes.data()))
|
||||
{
|
||||
case CMIDI2_MESSAGE_TYPE_UTILITY:
|
||||
{
|
||||
// All the utility messages are about timing
|
||||
if (this->configuration.ignore_timing)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
case CMIDI2_MESSAGE_TYPE_SYSTEM:
|
||||
{
|
||||
if (this->configuration.ignore_timing)
|
||||
{
|
||||
auto status = cmidi2_ump_get_system_message_byte2(bytes.data());
|
||||
switch(status)
|
||||
{
|
||||
case CMIDI2_SYSTEM_STATUS_MIDI_TIME_CODE:
|
||||
case CMIDI2_SYSTEM_STATUS_SONG_POSITION:
|
||||
case CMIDI2_SYSTEM_STATUS_TIMING_CLOCK:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->configuration.ignore_sensing)
|
||||
{
|
||||
auto status = cmidi2_ump_get_system_message_byte2(bytes.data());
|
||||
if(status == CMIDI2_SYSTEM_STATUS_ACTIVE_SENSING)
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CMIDI2_MESSAGE_TYPE_SYSEX7:
|
||||
case CMIDI2_MESSAGE_TYPE_SYSEX8_MDS:
|
||||
{
|
||||
if (this->configuration.ignore_sysex)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
libremidi::ump msg;
|
||||
std::copy(bytes.begin(), bytes.end(), msg.data);
|
||||
msg.timestamp = timestamp;
|
||||
cb(std::move(msg));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <libremidi/api.hpp>
|
||||
#include <libremidi/config.hpp>
|
||||
#include <libremidi/observer_configuration.hpp>
|
||||
#include <libremidi/error_handler.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <libremidi/cmidi2.hpp>
|
||||
#include <libremidi/error.hpp>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
@@ -17,7 +18,7 @@ enum class segmentation_error
|
||||
* Utility function to segment an ump stream into individual messages.
|
||||
* Used to send a stream to APIs that work message-by-message.
|
||||
*/
|
||||
inline void
|
||||
inline stdx::error
|
||||
segment_ump_stream(const uint32_t* ump_stream, int64_t count, auto write_func, auto realloc_func)
|
||||
{
|
||||
while (count > 0)
|
||||
@@ -35,24 +36,27 @@ segment_ump_stream(const uint32_t* ump_stream, int64_t count, auto write_func, a
|
||||
const auto ump_bytes = cmidi2_ump_get_num_bytes(ump_stream[0]);
|
||||
|
||||
// FIXME std::expected, propagate the error back to caller?
|
||||
switch (write_func(ump_stream, ump_bytes))
|
||||
switch (int err = static_cast<int>(write_func(ump_stream, ump_bytes)))
|
||||
{
|
||||
case segmentation_error::no_error:
|
||||
case 0:
|
||||
break;
|
||||
case segmentation_error::need_space:
|
||||
case -ENOMEM:
|
||||
case ENOMEM:
|
||||
// Try again if we didn't have enough space in the OS queue
|
||||
realloc_func();
|
||||
if (write_func(ump_stream, ump_bytes) != segmentation_error::no_error)
|
||||
return;
|
||||
if (auto err = write_func(ump_stream, ump_bytes); err != std::errc{})
|
||||
return std::make_error_code(err);
|
||||
break;
|
||||
case segmentation_error::other:
|
||||
return;
|
||||
default:
|
||||
return from_errc(err);
|
||||
}
|
||||
|
||||
const auto ump_uints = ump_bytes / 4;
|
||||
ump_stream += ump_uints;
|
||||
count -= ump_uints;
|
||||
}
|
||||
|
||||
return stdx::error{};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user