Skip to content

Commit

Permalink
api: use std::variant instead of std::any for API configurations
Browse files Browse the repository at this point in the history
Necessary as std::any just does not work across dylibs on macOS
  • Loading branch information
jcelerier committed Jan 8, 2025
1 parent d818732 commit d131e30
Show file tree
Hide file tree
Showing 17 changed files with 283 additions and 175 deletions.
4 changes: 0 additions & 4 deletions include/libremidi/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include <libremidi/api-c.h>
#include <libremidi/config.hpp>

#include <any>
#include <string_view>
#include <vector>

Expand Down Expand Up @@ -31,9 +30,6 @@ LIBREMIDI_EXPORT std::vector<libremidi::API> available_apis() noexcept;
*/
LIBREMIDI_EXPORT std::vector<libremidi::API> available_ump_apis() noexcept;

LIBREMIDI_EXPORT
libremidi::API midi_api(const std::any& conf);

//! A static function to determine the current version.
LIBREMIDI_EXPORT std::string_view get_version() noexcept;

Expand Down
13 changes: 6 additions & 7 deletions include/libremidi/backends.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once
#include <libremidi/config.hpp>

#include <any>
#include <tuple>

#if !__has_include(<weak_libjack.h>) && !__has_include(<jack/jack.h>)
Expand Down Expand Up @@ -202,10 +201,10 @@ auto for_backend(libremidi::API api, F&& f)
midi2::for_backend(api, f);
}

void for_input_configuration(auto f, std::any& api_conf)
void for_input_configuration(auto f, libremidi::input_api_configuration& api_conf)
{
auto from_api = [&]<typename T>(T& /*backend*/) mutable {
if (auto conf = std::any_cast<typename T::midi_in_configuration>(&api_conf))
if (auto conf = std::get_if<typename T::midi_in_configuration>(&api_conf))
{
f(*conf);
return true;
Expand All @@ -217,10 +216,10 @@ void for_input_configuration(auto f, std::any& api_conf)
std::apply([&](auto&&... b) { return (from_api(b) || ...); }, midi2::available_backends);
}

void for_output_configuration(auto f, std::any& api_conf)
void for_output_configuration(auto f, libremidi::output_api_configuration& api_conf)
{
auto from_api = [&]<typename T>(T& /*backend*/) mutable {
if (auto conf = std::any_cast<typename T::midi_out_configuration>(&api_conf))
if (auto conf = std::get_if<typename T::midi_out_configuration>(&api_conf))
{
f(*conf);
return true;
Expand All @@ -232,10 +231,10 @@ void for_output_configuration(auto f, std::any& api_conf)
std::apply([&](auto&&... b) { return (from_api(b) || ...); }, midi2::available_backends);
}

void for_observer_configuration(auto f, std::any& api_conf)
void for_observer_configuration(auto f, libremidi::observer_api_configuration& api_conf)
{
auto from_api = [&]<typename T>(T& /*backend*/) mutable {
if (auto conf = std::any_cast<typename T::midi_observer_configuration>(&api_conf))
if (auto conf = std::get_if<typename T::midi_observer_configuration>(&api_conf))
{
f(*conf);
return true;
Expand Down
5 changes: 2 additions & 3 deletions include/libremidi/backends/alsa_raw_ump.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ struct backend
using midi_observer = observer_impl;
using midi_in_configuration = alsa_raw_ump::input_configuration;
using midi_out_configuration = alsa_raw_ump::output_configuration;
struct midi_observer_configuration : alsa_raw_observer_configuration
{
};
using midi_observer_configuration = alsa_raw_ump::observer_configuration;

static const constexpr auto API = libremidi::API::ALSA_RAW_UMP;
static const constexpr std::string_view name = "alsa_raw_ump";
static const constexpr std::string_view display_name = "ALSA (raw UMP)";
Expand Down
3 changes: 1 addition & 2 deletions include/libremidi/backends/alsa_raw_ump/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ struct output_configuration
std::optional<chunking_parameters> chunking;
};

struct observer_configuration
struct observer_configuration : public alsa_raw_observer_configuration
{
std::chrono::milliseconds poll_period{100};
};
}
204 changes: 133 additions & 71 deletions include/libremidi/backends/keyboard/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,87 +6,149 @@

namespace libremidi
{
enum kbd_event
{
NOTE_0 = 0x0, // C
VEL_0 = NOTE_0 + 128, // Set velocity to 0
OCT_0 = VEL_0 + 128, // Set octave to 0
OCTAVE_PLUS = OCT_0 + 128,
OCTAVE_MINUS,
VELOCITY_PLUS,
VELOCITY_MINUS,
};

// Default map
//
// ,---,---,---,---,---,---,---,---,---,---,---,---,---,-------,
// | V0| V1| V2| V3| V4| V5| V6| V7| V8| V9|V10|V11|V12| <- |
// |---'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-----|
// | ->| | | C#| D#| | F#| G#| A#| | C#| D#| | F#| |
// |-----',--',--',--',--',--',--',--',--',--',--',--',--'| |
// | Caps | C | D | E | F | G | A | B | C | D | E | F | G | |
// |----,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'---'----|
// | -^ | | O-| O+| V-| V+| | | | | | | ----^ |
// |----'-,-',--'--,'---'---'---'---'---'---'-,-'---',--,------|
// | ctrl | | alt | |altgr | | ctrl |
// '------' '-----'--------------------------'------' '------'

// clang-format off

// actually based on macOS virtual codes as it seems
// impossible to get raw scan codes.
// https://eastmanreference.com/complete-list-of-applescript-key-codes
static inline const std::map<int, int>& scancode_map_macos(){
static const std::map<int, int> ret{
{ 0, NOTE_0}, // C0
{ 13, NOTE_0 + 1},
{ 1, NOTE_0 + 2},
{ 14, NOTE_0 + 3},
{ 2, NOTE_0 + 4},
{ 3, NOTE_0 + 5},
{ 17, NOTE_0 + 6},
{ 5, NOTE_0 + 7},
{ 16, NOTE_0 + 8},
{ 4, NOTE_0 + 9},
{ 32, NOTE_0 + 10},
{ 38, NOTE_0 + 11},
{ 40, NOTE_0 + 12}, // C1
{ 31, NOTE_0 + 13},
{ 37, NOTE_0 + 14},
{ 35, NOTE_0 + 15},
{ 41, NOTE_0 + 16},
{ 39, NOTE_0 + 17},
{ 30, NOTE_0 + 18},
{ 42, NOTE_0 + 19},

{ 50, VEL_0 + int(0)},
{ 18, VEL_0 + int(1 * 127 / 12)},
{ 19, VEL_0 + int(2 * 127 / 12)},
{ 20, VEL_0 + int(3 * 127 / 12)},
{ 21, VEL_0 + int(4 * 127 / 12)},
{ 23, VEL_0 + int(5 * 127 / 12)},
{ 22, VEL_0 + int(6 * 127 / 12)},
{ 26, VEL_0 + int(7 * 127 / 12)},
{ 28, VEL_0 + int(8 * 127 / 12)},
{ 25, VEL_0 + int(9 * 127 / 12)},
{ 29, VEL_0 + int(10 * 127 / 12)},
{ 27, VEL_0 + int(11 * 127 / 12)},
{ 24, VEL_0 + int(127)},

{ 6, OCTAVE_MINUS },
{ 7, OCTAVE_PLUS },
{ 8, VELOCITY_MINUS },
{ 9, VELOCITY_PLUS},
};
return ret;
}

// Use: https://kbdlayout.info/KBDUSX/scancodes
// Also valid for windows
static inline const std::map<int, int>& scancode_map_linux() {
static const std::map<int, int> ret{
{ 0x1E, NOTE_0}, // C0
{ 0x11, NOTE_0 + 1},
{ 0x1F, NOTE_0 + 2},
{ 0x12, NOTE_0 + 3},
{ 0x20, NOTE_0 + 4},
{ 0x21, NOTE_0 + 5},
{ 0x14, NOTE_0 + 6},
{ 0x22, NOTE_0 + 7},
{ 0x15, NOTE_0 + 8},
{ 0x23, NOTE_0 + 9},
{ 0x16, NOTE_0 + 10},
{ 0x24, NOTE_0 + 11},
{ 0x25, NOTE_0 + 12}, // C1
{ 0x18, NOTE_0 + 13},
{ 0x26, NOTE_0 + 14},
{ 0x19, NOTE_0 + 15},
{ 0x27, NOTE_0 + 16},
{ 0x28, NOTE_0 + 17},
{ 0x1B, NOTE_0 + 18},
{ 0x2B, NOTE_0 + 19},

{ 0x29, VEL_0 + int(0)},
{ 0x02, VEL_0 + int(1 * 127 / 12)},
{ 0x03, VEL_0 + int(2 * 127 / 12)},
{ 0x04, VEL_0 + int(3 * 127 / 12)},
{ 0x05, VEL_0 + int(4 * 127 / 12)},
{ 0x06, VEL_0 + int(5 * 127 / 12)},
{ 0x07, VEL_0 + int(6 * 127 / 12)},
{ 0x08, VEL_0 + int(7 * 127 / 12)},
{ 0x09, VEL_0 + int(8 * 127 / 12)},
{ 0x0A, VEL_0 + int(9 * 127 / 12)},
{ 0x0B, VEL_0 + int(10 * 127 / 12)},
{ 0x0C, VEL_0 + int(11 * 127 / 12)},
{ 0x0D, VEL_0 + int(127)},

{ 0x2C, OCTAVE_MINUS },
{ 0x2D, OCTAVE_PLUS },
{ 0x2E, VELOCITY_MINUS },
{ 0x2F, VELOCITY_PLUS},
};
return ret;
}
// clang-format on
/**
* Used to set up keyboard input.
* Your app should pass a function that will give you a callback
* that you should call whenever you are getting a key input (press or release).
*/
struct kbd_input_configuration
{
// Note that on Linux with X11 scancodes should be offset by 8 (an X11 legacy quirk)
// ex.: pass QKeyEvent::nativeScanCode() - 8 to this API
using scancode_callback = std::function<void(int)>;

// First argument is on key press, second on key release
std::function<void(scancode_callback, scancode_callback)> set_input_scancode_callbacks
= [](scancode_callback, scancode_callback) {};

enum kbd_event
{
NOTE_0 = 0x0, // C
VEL_0 = NOTE_0 + 128, // Set velocity to 0
OCT_0 = VEL_0 + 128, // Set octave to 0
OCTAVE_PLUS = OCT_0 + 128,
OCTAVE_MINUS,
VELOCITY_PLUS,
VELOCITY_MINUS,
};
= [](scancode_callback, scancode_callback) { };

// Default map
//
// ,---,---,---,---,---,---,---,---,---,---,---,---,---,-------,
// | V0| V1| V2| V3| V4| V5| V6| V7| V8| V9|V10|V11|V12| <- |
// |---'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-----|
// | ->| | | C#| D#| | F#| G#| A#| | C#| D#| | F#| |
// |-----',--',--',--',--',--',--',--',--',--',--',--',--'| |
// | Caps | C | D | E | F | G | A | B | C | D | E | F | G | |
// |----,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'---'----|
// | -^ | | O-| O+| V-| V+| | | | | | | ----^ |
// |----'-,-',--'--,'---'---'---'---'---'---'-,-'---',--,------|
// | ctrl | | alt | |altgr | | ctrl |
// '------' '-----'--------------------------'------' '------'

// clang-format off
// Use: https://kbdlayout.info/KBDUSX/scancodes
std::map<int, int> scancode_map{
{ 0x1E, NOTE_0}, // C0
{ 0x11, NOTE_0 + 1},
{ 0x1F, NOTE_0 + 2},
{ 0x12, NOTE_0 + 3},
{ 0x20, NOTE_0 + 4},
{ 0x21, NOTE_0 + 5},
{ 0x14, NOTE_0 + 6},
{ 0x22, NOTE_0 + 7},
{ 0x15, NOTE_0 + 8},
{ 0x23, NOTE_0 + 9},
{ 0x16, NOTE_0 + 10},
{ 0x24, NOTE_0 + 11},
{ 0x25, NOTE_0 + 12}, // C1
{ 0x18, NOTE_0 + 13},
{ 0x26, NOTE_0 + 14},
{ 0x19, NOTE_0 + 15},
{ 0x27, NOTE_0 + 16},
{ 0x28, NOTE_0 + 17},
{ 0x1B, NOTE_0 + 18},
{ 0x2B, NOTE_0 + 19},

{ 0x29, VEL_0 + int(0)},
{ 0x02, VEL_0 + int(1 * 127 / 12)},
{ 0x03, VEL_0 + int(2 * 127 / 12)},
{ 0x04, VEL_0 + int(3 * 127 / 12)},
{ 0x05, VEL_0 + int(4 * 127 / 12)},
{ 0x06, VEL_0 + int(5 * 127 / 12)},
{ 0x07, VEL_0 + int(6 * 127 / 12)},
{ 0x08, VEL_0 + int(7 * 127 / 12)},
{ 0x09, VEL_0 + int(8 * 127 / 12)},
{ 0x0A, VEL_0 + int(9 * 127 / 12)},
{ 0x0B, VEL_0 + int(10 * 127 / 12)},
{ 0x0C, VEL_0 + int(11 * 127 / 12)},
{ 0x0D, VEL_0 + int(127)},

{ 0x2C, OCTAVE_MINUS },
{ 0x2D, OCTAVE_PLUS },
{ 0x2E, VELOCITY_MINUS },
{ 0x2F, VELOCITY_PLUS},
};
// clang-format on
std::map<int, int> scancode_map
#if defined(__APPLE__)
= scancode_map_macos()
#else
= scancode_map_linux()
#endif
;
};

}
4 changes: 2 additions & 2 deletions include/libremidi/backends/keyboard/midi_in.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class midi_in_kbd final

void on_keypress(int scancode)
{
using kevent = kbd_input_configuration::kbd_event;
using kevent = libremidi::kbd_event;

auto it = configuration.scancode_map.find(scancode);
if (it == configuration.scancode_map.end())
Expand Down Expand Up @@ -88,7 +88,7 @@ class midi_in_kbd final

void on_keyrelease(int scancode)
{
using kevent = kbd_input_configuration::kbd_event;
using kevent = libremidi::kbd_event;

auto it = configuration.scancode_map.find(scancode);
if (it == configuration.scancode_map.end())
Expand Down
43 changes: 42 additions & 1 deletion include/libremidi/configurations.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <libremidi/api.hpp>
#include <libremidi/backends/alsa_raw/config.hpp>
#include <libremidi/backends/alsa_raw_ump/config.hpp>
#include <libremidi/backends/alsa_seq/config.hpp>
Expand All @@ -14,11 +15,51 @@
#include <libremidi/backends/winmidi/config.hpp>
#include <libremidi/backends/winmm/config.hpp>
#include <libremidi/backends/winuwp/config.hpp>
#include <libremidi/config.hpp>

#include <variant>
namespace libremidi
{

struct unspecified_configuration
{
};
struct dummy_configuration
{
};

using input_api_configuration = std::variant<
unspecified_configuration, dummy_configuration, alsa_raw_input_configuration,
alsa_raw_ump::input_configuration, alsa_seq::input_configuration,
alsa_seq_ump::input_configuration, coremidi_input_configuration,
coremidi_ump::input_configuration, emscripten_input_configuration, jack_input_configuration,
kbd_input_configuration, libremidi::net::dgram_input_configuration,
libremidi::net_ump::dgram_input_configuration, pipewire_input_configuration,
winmidi::input_configuration, winmm_input_configuration, winuwp_input_configuration,
libremidi::API>;

using output_api_configuration = std::variant<
unspecified_configuration, dummy_configuration, alsa_raw_output_configuration,
alsa_raw_ump::output_configuration, alsa_seq::output_configuration,
alsa_seq_ump::output_configuration, coremidi_output_configuration,
coremidi_ump::output_configuration, emscripten_output_configuration, jack_output_configuration,
libremidi::net::dgram_output_configuration, libremidi::net_ump::dgram_output_configuration,
pipewire_output_configuration, winmidi::output_configuration, winmm_output_configuration,
winuwp_output_configuration, libremidi::API>;

using observer_api_configuration = std::variant<
unspecified_configuration, dummy_configuration, alsa_raw_observer_configuration,
alsa_raw_ump::observer_configuration, alsa_seq::observer_configuration,
alsa_seq_ump::observer_configuration, coremidi_observer_configuration,
coremidi_ump::observer_configuration, emscripten_observer_configuration,
jack_observer_configuration, libremidi::net::dgram_observer_configuration,
libremidi::net_ump::dgram_observer_configuration, pipewire_observer_configuration,
winmidi::observer_configuration, winmm_observer_configuration, winuwp_observer_configuration,
libremidi::API>;

LIBREMIDI_EXPORT
libremidi::API midi_api(const input_api_configuration& conf);
LIBREMIDI_EXPORT
libremidi::API midi_api(const output_api_configuration& conf);
LIBREMIDI_EXPORT
libremidi::API midi_api(const observer_api_configuration& conf);
}
Loading

0 comments on commit d131e30

Please sign in to comment.