2 Commits

Author SHA1 Message Date
Sebastian @ HfG 96bd5f30e6 upgrade to libremidi 5.1, raylib and raygui latest 2025-06-13 19:06:09 +02:00
sebastian bd642576bb Minimal guide added 2024-11-24 19:54:38 +01:00
215 changed files with 3093 additions and 8528 deletions
-3
View File
@@ -1,3 +0,0 @@
CompileFlags:
Add: [-I/home/sebastian/code/viseq/include, -std=c++23]
+24 -13
View File
@@ -1,21 +1,32 @@
.PHONY: clean
CXX = g++
CXXFLAGS = -std=c++23 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -Wnarrowing -DLIBREMIDI_ALSA=1
CXXINC = -Iinclude
CXX := g++
CXXFLAGS := -std=c++23 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -Wnarrowing -DLIBREMIDI_ALSA=1 -DLIBREMIDI_HEADER_ONLY=1
CXXINC := -Iinclude -isysteminclude
LDLIBS = -lraylib -lasound -pthread
LDFLAGS = -Llib -L/usr/lib -L/usr/local/lib
LDLIBS := -lraylib -lasound -llibremidi -pthread
LDFLAGS := -Llib -L/usr/lib -L/usr/local/lib
hdr_files = ${wildcard src/*.h}
SRCS := $(wildcard src/*.cpp)
OBJECTS := $(patsubst src/%.cpp,build/%.o, $(SRCS))
src_files = ${wildcard src/*.cpp}
src_files += ${wildcard include/libremidi/*.cpp}
viseq: $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS) $(LDLIBS)
all: viseq
viseq: $(src_files) $(hdr_files)
mkdir -p build
$(CXX) ${wildcard build/*.o} -o viseq $(LDFLAGS) $(LDLIBS)
$(src_files): %.cpp : %_src
$(CXX) -c $@ -o build/$(*F).o $(CXXFLAGS) $(CXXINC)
$(OBJECTS): build/%.o : src/%.cpp
@mkdir -p build
$(CXX) -c $< -o $@ $(CXXFLAGS) $(CXXINC)
$(hdr_files): %.h : %_hdr
@echo "G++ >---------- $(*F).h -----------<"
%_src:
@echo "G++ >---------- $(*F).cpp -----------<"
%_hdr:
@echo "G++ >---------- $(*F).h -----------<"
clean:
rm -f build/*
+57
View File
@@ -0,0 +1,57 @@
# VISEQ - THE VISUAL SEQUENCER
## BUILD
### LINUX
```
make
```
### WINDOWS
You need MingW64.
```
make -f MakefileMingW64
```
## USAGE
### CHANGING NODES
Nodes can be moved around, connected and modified. A right-cick opens a context menu with settings. Midi settings can also be changed with the mouse-wheel and modifier keys while hovering a node. All selected nodes can be changed at once. Draw a selectionbox around all nodes that you want to change, or select them by holding shift while clicking or dragging.
#### MOUSE WHEEL
- ctrl + wheel -> change midi note
- shift + wheel -> change midi velocity
- alt + wheel -> change midi gate
#### DRAGGING NODES
While dragging nodes one can use ctrl and shift to activate two different snapping modes. Ctrl enables snap to grid, while shift enables snap to next nodes. The grid is divided into 32nd notes. Snap to next nodes shows the time divisions to the closest nodes and can snap to two nodes at once.
#### SELECTING NODES
Dragging the mouse on the canvas, creates a selection rectangle. All nodes in the rectanlge will be highlighted. Using the above mentioned modification methods, one can change all selected nodes at once.
All selected nodes can also be moved together.
### CONNECTIONS
Connect nodes by dragging from the orbiting circle to another node. A new connection appears. Connections can be closed for signals in one, or both directions. Click the small green circle and it will block traffic in this direction.
#### DISTRIBUTION MODES
Nodes can have three distribution modes, "Geometric", "RR" (Round Robin), and "Random".
**Geometric Distribution**
Geometric distribution means that signals take the straightest path that is available, when more than two paths are available. By moving around nodes so that another connection becomes the straighter path, one can divert signals to other areas of the canvas.
**Round Robin Distribution**
Round Robin means, that every signal that arrives at a node with RR distribution switches the node to the next connection. With multiple signals in one path, this can lead to some beautiful patterns emerging.
**Random Distribution**
Random distribution sends every arriving signal to a random connection.
+4 -11
View File
@@ -1,15 +1,13 @@
#pragma once
#ifndef LIBREMIDI_API_C_H
#define LIBREMIDI_API_C_H
#if __cplusplus && !defined(LIBREMIDI_MODULE_BUILD)
#if __cplusplus
extern "C" {
#endif
//! MIDI API specifier arguments.
//! To get information on which feature is supported by each back-end, check their backend file
//! in e.g. backends/winmm.hpp, etc.
enum libremidi_api
typedef enum libremidi_api
{
UNSPECIFIED = 0x0, /*!< Search for a working compiled API. */
@@ -24,8 +22,6 @@ enum libremidi_api
PIPEWIRE, /*!< PipeWire */
KEYBOARD, /*!< Computer keyboard input */
NETWORK, /*!< MIDI over IP */
ANDROID_AMIDI, /*!< Android AMidi API */
KDMAPI, /*!< OmniMIDI KDMAPI (Windows) */
// MIDI 2.0 APIs
ALSA_RAW_UMP = 0x1000, /*!< Raw ALSA API for MIDI 2.0 */
@@ -38,11 +34,8 @@ enum libremidi_api
PIPEWIRE_UMP, /*!< MIDI2 over PipeWire. Requires v1.4+. */
DUMMY = 0xFFFF /*!< A compilable but non-functional API. */
};
} libremidi_api;
typedef enum libremidi_api libremidi_api;
#if __cplusplus && !defined(LIBREMIDI_MODULE_BUILD)
#if __cplusplus
}
#endif
#endif
+1 -3
View File
@@ -5,7 +5,7 @@
#include <string_view>
#include <vector>
NAMESPACE_LIBREMIDI
namespace libremidi
{
//! MIDI API specifier arguments.
//! To get information on which feature is supported by each back-end, check their backend file
@@ -64,8 +64,6 @@ inline constexpr libremidi::API default_api() noexcept
return API::ALSA_SEQ;
#elif defined(__emscripten__)
return API::EMSCRIPTEN_WEBMIDI;
#elif defined(LIBREMIDI_ANDROID)
return API::ANDROID_AMIDI;
#else
return API::DUMMY;
#endif
+8 -26
View File
@@ -29,9 +29,7 @@
#if defined(LIBREMIDI_JACK)
#include <libremidi/backends/jack.hpp>
#if defined(LIBREMIDI_HAS_JACK_GET_VERSION)
#include <libremidi/backends/jack_ump.hpp>
#endif
#include <libremidi/backends/jack_ump.hpp>
#endif
#if defined(LIBREMIDI_PIPEWIRE)
@@ -50,10 +48,6 @@
#include <libremidi/backends/winmm.hpp>
#endif
#if defined(LIBREMIDI_KDMAPI)
#include <libremidi/backends/kdmapi.hpp>
#endif
#if defined(LIBREMIDI_WINUWP)
#include <libremidi/backends/winuwp.hpp>
#endif
@@ -77,11 +71,7 @@
#include <libremidi/backends/network_ump.hpp>
#endif
#if defined(LIBREMIDI_ANDROID)
#include <libremidi/backends/android/android.hpp>
#endif
NAMESPACE_LIBREMIDI
namespace libremidi
{
// The order here will control the order of the API search in
// the constructor.
@@ -93,7 +83,7 @@ constexpr auto make_tl(unused, Args...)
namespace midi1
{
LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
static constexpr auto available_backends = make_tl(
0
#if defined(LIBREMIDI_ALSA)
,
@@ -111,10 +101,6 @@ LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
,
winmm_backend{}
#endif
#if defined(LIBREMIDI_KDMAPI)
,
kdmapi_backend{}
#endif
#if defined(LIBREMIDI_WINUWP)
,
winuwp_backend{}
@@ -138,10 +124,6 @@ LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
#if defined(LIBREMIDI_NETWORK)
,
net::backend{}
#endif
#if defined(LIBREMIDI_ANDROID)
,
android::backend{}
#endif
,
dummy_backend{});
@@ -167,7 +149,7 @@ auto for_backend(libremidi::API api, F&& f)
namespace midi2
{
LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
static constexpr auto available_backends = make_tl(
0
#if defined(LIBREMIDI_ALSA) && LIBREMIDI_ALSA_HAS_UMP
,
@@ -185,7 +167,7 @@ LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
,
net_ump::backend{}
#endif
#if defined(LIBREMIDI_JACK) && defined(LIBREMIDI_HAS_JACK_GET_VERSION)
#if defined(LIBREMIDI_JACK)
,
jack_ump::backend{}
#endif
@@ -234,7 +216,7 @@ auto for_backend(libremidi::API api, F&& f)
void for_input_configuration(auto f, libremidi::input_api_configuration& api_conf)
{
auto from_api = [&]<typename T>(T& /*backend*/) mutable {
if (auto conf = get_if<typename T::midi_in_configuration>(&api_conf))
if (auto conf = std::get_if<typename T::midi_in_configuration>(&api_conf))
{
f(*conf);
return true;
@@ -249,7 +231,7 @@ void for_input_configuration(auto f, libremidi::input_api_configuration& api_con
void for_output_configuration(auto f, libremidi::output_api_configuration& api_conf)
{
auto from_api = [&]<typename T>(T& /*backend*/) mutable {
if (auto conf = get_if<typename T::midi_out_configuration>(&api_conf))
if (auto conf = std::get_if<typename T::midi_out_configuration>(&api_conf))
{
f(*conf);
return true;
@@ -264,7 +246,7 @@ void for_output_configuration(auto f, libremidi::output_api_configuration& api_c
void for_observer_configuration(auto f, libremidi::observer_api_configuration& api_conf)
{
auto from_api = [&]<typename T>(T& /*backend*/) mutable {
if (auto conf = get_if<typename T::midi_observer_configuration>(&api_conf))
if (auto conf = std::get_if<typename T::midi_observer_configuration>(&api_conf))
{
f(*conf);
return true;
+1 -1
View File
@@ -9,7 +9,7 @@
// https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c
// Thanks Craig Stuart Sapp <craig@ccrma.stanford.edu>
NAMESPACE_LIBREMIDI::alsa_raw
namespace libremidi::alsa_raw
{
struct backend
{
@@ -10,12 +10,12 @@
#if __has_include(<poll.h>)
#include <poll.h>
NAMESPACE_LIBREMIDI
namespace libremidi
{
using poll_descriptors = pollfd;
}
#else
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct poll_descriptors
{
@@ -26,7 +26,7 @@ struct poll_descriptors
}
#endif
NAMESPACE_LIBREMIDI
namespace libremidi
{
/**
@@ -20,7 +20,9 @@
// https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c
// Thanks Craig Stuart Sapp <craig@ccrma.stanford.edu>
NAMESPACE_LIBREMIDI
namespace libremidi
{
namespace
{
struct alsa_raw_port_id
{
@@ -45,12 +47,12 @@ inline constexpr alsa_raw_port_id raw_from_port_handle(port_handle p) noexcept
static_assert(raw_from_port_handle(raw_to_port_handle({102, 7, 3})).card == 102);
static_assert(raw_from_port_handle(raw_to_port_handle({12, 7, 3})).dev == 7);
static_assert(raw_from_port_handle(raw_to_port_handle({12, 7, 3})).port == 3);
}
namespace alsa_raw
{
struct alsa_raw_port_info
{
// hw:1,2,0
std::string device;
std::string card_name;
std::string device_name;
@@ -96,6 +98,7 @@ struct enumerator
: handler{self}
, configuration{self.configuration}
{
}
// 1: is an input / output
@@ -118,9 +121,8 @@ struct enumerator
else if (status < 0 && status != -ENXIO)
{
handler.libremidi_handle_error(
configuration,
"Cannot get rawmidi information: " + device_identifier(card, device, sub) + " : "
+ snd.strerror(status));
configuration, "Cannot get rawmidi information: " + device_identifier(card, device, sub)
+ " : " + snd.strerror(status));
return status;
}
else
@@ -206,8 +208,7 @@ inline snd_ctl_wrapper::snd_ctl_wrapper(enumerator& self, const char* name)
if (status < 0)
{
self.handler.libremidi_handle_error(
self.configuration,
"cannot open control for card"s + name + " : " + snd.strerror(status));
self.configuration, "cannot open control for card"s + name + " : " + snd.strerror(status));
}
}
+14 -15
View File
@@ -10,7 +10,7 @@
#include <chrono>
#include <thread>
NAMESPACE_LIBREMIDI::alsa_raw
namespace libremidi::alsa_raw
{
class midi_in_impl
: public midi1::in_api
@@ -264,7 +264,8 @@ private:
using namespace std::literals;
libremidi_handle_error(
this->configuration, "error starting MIDI input thread: "s + e.what());
this->configuration,
"error starting MIDI input thread: "s + e.what());
return e.code();
}
return stdx::error{};
@@ -315,21 +316,19 @@ private:
{
if (configuration.timestamps == timestamp_mode::NoTimestamp)
{
configuration.manual_poll(
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer, fds);
}});
configuration.manual_poll(manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer, fds);
}});
}
else
{
configuration.manual_poll(
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
}});
configuration.manual_poll(manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
}});
}
}
@@ -343,7 +342,7 @@ private:
};
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <>
inline std::unique_ptr<midi_in_api> make<alsa_raw::midi_in_impl>(
@@ -8,7 +8,7 @@
#include <atomic>
#include <thread>
NAMESPACE_LIBREMIDI::alsa_raw
namespace libremidi::alsa_raw
{
class midi_out_impl final
: public midi1::out_api
@@ -44,7 +44,8 @@ public:
int status = snd.rawmidi.open(NULL, &midiport_, portname, mode);
if (status < 0)
{
libremidi_handle_error(this->configuration, "cannot open device.");
libremidi_handle_error(
this->configuration, "cannot open device.");
return from_errc(status);
}
return stdx::error{};
@@ -86,7 +87,8 @@ public:
{
if (auto err = snd.rawmidi.write(midiport_, message, size); err < 0)
{
libremidi_handle_error(this->configuration, "cannot write message.");
libremidi_handle_error(
this->configuration, "cannot write message.");
return from_errc(err);
}
@@ -9,7 +9,7 @@
#endif
#include <libremidi/detail/observer.hpp>
NAMESPACE_LIBREMIDI::alsa_raw
namespace libremidi::alsa_raw
{
template <typename Enumerator>
class observer_impl_base
@@ -28,21 +28,6 @@ public:
explicit observer_impl_base(
observer_configuration&& conf, alsa_raw_observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
}
~observer_impl_base()
{
#if LIBREMIDI_HAS_UDEV
m_termination_event.notify();
if (m_thread.joinable())
m_thread.join();
#endif
}
// Must be called for child classes due to virtual function calls
void finish_init()
{
if (!configuration.has_callbacks())
return;
@@ -63,6 +48,16 @@ public:
#endif
}
~observer_impl_base()
{
#if LIBREMIDI_HAS_UDEV
m_termination_event.notify();
if (m_thread.joinable())
m_thread.join();
#endif
}
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
std::vector<libremidi::input_port> ret;
@@ -89,6 +84,7 @@ public:
return ret;
}
private:
#if LIBREMIDI_HAS_UDEV
void run()
{
@@ -102,12 +98,6 @@ public:
return;
}
// Check eventfd to see if we need to exit
if (m_fds[1].revents & POLLIN)
{
break;
}
// Check udev
if (m_fds[0].revents & POLLIN)
{
@@ -132,6 +122,12 @@ public:
m_fds[0].revents = 0;
}
// Check eventfd
if (m_fds[1].revents & POLLIN)
{
break;
}
// Check timer
if (m_fds[2].revents & POLLIN)
{
@@ -150,35 +146,26 @@ public:
-> std::conditional_t<Input, input_port, output_port>
{
#if LIBREMIDI_HAS_UDEV
auto [container, device, type, manufacturer, product, serial]
= get_udev_soundcard_info(m_udev, p.card);
auto [container, device, type] = get_udev_soundcard_info(m_udev, p.card);
#else
container_identifier container{};
device_identifier device{};
libremidi::transport_type type{};
std::string manufacturer;
std::string product;
std::string serial;
libremidi::port_information::port_type type{};
#endif
std::string display_name = p.subdevice_name;
if (display_name.empty())
display_name = p.device_name;
if (display_name.empty())
display_name = p.card_name;
if (display_name.empty())
display_name = product;
if (display_name.empty())
display_name = "unknown";
return {
{.api = get_current_api(),
.client = 0,
{.client = 0,
.container = container,
.device = device,
.port = raw_to_port_handle({p.card, p.dev, p.sub}),
.manufacturer = manufacturer,
.product = product,
.serial = serial,
.manufacturer = p.card_name,
.device_name = p.device_name,
.port_name = p.subdevice_name,
.display_name = std::move(display_name),
@@ -256,16 +243,11 @@ public:
};
}
NAMESPACE_LIBREMIDI::alsa_raw
namespace libremidi::alsa_raw
{
struct observer_impl : observer_impl_base<alsa_raw::midi1_enumerator>
{
observer_impl(observer_configuration&& conf, alsa_raw_observer_configuration&& apiconf)
: observer_impl_base<alsa_raw::midi1_enumerator>{std::move(conf), std::move(apiconf)}
{
finish_init();
}
using alsa_raw::observer_impl_base<midi1_enumerator>::observer_impl_base;
libremidi::API get_current_api() const noexcept override { return libremidi::API::ALSA_RAW; }
};
}
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI::alsa_raw_ump
namespace libremidi::alsa_raw_ump
{
struct backend
{
@@ -4,7 +4,7 @@
#include <chrono>
#include <functional>
NAMESPACE_LIBREMIDI::alsa_raw_ump
namespace libremidi::alsa_raw_ump
{
struct input_configuration
{
@@ -5,7 +5,7 @@
#include <cstdio>
NAMESPACE_LIBREMIDI::alsa_raw_ump
namespace libremidi::alsa_raw_ump
{
struct midi2_enumerator : alsa_raw::enumerator
{
@@ -10,7 +10,7 @@
#include <atomic>
#include <thread>
NAMESPACE_LIBREMIDI::alsa_raw_ump
namespace libremidi::alsa_raw_ump
{
class midi_in_impl
: public midi2::in_api
@@ -44,8 +44,7 @@ public:
SND_RAWMIDI_NONBLOCK; // fixme
if (int err = snd.ump.open(&midiport_, 0, portname, mode); err < 0)
{
libremidi_handle_error(
this->configuration, "alsa_raw_ump::ump::open_port: cannot open device.");
libremidi_handle_error(this->configuration, "alsa_raw_ump::ump::open_port: cannot open device.");
return from_errc(err);
}
@@ -144,8 +143,7 @@ public:
while ((err = snd.ump.read(this->midiport_, words, nwords * 4)) > 0)
{
const auto to_ns = [this] { return absolute_timestamp(); };
m_processing.on_bytes(
{words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
m_processing.on_bytes({words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
}
return err;
}
@@ -168,8 +166,7 @@ public:
const auto to_ns = [ts] {
return static_cast<int64_t>(ts.tv_sec) * 1'000'000'000 + static_cast<int64_t>(ts.tv_nsec);
};
m_processing.on_bytes(
{words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
m_processing.on_bytes({words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
}
return err;
}
@@ -260,7 +257,8 @@ private:
using namespace std::literals;
libremidi_handle_error(
this->configuration, "error starting MIDI input thread: "s + e.what());
this->configuration,
"error starting MIDI input thread: "s + e.what());
return e.code();
}
return stdx::error{};
@@ -311,21 +309,19 @@ private:
{
if (configuration.timestamps == timestamp_mode::NoTimestamp)
{
configuration.manual_poll(
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer, fds);
}});
configuration.manual_poll(manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer, fds);
}});
}
else
{
configuration.manual_poll(
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
}});
configuration.manual_poll(manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
}});
}
}
@@ -339,7 +335,7 @@ private:
};
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <>
inline std::unique_ptr<midi_in_api> make<alsa_raw_ump::midi_in_impl>(
@@ -13,7 +13,7 @@
#include <system_error>
#include <utility>
NAMESPACE_LIBREMIDI::alsa_raw_ump
namespace libremidi::alsa_raw_ump
{
class midi_out_impl final
: public midi2::out_api
@@ -2,18 +2,13 @@
#include <libremidi/backends/alsa_raw/observer.hpp>
#include <libremidi/backends/alsa_raw_ump/helpers.hpp>
NAMESPACE_LIBREMIDI::alsa_raw_ump
namespace libremidi::alsa_raw_ump
{
class observer_impl : public alsa_raw::observer_impl_base<midi2_enumerator>
{
public:
observer_impl(
libremidi::observer_configuration&& conf, alsa_raw_ump::observer_configuration&& apiconf)
: observer_impl_base<alsa_raw_ump::midi2_enumerator>{std::move(conf), std::move(apiconf)}
{
finish_init();
}
using alsa_raw::observer_impl_base<midi2_enumerator>::observer_impl_base;
libremidi::API get_current_api() const noexcept override { return libremidi::API::ALSA_RAW_UMP; }
};
}
+2 -2
View File
@@ -20,7 +20,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI::alsa_seq
namespace libremidi::alsa_seq
{
struct backend
@@ -42,7 +42,7 @@ struct backend
if (!snd.available || !snd.seq.available)
return false;
return ::access("/dev/snd/seq", F_OK | R_OK | W_OK) == 0;
return ::access("/dev/snd/seq", F_OK) == 0;
}
};
@@ -15,7 +15,7 @@ typedef struct snd_seq_addr
}
#endif
NAMESPACE_LIBREMIDI::alsa_seq
namespace libremidi::alsa_seq
{
// Possible parameters:
@@ -9,7 +9,7 @@
#include <optional>
#include <string>
NAMESPACE_LIBREMIDI::alsa_seq
namespace libremidi::alsa_seq
{
struct event_handle
{
@@ -42,6 +42,8 @@ struct event_handle
~event_handle() { snd.seq.free_event(ev); }
};
namespace
{
inline constexpr port_handle seq_to_port_handle(uint64_t client, uint64_t port) noexcept
{
return (port << 32) + client;
@@ -54,24 +56,6 @@ inline constexpr std::pair<int, int> seq_from_port_handle(port_handle p) noexcep
return {client, port};
}
inline void for_all_clients(
const libasound& snd, snd_seq_t* seq, const std::function<void(snd_seq_client_info_t&)>& func)
{
snd_seq_client_info_t* cinfo{};
snd_seq_client_info_alloca(&cinfo);
snd_seq_port_info_t* pinfo{};
snd_seq_port_info_alloca(&pinfo);
snd.seq.client_info_set_client(cinfo, -1);
while (snd.seq.query_next_client(seq, cinfo) >= 0)
{
int client = snd.seq.client_info_get_client(cinfo);
if (client == 0)
continue;
func(*cinfo);
}
}
inline void for_all_ports(
const libasound& snd, snd_seq_t* seq,
const std::function<void(snd_seq_client_info_t&, snd_seq_port_info_t&)>& func)
@@ -98,21 +82,6 @@ inline void for_all_ports(
}
}
inline void for_all_ports(
const libasound& snd, snd_seq_t* seq, int client,
const std::function<void(snd_seq_port_info_t&)>& func)
{
snd_seq_port_info_t* pinfo{};
snd_seq_port_info_alloca(&pinfo);
snd.seq.port_info_set_client(pinfo, client);
snd.seq.port_info_set_port(pinfo, -1);
while (snd.seq.query_next_port(seq, pinfo) >= 0)
{
func(*pinfo);
}
}
// This function is used to count or get the pinfo structure for a given port
// number.
inline unsigned int iterate_port_info(
@@ -156,6 +125,7 @@ inline unsigned int iterate_port_info(
return count;
return 0;
}
}
// A structure to hold variables related to the ALSA API
// implementation.
@@ -189,15 +159,15 @@ struct alsa_data
snd.seq.set_client_name(seq, configuration.client_name.data());
#if __has_include(<alsa/ump.h>)
if (snd.seq.set_client_midi_version)
if (snd.seq.ump.set_client_midi_version)
{
switch (configuration.midi_version)
{
case 1:
snd.seq.set_client_midi_version(seq, SND_SEQ_CLIENT_LEGACY_MIDI);
snd.seq.ump.set_client_midi_version(seq, SND_SEQ_CLIENT_LEGACY_MIDI);
break;
case 2:
snd.seq.set_client_midi_version(seq, SND_SEQ_CLIENT_UMP_MIDI_2_0);
snd.seq.ump.set_client_midi_version(seq, SND_SEQ_CLIENT_UMP_MIDI_2_0);
break;
}
}
@@ -302,7 +272,8 @@ struct alsa_data
// Make subscription
if (int err = snd.seq.port_subscribe_malloc(&this->subscription); err < 0)
{
self.libremidi_handle_error(self.configuration, "ALSA error allocation port subscription.");
self.libremidi_handle_error(
self.configuration, "ALSA error allocation port subscription.");
return err;
}
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI::alsa_seq
namespace libremidi::alsa_seq
{
struct dummy_processing
{
@@ -195,6 +195,11 @@ public:
return stdx::error{};
}
stdx::error set_client_name(std::string_view clientName) override
{
return alsa_data::set_client_name(clientName);
}
stdx::error set_port_name(std::string_view portName) override
{
return alsa_data::set_port_name(portName);
@@ -482,13 +487,13 @@ public:
using poll_params = typename ConfigurationImpl::poll_parameters_type;
this->configuration.manual_poll(
poll_params{.addr = this->vaddr, .callback = [this](const auto& ev) {
if constexpr (ConfigurationImpl::midi_version == 1)
return this->process_event(ev);
if constexpr (ConfigurationImpl::midi_version == 1)
return this->process_event(ev);
#if __has_include(<alsa/ump.h>)
else
return this->process_ump_event(ev);
else
return this->process_ump_event(ev);
#endif
}});
}});
return 0;
}
@@ -525,7 +530,7 @@ public:
};
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <>
inline std::unique_ptr<midi_in_api>
@@ -3,7 +3,7 @@
#include <libremidi/backends/alsa_seq/helpers.hpp>
#include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI::alsa_seq
namespace libremidi::alsa_seq
{
class midi_out_impl final
@@ -110,6 +110,11 @@ public:
return stdx::error{};
}
stdx::error set_client_name(std::string_view clientName) override
{
return alsa_data::set_client_name(clientName);
}
stdx::error set_port_name(std::string_view portName) override
{
return alsa_data::set_port_name(portName);
+76 -191
View File
@@ -13,26 +13,19 @@
#include <map>
NAMESPACE_LIBREMIDI::alsa_seq
namespace libremidi::alsa_seq
{
struct client_info
{
std::string client_name;
int client_id{};
std::optional<int> card{};
};
struct port_info
{
std::string client_name;
std::string port_name;
int client{};
int port{};
bool is_input{};
bool is_output{};
bool isInput{};
bool isOutput{};
std::optional<int> card{};
libremidi::transport_type type{};
libremidi::port_information::port_type type{};
};
template <typename ConfigurationImpl>
@@ -100,69 +93,12 @@ public:
}
}
std::optional<client_info>
get_client_info(int client, snd_seq_client_info_t& cinfo) const noexcept
std::optional<port_info> get_info(int client, int port) const noexcept
{
client_info p;
p.client_id = client;
if (auto name = snd.seq.client_info_get_name(&cinfo))
p.client_name = name;
if (int card = snd.seq.client_info_get_card(&cinfo); card >= 0)
p.card = card;
return p;
}
std::optional<port_info> get_info(
int client, int port, snd_seq_client_info_t& cinfo,
snd_seq_port_info_t& pinfo) const noexcept
{
const auto tp = snd.seq.port_info_get_type(&pinfo);
const auto cap = snd.seq.port_info_get_capability(&pinfo);
if ((cap & SND_SEQ_PORT_CAP_NO_EXPORT) != 0)
return std::nullopt;
port_info p;
p.client = client;
p.port = port;
bool ok = this->configuration.track_any;
static constexpr auto virtual_port = SND_SEQ_PORT_TYPE_SOFTWARE | SND_SEQ_PORT_TYPE_SYNTHESIZER
| SND_SEQ_PORT_TYPE_APPLICATION;
if ((tp & SND_SEQ_PORT_TYPE_HARDWARE) && this->configuration.track_hardware)
{
p.type = libremidi::transport_type::hardware;
ok = true;
}
else if ((tp & virtual_port) && this->configuration.track_virtual)
{
p.type = libremidi::transport_type::software;
ok = true;
}
if (!ok)
return {};
if (auto name = snd.seq.client_info_get_name(&cinfo))
p.client_name = name;
if (auto name = snd.seq.port_info_get_name(&pinfo))
p.port_name = name;
if (int card = snd.seq.client_info_get_card(&cinfo); card >= 0)
p.card = card;
p.is_input = (cap & SND_SEQ_PORT_CAP_DUPLEX) | (cap & SND_SEQ_PORT_CAP_READ);
p.is_output = (cap & SND_SEQ_PORT_CAP_DUPLEX) | (cap & SND_SEQ_PORT_CAP_WRITE);
return p;
}
std::optional<port_info> get_info(int client, int port) const noexcept
{
snd_seq_client_info_t* cinfo;
snd_seq_client_info_alloca(&cinfo);
if (int err = snd.seq.get_any_client_info(seq, client, cinfo); err < 0)
@@ -173,7 +109,39 @@ public:
if (int err = snd.seq.get_any_port_info(seq, client, port, pinfo); err < 0)
return std::nullopt;
return get_info(client, port, *cinfo, *pinfo);
const auto tp = snd.seq.port_info_get_type(pinfo);
bool ok = this->configuration.track_any;
static constexpr auto virtual_port = SND_SEQ_PORT_TYPE_SOFTWARE | SND_SEQ_PORT_TYPE_SYNTHESIZER
| SND_SEQ_PORT_TYPE_APPLICATION;
if ((tp & SND_SEQ_PORT_TYPE_HARDWARE) && this->configuration.track_hardware)
{
p.type = libremidi::port_information::port_type::hardware;
ok = true;
}
else if ((tp & virtual_port) && this->configuration.track_virtual)
{
p.type = libremidi::port_information::port_type::software;
ok = true;
}
if (!ok)
return {};
if (auto name = snd.seq.client_info_get_name(cinfo))
p.client_name = name;
if (auto name = snd.seq.port_info_get_name(pinfo))
p.port_name = name;
if (int card = snd.seq.client_info_get_card(cinfo); card >= 0)
p.card = card;
const auto cap = snd.seq.port_info_get_capability(pinfo);
p.isInput = (cap & SND_SEQ_PORT_CAP_DUPLEX) | (cap & SND_SEQ_PORT_CAP_READ);
p.isOutput = (cap & SND_SEQ_PORT_CAP_DUPLEX) | (cap & SND_SEQ_PORT_CAP_WRITE);
return p;
}
template <bool Input>
@@ -185,10 +153,7 @@ public:
container_identifier container{};
device_identifier device{};
libremidi::transport_type type = p.type;
std::string manufacturer;
std::string product;
std::string serial;
libremidi::port_information::port_type type = p.type;
#if LIBREMIDI_HAS_UDEV
if (p.card)
{
@@ -196,20 +161,14 @@ public:
container = res.container;
device = res.path;
type = res.type;
manufacturer = res.vendor;
product = res.product;
serial = res.serial;
}
#endif
return {
{.api = get_current_api(),
.client = std::uintptr_t(this->seq),
{.client = std::uintptr_t(this->seq),
.container = container,
.device = device,
.port = alsa_seq::seq_to_port_handle(p.client, p.port),
.manufacturer = manufacturer,
.product = product,
.serial = serial,
.manufacturer = "",
.device_name = p.client_name,
.port_name = p.port_name,
.display_name = p.port_name,
@@ -220,10 +179,10 @@ public:
{
alsa_seq::for_all_ports(
snd, this->seq, [this](snd_seq_client_info_t& client, snd_seq_port_info_t& port) {
int clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port);
register_port(clt, pt);
});
int clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port);
register_port(clt, pt);
});
}
libremidi::API get_current_api() const noexcept override
@@ -239,12 +198,12 @@ public:
std::vector<libremidi::input_port> ret;
alsa_seq::for_all_ports(
snd, this->seq, [this, &ret](snd_seq_client_info_t& client, snd_seq_port_info_t& port) {
int clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port);
if (auto p = get_info(clt, pt, client, port))
if (p->is_input)
ret.push_back(to_port_info<true>(*p));
});
int clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port);
if (auto p = get_info(clt, pt))
if (p->isInput)
ret.push_back(to_port_info<true>(*p));
});
return ret;
}
@@ -253,12 +212,12 @@ public:
std::vector<libremidi::output_port> ret;
alsa_seq::for_all_ports(
snd, this->seq, [this, &ret](snd_seq_client_info_t& client, snd_seq_port_info_t& port) {
int clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port);
if (auto p = get_info(clt, pt, client, port))
if (p->is_output)
ret.push_back(to_port_info<false>(*p));
});
int clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port);
if (auto p = get_info(clt, pt))
if (p->isOutput)
ret.push_back(to_port_info<false>(*p));
});
return ret;
}
@@ -272,12 +231,12 @@ public:
return;
m_knownClients[{p.client, p.port}] = p;
if (p.is_input && configuration.input_added)
if (p.isInput && configuration.input_added)
{
configuration.input_added(to_port_info<true>(p));
}
if (p.is_output && configuration.output_added)
if (p.isOutput && configuration.output_added)
{
configuration.output_added(to_port_info<false>(p));
}
@@ -291,55 +250,31 @@ public:
auto p = it->second;
m_knownClients.erase(it);
if (p.is_input && configuration.input_removed)
if (p.isInput && configuration.input_removed)
{
configuration.input_removed(to_port_info<true>(p));
}
if (p.is_output && configuration.output_removed)
if (p.isOutput && configuration.output_removed)
{
configuration.output_removed(to_port_info<false>(p));
}
}
}
void handle_event_direct(const snd_seq_event_t& ev)
void handle_event(const snd_seq_event_t& ev)
{
switch (ev.type)
{
case SND_SEQ_EVENT_CLIENT_START: {
// TODO
break;
}
case SND_SEQ_EVENT_CLIENT_EXIT: {
// TODO
break;
}
case SND_SEQ_EVENT_CLIENT_CHANGE: {
// TODO
break;
}
#if LIBREMIDI_ALSA_HAS_UMP_SEQ_EVENTS
case SND_SEQ_EVENT_UMP_EP_CHANGE: {
// TODO
break;
}
case SND_SEQ_EVENT_UMP_BLOCK_CHANGE: {
// TODO
break;
}
#endif
case SND_SEQ_EVENT_PORT_START: {
this->register_port(ev.data.addr.client, ev.data.addr.port);
register_port(ev.data.addr.client, ev.data.addr.port);
break;
}
case SND_SEQ_EVENT_PORT_EXIT: {
this->unregister_port(ev.data.addr.client, ev.data.addr.port);
unregister_port(ev.data.addr.client, ev.data.addr.port);
break;
}
case SND_SEQ_EVENT_PORT_CHANGE:
// TODO
break;
default:
break;
}
@@ -374,87 +309,38 @@ public:
{
// Create relevant descriptors
auto& snd = alsa_data::snd;
// 1. Descriptor count
const auto n = snd.seq.poll_descriptors_count(this->seq, POLLIN);
int total_descriptors = n;
total_descriptors++; // eventfd for terminating the thread
// 2. Create storage
descriptors.resize(total_descriptors);
// 3. Store descriptors
snd.seq.poll_descriptors(this->seq, descriptors.data(), n, POLLIN);
descriptors[n] = this->termination_event;
descriptors_.resize(n + 1);
snd.seq.poll_descriptors(this->seq, descriptors_.data(), n, POLLIN);
descriptors_.back() = this->termination_event;
// Start the listening thread
thread = std::thread{[this, n] {
thread = std::thread{[this] {
auto& snd = alsa_data::snd;
const auto period
= std::chrono::duration_cast<std::chrono::milliseconds>(this->configuration.poll_period)
.count();
for (;;)
{
int err = poll(descriptors.data(), descriptors.size(), static_cast<int32_t>(period));
int err = poll(descriptors_.data(), descriptors_.size(), static_cast<int32_t>(period));
if (err >= 0)
{
// We got our stop-thread signal
if (descriptors[n].revents & POLLIN)
if (descriptors_.back().revents & POLLIN)
break;
// Put ALSA event in our queue
snd_seq_event_t* ev{};
event_handle handle{snd};
while (snd.seq.event_input(this->seq, &ev) >= 0)
{
handle.reset(ev);
this->handle_event_delayed(*ev);
}
// Process the events in a deferred way.
// This is because udev takes some milliseconds to populate its field after a
// port was added
auto tm = std::chrono::steady_clock::now();
for (auto it = queued_events.begin(); it != queued_events.end();)
{
if ((tm - it->second) >= this->configuration.poll_period)
{
this->handle_event_direct(it->first);
it = queued_events.erase(it);
}
else
{
break;
}
this->handle_event(*ev);
}
}
}
}};
}
void handle_event_delayed(const snd_seq_event_t& ev)
{
switch (ev.type)
{
case SND_SEQ_EVENT_CLIENT_START:
case SND_SEQ_EVENT_CLIENT_EXIT:
case SND_SEQ_EVENT_CLIENT_CHANGE:
#if LIBREMIDI_ALSA_HAS_UMP_SEQ_EVENTS
case SND_SEQ_EVENT_UMP_EP_CHANGE:
case SND_SEQ_EVENT_UMP_BLOCK_CHANGE:
#endif
case SND_SEQ_EVENT_PORT_START:
case SND_SEQ_EVENT_PORT_EXIT:
case SND_SEQ_EVENT_PORT_CHANGE:
queued_events.emplace_back(ev, std::chrono::steady_clock::now());
break;
default:
break;
}
}
~observer_threaded()
{
termination_event.notify();
@@ -465,8 +351,7 @@ public:
eventfd_notifier termination_event{};
std::thread thread;
std::vector<pollfd> descriptors;
std::vector<std::pair<snd_seq_event_t, std::chrono::steady_clock::time_point>> queued_events;
std::vector<pollfd> descriptors_;
};
template <typename ConfigurationImpl>
@@ -478,16 +363,16 @@ public:
{
this->configuration.manual_poll(
poll_parameters{.addr = this->vaddr, .callback = [this](const auto& v) {
this->handle_event_direct(v);
return 0;
}});
this->handle_event(v);
return 0;
}});
}
~observer_manual() { this->configuration.stop_poll(this->vaddr); }
};
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <>
inline std::unique_ptr<observer_api>
@@ -9,7 +9,7 @@
#include <variant>
NAMESPACE_LIBREMIDI::alsa_seq
namespace libremidi::alsa_seq
{
struct shared_handler : public libremidi::shared_context
@@ -127,13 +127,13 @@ struct shared_handler : public libremidi::shared_context
{
case callback_added: {
auto [addr, cb]
= std::move(*get_if<libremidi::alsa_seq::poll_parameters>(&ev.payload));
= std::move(*std::get_if<libremidi::alsa_seq::poll_parameters>(&ev.payload));
addresses.push_back(addr);
callbacks.push_back(std::move(cb));
break;
}
case callback_removed:
auto addr = *get_if<snd_seq_addr_t>(&ev.payload);
auto addr = *std::get_if<snd_seq_addr_t>(&ev.payload);
if (auto index = index_of_address(addr); index >= 0)
{
addresses.erase(addresses.begin() + index);
@@ -179,7 +179,7 @@ struct shared_handler : public libremidi::shared_context
struct event
{
event_type type;
libremidi_variant_alias::variant<libremidi::alsa_seq::poll_parameters, snd_seq_addr_t> payload;
std::variant<libremidi::alsa_seq::poll_parameters, snd_seq_addr_t> payload;
};
snd_seq_t* client{};
+3 -3
View File
@@ -9,7 +9,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <>
inline std::unique_ptr<observer_api>
@@ -42,7 +42,7 @@ inline std::unique_ptr<midi_in_api> make<
}
NAMESPACE_LIBREMIDI::alsa_seq_ump
namespace libremidi::alsa_seq_ump
{
struct backend
{
@@ -63,7 +63,7 @@ struct backend
if (!snd.available || !snd.seq.available || !snd.seq.ump.available || !snd.ump.available)
return false;
return ::access("/dev/snd/seq", F_OK | R_OK | W_OK) == 0;
return ::access("/dev/snd/seq", F_OK) == 0;
}
};
}
@@ -11,7 +11,7 @@ extern "C" {
typedef struct snd_seq_ump_event snd_seq_ump_event_t;
}
NAMESPACE_LIBREMIDI::alsa_seq_ump
namespace libremidi::alsa_seq_ump
{
struct poll_parameters
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_out.hpp>
#include <libremidi/detail/ump_stream.hpp>
NAMESPACE_LIBREMIDI::alsa_seq_ump
namespace libremidi::alsa_seq_ump
{
class midi_out_impl final
@@ -106,6 +106,11 @@ public:
return stdx::error{};
}
stdx::error set_client_name(std::string_view clientName) override
{
return alsa_data::set_client_name(clientName);
}
stdx::error set_port_name(std::string_view portName) override
{
return alsa_data::set_port_name(portName);
@@ -132,7 +137,7 @@ public:
static_assert(std::errc{0} == std::errc{});
return std::errc{};
};
segment_ump_stream(ump_stream, count, write_func, []() { });
segment_ump_stream(ump_stream, count, write_func, []() {});
snd.seq.drain_output(this->seq);
return stdx::error{};
@@ -1,33 +0,0 @@
#pragma once
#include <libremidi/backends/android/config.hpp>
#include <libremidi/backends/android/midi_in.hpp>
#include <libremidi/backends/android/midi_out.hpp>
#include <libremidi/backends/android/observer.hpp>
NAMESPACE_LIBREMIDI
{
namespace android
{
struct backend
{
using midi_in = android::midi_in;
using midi_out = android::midi_out;
using midi_observer = android::observer;
using midi_in_configuration = android::input_configuration;
using midi_out_configuration = android::output_configuration;
using midi_observer_configuration = android::observer_configuration;
static const constexpr auto API = libremidi::API::ANDROID_AMIDI;
static const constexpr std::string_view name = "android";
static const constexpr std::string_view display_name = "Android MIDI API";
static bool available() noexcept
{
#if defined(__ANDROID__)
return true;
#else
return false;
#endif
}
};
}
}
@@ -1,23 +0,0 @@
#pragma once
#include <libremidi/config.hpp>
NAMESPACE_LIBREMIDI
{
namespace android
{
struct input_configuration
{
std::string_view client_name = "libremidi client";
};
struct output_configuration
{
std::string_view client_name = "libremidi client";
};
struct observer_configuration
{
std::string_view client_name = "libremidi client";
};
}
}
@@ -1,239 +0,0 @@
#include <libremidi/backends/android/helpers.hpp>
#include <libremidi/backends/android/midi_in.hpp>
#include <libremidi/backends/android/midi_out.hpp>
NAMESPACE_LIBREMIDI::android
{
// Main source of knowledge is RtMidi implementation
// which was done by Yellow Labrador
JNIEnv* context::get_thread_env()
{
JNIEnv* env;
JavaVM* jvm;
jsize count;
if (JNI_GetCreatedJavaVMs(&jvm, 1, &count) != JNI_OK || count < 1)
{
LOGE("No JVM found");
return nullptr;
}
if (jvm->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_EDETACHED)
{
if (jvm->AttachCurrentThread(&env, nullptr) != JNI_OK)
{
LOGE("Failed to attach thread");
return nullptr;
}
}
return env;
}
jobject context::get_context(JNIEnv* env)
{
auto activity_thread = env->FindClass("android/app/ActivityThread");
auto current_activity_thread = env->GetStaticMethodID(
activity_thread, "currentActivityThread", "()Landroid/app/ActivityThread;");
auto at = env->CallStaticObjectMethod(activity_thread, current_activity_thread);
if (!at)
{
LOGE("Failed to get ActivityThread");
return nullptr;
}
auto get_application
= env->GetMethodID(activity_thread, "getApplication", "()Landroid/app/Application;");
auto app = env->CallObjectMethod(at, get_application);
if (!app)
{
LOGE("Failed to get Application");
return nullptr;
}
return app;
}
jobject context::get_midi_manager(JNIEnv* env, jobject ctx)
{
auto context_class = env->FindClass("android/content/Context");
auto get_system_service = env->GetMethodID(
context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
auto midi_service_str = env->NewStringUTF("midi");
auto service = env->CallObjectMethod(ctx, get_system_service, midi_service_str);
env->DeleteLocalRef(midi_service_str);
return service;
}
void context::refresh_midi_devices(JNIEnv* env, jobject ctx, bool is_output)
{
cleanup_devices(env);
auto midi_service = get_midi_manager(env, ctx);
if (!midi_service)
return;
auto midi_mgr_class = env->FindClass("android/media/midi/MidiManager");
auto get_devices_method
= env->GetMethodID(midi_mgr_class, "getDevices", "()[Landroid/media/midi/MidiDeviceInfo;");
auto device_array = (jobjectArray)env->CallObjectMethod(midi_service, get_devices_method);
auto device_info_class = env->FindClass("android/media/midi/MidiDeviceInfo");
auto get_input_count = env->GetMethodID(device_info_class, "getInputPortCount", "()I");
auto get_output_count = env->GetMethodID(device_info_class, "getOutputPortCount", "()I");
jsize count = env->GetArrayLength(device_array);
for (jsize i = 0; i < count; ++i)
{
auto device_info = env->GetObjectArrayElement(device_array, i);
int port_count = is_output ? env->CallIntMethod(device_info, get_input_count)
: env->CallIntMethod(device_info, get_output_count);
if (port_count > 0)
{
midi_devices.push_back(env->NewGlobalRef(device_info));
}
env->DeleteLocalRef(device_info);
}
env->DeleteLocalRef(device_array);
env->DeleteLocalRef(midi_service);
}
std::string context::port_name(JNIEnv* env, unsigned int port_number)
{
if (port_number >= midi_devices.size())
{
LOGE("Invalid port number");
return "";
}
auto device_info_class = env->FindClass("android/media/midi/MidiDeviceInfo");
auto get_props_method
= env->GetMethodID(device_info_class, "getProperties", "()Landroid/os/Bundle;");
auto bundle = env->CallObjectMethod(midi_devices[port_number], get_props_method);
auto bundle_class = env->FindClass("android/os/Bundle");
auto get_string_method
= env->GetMethodID(bundle_class, "getString", "(Ljava/lang/String;)Ljava/lang/String;");
auto key = env->NewStringUTF("name");
auto name_str = (jstring)env->CallObjectMethod(bundle, get_string_method, key);
std::string result;
if (name_str)
{
const char* name_chars = env->GetStringUTFChars(name_str, nullptr);
result = name_chars;
env->ReleaseStringUTFChars(name_str, name_chars);
env->DeleteLocalRef(name_str);
}
env->DeleteLocalRef(key);
env->DeleteLocalRef(bundle);
return result;
}
void context::cleanup_devices(JNIEnv* env)
{
for (jobject device : midi_devices)
{
env->DeleteGlobalRef(device);
}
midi_devices.clear();
}
void context::open_device(jobject device_info, void* target, bool is_output)
{
auto env = get_thread_env();
if (!env)
return;
auto ctx = get_context(env);
if (!ctx)
return;
auto midi_mgr = get_midi_manager(env, ctx);
if (!midi_mgr)
return;
// Check if we're on the main thread
auto looper_class = env->FindClass("android/os/Looper");
auto get_my_looper_method
= env->GetStaticMethodID(looper_class, "myLooper", "()Landroid/os/Looper;");
auto my_looper = env->CallStaticObjectMethod(looper_class, get_my_looper_method);
jobject handler = nullptr;
if (!my_looper)
{
LOGI("Not on a Looper thread, using main looper");
auto get_main_looper_method
= env->GetStaticMethodID(looper_class, "getMainLooper", "()Landroid/os/Looper;");
auto main_looper = env->CallStaticObjectMethod(looper_class, get_main_looper_method);
auto handler_class = env->FindClass("android/os/Handler");
auto handler_ctor = env->GetMethodID(handler_class, "<init>", "(Landroid/os/Looper;)V");
handler = env->NewObject(handler_class, handler_ctor, main_looper);
env->DeleteLocalRef(main_looper);
}
// Create the callback listener
auto callback_class = env->FindClass("dev/celtera/libremidi/MidiDeviceCallback");
if (!callback_class)
{
LOGE("MidiDeviceCallback class not found - ensure the Java class is loaded");
if (handler)
env->DeleteLocalRef(handler);
return;
}
auto callback_ctor = env->GetMethodID(callback_class, "<init>", "(JZ)V");
auto callback
= env->NewObject(callback_class, callback_ctor, (jlong)target, (jboolean)is_output);
// Open the device
auto midi_mgr_class = env->FindClass("android/media/midi/MidiManager");
auto open_device_method = env->GetMethodID(
midi_mgr_class, "openDevice",
"(Landroid/media/midi/MidiDeviceInfo;Landroid/media/midi/"
"MidiManager$OnDeviceOpenedListener;Landroid/os/Handler;)V");
env->CallVoidMethod(midi_mgr, open_device_method, device_info, callback, handler);
env->DeleteLocalRef(callback);
if (handler)
env->DeleteLocalRef(handler);
}
extern "C" JNIEXPORT void JNICALL Java_dev_celtera_libremidi_MidiDeviceCallback_onDeviceOpened(
JNIEnv* env, jobject /*thiz*/, jobject midi_device, jlong target_ptr, jboolean is_output)
{
if (!midi_device || target_ptr == 0)
{
LOGE("Invalid device or target pointer in callback");
return;
}
AMidiDevice* amidi_device = nullptr;
AMidiDevice_fromJava(env, midi_device, &amidi_device);
if (!amidi_device)
{
LOGE("Failed to convert Java MIDI device to AMidiDevice");
return;
}
if (is_output)
{
midi_out::open_callback(reinterpret_cast<midi_out*>(target_ptr), amidi_device);
}
else
{
midi_in::open_callback(reinterpret_cast<midi_in*>(target_ptr), amidi_device);
}
}
}
@@ -1,40 +0,0 @@
#pragma once
#include <libremidi/error.hpp>
#include <amidi/AMidi.h>
#include <android/log.h>
#include <jni.h>
#include <pthread.h>
#include <string>
#include <vector>
#define LOG_TAG "libremidi"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
NAMESPACE_LIBREMIDI
{
namespace android
{
struct context
{
static inline std::string client_name;
static inline std::vector<jobject> midi_devices;
static JNIEnv* get_thread_env();
static jobject get_context(JNIEnv* env);
static jobject get_midi_manager(JNIEnv* env, jobject context);
static void refresh_midi_devices(JNIEnv* env, jobject context, bool is_output);
static void open_device(jobject device_info, void* target, bool is_output);
static std::string port_name(JNIEnv* env, unsigned int port_number);
static void cleanup_devices(JNIEnv* env);
};
extern "C" JNIEXPORT void JNICALL Java_dev_celtera_libremidi_MidiDeviceCallback_onDeviceOpened(
JNIEnv* env, jobject /*thiz*/, jobject midi_device, jlong target_ptr, jboolean is_output);
}
}
@@ -1,165 +0,0 @@
#pragma once
#include <libremidi/backends/android/config.hpp>
#include <libremidi/backends/android/helpers.hpp>
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
#include <atomic>
#include <chrono>
#include <thread>
NAMESPACE_LIBREMIDI
{
namespace android
{
class midi_in
: public midi1::in_api
, public error_handler
{
public:
explicit midi_in(
libremidi::input_configuration&& conf, libremidi::android::input_configuration aconf)
: configuration{std::move(conf)}
{
if (!context::client_name.empty() && context::client_name != aconf.client_name)
{
libremidi_handle_error(
configuration, "Android backend only supports one client name per process");
return;
}
context::client_name = std::string(aconf.client_name);
client_open_ = stdx::error{};
}
~midi_in() override { close_port(); }
libremidi::API get_current_api() const noexcept override
{
return libremidi::API::ANDROID_AMIDI;
}
stdx::error open_port(const input_port& port, std::string_view /*name*/) override
{
if (is_port_open())
{
libremidi_handle_error(configuration, "midi_in::open_port: a port is already open");
return std::errc{};
}
const unsigned int port_number = port.port;
auto env = context::get_thread_env();
if (!env)
{
libremidi_handle_error(configuration, "midi_in::open_port: failed to get JNI environment");
return std::errc{};
}
auto ctx = context::get_context(env);
if (!ctx)
{
libremidi_handle_error(configuration, "midi_in::open_port: failed to get Android context");
return std::errc{};
}
context::refresh_midi_devices(env, ctx, false);
if (port_number >= context::midi_devices.size())
{
libremidi_handle_error(configuration, "midi_in::open_port: invalid port number");
return std::errc{};
}
port_open = true;
running = true;
context::open_device(context::midi_devices[port_number], this, false);
return stdx::error{};
}
stdx::error close_port() override
{
if (!is_port_open())
return std::errc{};
running = false;
if (poll_thread.joinable())
{
poll_thread.join();
}
if (midi_output_port)
{
AMidiOutputPort_close(midi_output_port);
midi_output_port = nullptr;
}
if (receive_device)
{
AMidiDevice_release(receive_device);
receive_device = nullptr;
}
port_open = false;
return stdx::error{};
}
bool is_port_open() const noexcept { return port_open; }
void start_midi_thread() { poll_thread = std::thread(&midi_in::poll_midi, this); }
timestamp absolute_timestamp() const noexcept override { return system_ns(); }
void poll_midi()
{
static constexpr timestamp_backend_info timestamp_info{
.has_absolute_timestamps = false,
.absolute_is_monotonic = false,
.has_samples = false,
};
uint8_t buffer[1024];
int32_t op_code;
int64_t timestamp;
size_t num_bytes;
while (running)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
ssize_t num_messages = AMidiOutputPort_receive(
midi_output_port, &op_code, buffer, sizeof(buffer), &num_bytes, &timestamp);
if (num_messages < 0)
{
LOGE("Error receiving MIDI data: %d", (int)num_messages);
continue;
}
if (num_messages > 0 && num_bytes > 0)
{
const auto to_ns = [=] { return timestamp; };
m_processing.on_bytes_multi(
{buffer, buffer + num_bytes},
m_processing.timestamp<timestamp_info>(to_ns, timestamp));
}
}
}
static void open_callback(midi_in* self, AMidiDevice* device)
{
self->receive_device = device;
AMidiOutputPort_open(device, 0, &self->midi_output_port);
self->start_midi_thread();
}
private:
libremidi::input_configuration configuration;
AMidiDevice* receive_device = nullptr;
AMidiOutputPort* midi_output_port = nullptr;
std::thread poll_thread;
std::atomic<bool> port_open{false};
std::atomic<bool> running{false};
midi1::input_state_machine m_processing{this->configuration};
};
}
}
@@ -1,126 +0,0 @@
#pragma once
#include <libremidi/backends/android/config.hpp>
#include <libremidi/backends/android/helpers.hpp>
#include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI
{
namespace android
{
class midi_out final
: public midi1::out_api
, public error_handler
{
public:
explicit midi_out(
libremidi::output_configuration&& conf, libremidi::android::output_configuration aconf)
: configuration{std::move(conf)}
{
if (!context::client_name.empty() && context::client_name != aconf.client_name)
{
libremidi_handle_error(
configuration, "Android backend only supports one client name per process");
return;
}
context::client_name = std::string(aconf.client_name);
client_open_ = stdx::error{};
}
~midi_out() override { close_port(); }
libremidi::API get_current_api() const noexcept override
{
return libremidi::API::ANDROID_AMIDI;
}
stdx::error open_port(const output_port& port, std::string_view /*name*/) override
{
if (is_port_open())
{
libremidi_handle_error(configuration, "midi_out::open_port: a port is already open");
return std::errc{};
}
const unsigned int port_number = port.port;
auto env = context::get_thread_env();
if (!env)
{
libremidi_handle_error(configuration, "midi_out::open_port: failed to get JNI environment");
return std::errc{};
}
auto ctx = context::get_context(env);
if (!ctx)
{
libremidi_handle_error(configuration, "midi_out::open_port: failed to get Android context");
return std::errc{};
}
context::refresh_midi_devices(env, ctx, true);
if (port_number >= context::midi_devices.size())
{
libremidi_handle_error(configuration, "midi_out::open_port: invalid port number");
return std::errc{};
}
port_open = true;
context::open_device(context::midi_devices[port_number], this, true);
return stdx::error{};
}
stdx::error close_port() override
{
if (!is_port_open())
return std::errc{};
if (midi_input_port)
{
AMidiInputPort_close(midi_input_port);
midi_input_port = nullptr;
}
if (send_device)
{
AMidiDevice_release(send_device);
send_device = nullptr;
}
port_open = false;
return stdx::error{};
}
bool is_port_open() const noexcept { return port_open; }
stdx::error send_message(const unsigned char* message, size_t size) override
{
if (!is_port_open() || !midi_input_port)
{
libremidi_handle_error(configuration, "midi_out::send_message: port is not open");
return std::errc{};
}
ssize_t result = AMidiInputPort_send(midi_input_port, message, size);
if (result < 0)
{
libremidi_handle_error(configuration, "midi_out::send_message: failed to send MIDI data");
return std::errc{};
}
return stdx::error{};
}
static void open_callback(midi_out* self, AMidiDevice* device)
{
self->send_device = device;
AMidiInputPort_open(device, 0, &self->midi_input_port);
}
private:
libremidi::output_configuration configuration;
AMidiDevice* send_device = nullptr;
AMidiInputPort* midi_input_port = nullptr;
bool port_open = false;
};
}
}
@@ -1,93 +0,0 @@
#pragma once
#include <libremidi/backends/android/config.hpp>
#include <libremidi/backends/android/helpers.hpp>
#include <libremidi/detail/observer.hpp>
NAMESPACE_LIBREMIDI
{
namespace android
{
class observer final
: public libremidi::observer_api
, public error_handler
{
public:
explicit observer(
libremidi::observer_configuration&& conf, libremidi::android::observer_configuration aconf)
: configuration{std::move(conf)}
{
if (!context::client_name.empty() && context::client_name != aconf.client_name)
{
LOGW("Android backend only supports one client name per process");
}
context::client_name = std::string(aconf.client_name);
}
~observer()
{
auto env = context::get_thread_env();
if (env)
{
context::cleanup_devices(env);
}
}
libremidi::API get_current_api() const noexcept override
{
return libremidi::API::ANDROID_AMIDI;
}
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
auto env = context::get_thread_env();
if (!env)
return {};
auto ctx = context::get_context(env);
if (!ctx)
return {};
context::refresh_midi_devices(env, ctx, false);
std::vector<libremidi::input_port> ports;
for (size_t i = 0; i < context::midi_devices.size(); ++i)
{
libremidi::input_port port;
port.api = libremidi::API::ANDROID_AMIDI;
port.port_name = context::port_name(env, i);
port.port = i;
ports.push_back(std::move(port));
}
return ports;
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
auto env = context::get_thread_env();
if (!env)
return {};
auto ctx = context::get_context(env);
if (!ctx)
return {};
context::refresh_midi_devices(env, ctx, true);
std::vector<libremidi::output_port> ports;
for (size_t i = 0; i < context::midi_devices.size(); ++i)
{
libremidi::output_port port;
port.port_name = context::port_name(env, i);
port.port = i;
ports.push_back(std::move(port));
}
return ports;
}
private:
libremidi::observer_configuration configuration;
};
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct core_backend
{
@@ -18,7 +18,7 @@ typedef MIDIObjectRef MIDIClientRef;
using MIDIClientRef = uint32_t;
using MIDIObjectRef = uint32_t;
#endif
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct coremidi_input_configuration
@@ -3,7 +3,7 @@
#include <CoreMIDI/CoreMIDI.h>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct coremidi_error_domain : public stdx::error_domain
{
+14 -10
View File
@@ -1,9 +1,9 @@
#pragma once
#include <libremidi/backends/coremidi/error_domain.hpp>
#include <libremidi/detail/memory.hpp>
#include <libremidi/detail/midi_api.hpp>
#include <libremidi/error_handler.hpp>
#include <libremidi/input_configuration.hpp>
#include <libremidi/backends/coremidi/error_domain.hpp>
#include <CoreMIDI/CoreMIDI.h>
#include <CoreServices/CoreServices.h>
@@ -21,12 +21,13 @@
#define LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME AudioGetCurrentHostTime
#endif
NAMESPACE_LIBREMIDI
namespace libremidi
{
using CFString_handle = unique_handle<const __CFString, CFRelease>;
using CFStringMutable_handle = unique_handle<__CFString, CFRelease>;
LIBREMIDI_STATIC std::string get_string_property(MIDIObjectRef object, CFStringRef property) noexcept
namespace
{
static inline std::string get_string_property(MIDIObjectRef object, CFStringRef property) noexcept
{
CFStringRef res;
if (MIDIObjectGetStringProperty(object, property, &res) || !res)
@@ -38,14 +39,14 @@ LIBREMIDI_STATIC std::string get_string_property(MIDIObjectRef object, CFStringR
return name;
}
LIBREMIDI_STATIC int32_t get_int_property(MIDIObjectRef object, CFStringRef property) noexcept
static inline int32_t get_int_property(MIDIObjectRef object, CFStringRef property) noexcept
{
SInt32 res;
MIDIObjectGetIntegerProperty(object, property, &res);
return res;
}
LIBREMIDI_STATIC CFString_handle toCFString(std::string_view str) noexcept
static inline CFString_handle toCFString(std::string_view str) noexcept
{
return CFString_handle{CFStringCreateWithCString(nullptr, str.data(), kCFStringEncodingASCII)};
}
@@ -71,12 +72,12 @@ inline uint64_t AudioConvertHostTimeToNanos(uint64_t hostTime)
}
#endif
LIBREMIDI_STATIC auto get_cfstring_property(MIDIObjectRef prop, CFStringRef name)
static inline auto get_cfstring_property(MIDIObjectRef prop, CFStringRef name)
{
CFStringRef str = nullptr;
MIDIObjectGetStringProperty(prop, name, &str);
return CFString_handle{str};
}
};
// This function was submitted by Douglas Casey Tucker and apparently
// derived largely from PortMidi.
@@ -219,19 +220,22 @@ locate_object(auto& self, const port_information& info, MIDIObjectType requested
auto ret = MIDIObjectFindByUniqueID(uid, &object, &type);
if (ret != noErr)
{
self.libremidi_handle_error(self.configuration, "cannot find port: " + info.port_name);
self.libremidi_handle_error(
self.configuration, "cannot find port: " + info.port_name);
return 0;
}
if (type != requested_type || object == 0)
{
self.libremidi_handle_error(
self.configuration, "invalid object: " + info.port_name + " : " + std::to_string(object));
self.configuration, "invalid object: " + info.port_name + " : "
+ std::to_string(object));
return 0;
}
return object;
}
}
// A structure to hold variables related to the CoreMIDI API
// implementation.
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_in_core final
: public midi1::in_api
@@ -25,7 +25,8 @@ public:
if (auto result = init_client(configuration); result != noErr)
{
libremidi_handle_error(
this->configuration, "error creating MIDI client object: " + std::to_string(result));
this->configuration,
"error creating MIDI client object: " + std::to_string(result));
client_open_ = from_osstatus(result);
return;
}
@@ -63,7 +64,8 @@ public:
{
close_client(*this);
libremidi_handle_error(
this->configuration, "error creating macOS MIDI input port: " + std::to_string(result));
this->configuration, "error creating macOS MIDI input port: "
+ std::to_string(result));
return from_osstatus(result);
}
@@ -72,7 +74,8 @@ public:
{
MIDIPortDispose(port);
close_client(*this);
libremidi_handle_error(this->configuration, "error connecting macOS MIDI input port.");
libremidi_handle_error(
this->configuration, "error connecting macOS MIDI input port.");
return from_osstatus(result);
}
@@ -102,7 +105,10 @@ public:
return stdx::error{};
}
stdx::error close_port() override { return coremidi_data::close_port(); }
stdx::error close_port() override
{
return coremidi_data::close_port();
}
timestamp absolute_timestamp() const noexcept override
{
@@ -3,7 +3,7 @@
#include <libremidi/backends/coremidi/helpers.hpp>
#include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_out_core final
: public midi1::out_api
@@ -24,7 +24,8 @@ public:
if (auto result = init_client(configuration); result != noErr)
{
libremidi_handle_error(
this->configuration, "error creating MIDI client object: " + std::to_string(result));
this->configuration,
"error creating MIDI client object: " + std::to_string(result));
client_open_ = from_osstatus(result);
return;
}
@@ -59,7 +60,8 @@ public:
if (result != noErr)
{
close_client(*this);
libremidi_handle_error(this->configuration, "error creating macOS MIDI output port.");
libremidi_handle_error(
this->configuration, "error creating macOS MIDI output port.");
return from_osstatus(result);
}
@@ -78,7 +80,9 @@ public:
if (result != noErr)
{
libremidi_handle_error(this->configuration, "error creating macOS virtual MIDI source.");
libremidi_handle_error(
this->configuration,
"error creating macOS virtual MIDI source.");
return from_osstatus(result);
}
@@ -88,7 +92,10 @@ public:
return stdx::error{};
}
stdx::error close_port() override { return coremidi_data::close_port(); }
stdx::error close_port() override
{
return coremidi_data::close_port();
}
stdx::error send_message(const unsigned char* message, size_t size) override
{
@@ -111,8 +118,8 @@ public:
const MIDITimeStamp timestamp = LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME();
const ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes;
Byte* buffer = (Byte*)alloca(bufsize + 16); // pad for other struct members
ByteCount listSize = bufsize + 16;
Byte buffer[bufsize + 16]; // pad for other struct members
ByteCount listSize = sizeof(buffer);
MIDIPacketList* packetList = (MIDIPacketList*)buffer;
ByteCount remainingBytes = nBytes;
@@ -130,7 +137,8 @@ public:
if (!packet)
{
libremidi_handle_error(this->configuration, "could not allocate packet list");
libremidi_handle_error(
this->configuration, "could not allocate packet list");
return std::errc::message_size;
}
@@ -155,7 +163,9 @@ public:
auto result = MIDISend(this->port, this->destinationId, packetList);
if (result != noErr)
{
libremidi_handle_warning(this->configuration, "error sending MIDI message to port.");
libremidi_handle_warning(
this->configuration,
"error sending MIDI message to port.");
return std::errc::io_error;
}
}
@@ -7,7 +7,7 @@
#include <bit>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class observer_core
: public observer_api
@@ -22,10 +22,6 @@ public:
explicit observer_core(observer_configuration&& conf, coremidi_observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
}
void finish_init()
{
if (configuration.client_name.empty())
configuration.client_name = "libremidi observer";
@@ -36,13 +32,15 @@ public:
auto result = MIDIClientCreate(
toCFString(configuration.client_name).get(),
+[](const MIDINotification* message, void* ctx) {
((observer_core*)ctx)->notify(message);
}, this, &client);
((observer_core*)ctx)->notify(message);
},
this, &client);
if (result != noErr)
{
libremidi_handle_error(
this->configuration, "error creating MIDI client object: " + std::to_string(result));
this->configuration,
"error creating MIDI client object: " + std::to_string(result));
return;
}
@@ -81,7 +79,7 @@ public:
return {};
// Get the MIDI device from the entity
libremidi::transport_type type{};
libremidi::port_information::port_type type{};
libremidi::container_identifier usb_location_id{};
libremidi::device_identifier usb_vendor_product{};
{
@@ -90,11 +88,11 @@ public:
{
if (device)
{
using enum libremidi::transport_type;
using enum libremidi::port_information::port_type;
SInt32 devid_res{};
if (MIDIObjectGetIntegerProperty(device, CFSTR("USBLocationID"), &devid_res) == noErr)
{
type = (libremidi::transport_type)(hardware | usb);
type = (libremidi::port_information::port_type)(hardware | usb);
usb_location_id = (uint64_t)devid_res;
}
@@ -102,26 +100,25 @@ public:
if (MIDIObjectGetIntegerProperty(device, CFSTR("USBVendorProduct"), &vendor_res)
== noErr)
{
type = (libremidi::transport_type)(hardware | usb);
type = (libremidi::port_information::port_type)(hardware | usb);
usb_vendor_product = (uint64_t)vendor_res;
}
const auto driver = get_string_property(device, kMIDIPropertyDriverOwner);
if (driver == "com.apple.AppleMIDIUSBDriver")
type = (libremidi::transport_type)(hardware | usb);
type = (libremidi::port_information::port_type)(hardware | usb);
if (driver == "com.apple.AppleMIDIBluetoothDriver")
type = (libremidi::transport_type)(hardware | bluetooth);
type = (libremidi::port_information::port_type)(hardware | bluetooth);
if (driver == "com.apple.AppleMIDIIACDriver")
type = (libremidi::transport_type)(software);
type = (libremidi::port_information::port_type)(software);
if (driver == "com.apple.AppleMIDIRTPDriver")
type = (libremidi::transport_type)(network);
type = (libremidi::port_information::port_type)(network);
}
}
}
return std::conditional_t<Input, input_port, output_port>{
{.api = get_current_api(),
.client = (std::uintptr_t)this->client,
{.client = (std::uintptr_t)this->client,
.container = usb_location_id,
.device = usb_vendor_product,
.port = std::bit_cast<uint32_t>(get_int_property(obj, kMIDIPropertyUniqueID)),
@@ -218,17 +215,3 @@ private:
MIDIClientRef client{};
};
}
NAMESPACE_LIBREMIDI::coremidi
{
struct observer_impl : observer_core
{
observer_impl(observer_configuration&& conf, coremidi_observer_configuration&& apiconf)
: observer_core{std::move(conf), std::move(apiconf)}
{
finish_init();
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::COREMIDI; }
};
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI::coremidi_ump
namespace libremidi::coremidi_ump
{
struct backend
{
@@ -1,7 +1,7 @@
#pragma once
#include <libremidi/backends/coremidi/config.hpp>
NAMESPACE_LIBREMIDI::coremidi_ump
namespace libremidi::coremidi_ump
{
struct input_configuration
{
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI::coremidi_ump
namespace libremidi::coremidi_ump
{
class midi_in_impl final
@@ -26,7 +26,8 @@ public:
if (auto result = init_client(configuration); result != noErr)
{
libremidi_handle_error(
this->configuration, "error creating MIDI client object: " + std::to_string(result));
this->configuration,
"error creating MIDI client object: " + std::to_string(result));
client_open_ = from_osstatus(result);
return;
}
@@ -68,7 +69,8 @@ public:
{
close_client(*this);
libremidi_handle_error(
this->configuration, "error creating macOS MIDI input port: " + std::to_string(result));
this->configuration, "error creating macOS MIDI input port: "
+ std::to_string(result));
return from_osstatus(result);
}
@@ -77,7 +79,8 @@ public:
{
MIDIPortDispose(port);
close_client(*this);
libremidi_handle_error(this->configuration, "error connecting macOS MIDI input port.");
libremidi_handle_error(
this->configuration, "error connecting macOS MIDI input port.");
return from_osstatus(result);
}
@@ -110,7 +113,10 @@ public:
return stdx::error{};
}
stdx::error close_port() override { return coremidi_data::close_port(); }
stdx::error close_port() override
{
return coremidi_data::close_port();
}
timestamp absolute_timestamp() const noexcept override
{
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_out.hpp>
#include <libremidi/detail/ump_stream.hpp>
NAMESPACE_LIBREMIDI::coremidi_ump
namespace libremidi::coremidi_ump
{
class midi_out_impl final
: public midi2::out_api
@@ -27,7 +27,8 @@ public:
if (auto result = init_client(configuration); result != noErr)
{
libremidi_handle_error(
this->configuration, "error creating MIDI client object: " + std::to_string(result));
this->configuration,
"error creating MIDI client object: " + std::to_string(result));
client_open_ = from_osstatus(result);
return;
}
@@ -62,7 +63,8 @@ public:
if (result != noErr)
{
close_client(*this);
libremidi_handle_error(this->configuration, "error creating macOS MIDI output port.");
libremidi_handle_error(
this->configuration, "error creating macOS MIDI output port.");
return from_osstatus(result);
}
@@ -82,7 +84,9 @@ public:
if (result != noErr)
{
this->endpoint = 0;
libremidi_handle_error(this->configuration, "error creating macOS virtual MIDI source.");
libremidi_handle_error(
this->configuration,
"error creating macOS virtual MIDI source.");
return from_osstatus(result);
}
@@ -90,7 +94,10 @@ public:
return stdx::error{};
}
stdx::error close_port() override { return coremidi_data::close_port(); }
stdx::error close_port() override
{
return coremidi_data::close_port();
}
stdx::error send_ump(const uint32_t* ump_stream, std::size_t count) override
{
@@ -135,7 +142,9 @@ public:
auto result = MIDISendEventList(this->port, this->destinationId, eventList);
if (result != noErr)
{
libremidi_handle_warning(this->configuration, "error sending MIDI message to port.");
libremidi_handle_warning(
this->configuration,
"error sending MIDI message to port.");
return std::errc::io_error;
}
}
@@ -1,20 +1,17 @@
#pragma once
#include <libremidi/backends/coremidi/observer.hpp>
#include <libremidi/backends/coremidi_ump/config.hpp>
#include <libremidi/backends/coremidi/observer.hpp>
NAMESPACE_LIBREMIDI::coremidi_ump
namespace libremidi::coremidi_ump
{
class observer_impl final : public libremidi::observer_core
{
public:
explicit observer_impl(
libremidi::observer_configuration&& conf, coremidi_ump::observer_configuration&& apiconf)
: observer_core{
std::move(conf),
coremidi_observer_configuration{apiconf.client_name, apiconf.on_create_context}}
explicit observer_impl(libremidi::observer_configuration&& conf, coremidi_ump::observer_configuration&& apiconf)
: observer_core{std::move(conf), coremidi_observer_configuration{apiconf.client_name, apiconf.on_create_context}}
{
finish_init();
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::COREMIDI_UMP; }
+25 -5
View File
@@ -7,7 +7,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class observer_dummy : public observer_api
{
@@ -40,9 +40,19 @@ public:
{
return stdx::error{};
}
stdx::error open_virtual_port(std::string_view /*portName*/) override { return stdx::error{}; }
stdx::error open_virtual_port(std::string_view /*portName*/) override
{
return stdx::error{};
}
stdx::error close_port() override { return stdx::error{}; }
stdx::error set_port_name(std::string_view /*portName*/) override { return stdx::error{}; }
stdx::error set_client_name(std::string_view /*clientName*/) override
{
return stdx::error{};
}
stdx::error set_port_name(std::string_view /*portName*/) override
{
return stdx::error{};
}
timestamp absolute_timestamp() const noexcept override { return 0; }
};
@@ -62,10 +72,20 @@ public:
{
return stdx::error{};
}
stdx::error open_virtual_port(std::string_view /*portName*/) override { return stdx::error{}; }
stdx::error open_virtual_port(std::string_view /*portName*/) override
{
return stdx::error{};
}
stdx::error close_port() override { return stdx::error{}; }
stdx::error set_port_name(std::string_view /*portName*/) override { return stdx::error{}; }
stdx::error set_client_name(std::string_view /*clientName*/) override
{
return stdx::error{};
}
stdx::error set_port_name(std::string_view /*portName*/) override
{
return stdx::error{};
}
stdx::error send_message(const unsigned char* /*message*/, size_t /*size*/) override
{
return stdx::error{};
+1 -1
View File
@@ -9,7 +9,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct emscripten_backend
@@ -1,7 +1,7 @@
#pragma once
#include <libremidi/config.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct emscripten_input_configuration
@@ -3,7 +3,7 @@
#include <string>
NAMESPACE_LIBREMIDI
namespace libremidi
{
namespace webmidi_helpers
{
@@ -16,7 +16,7 @@ EMSCRIPTEN_KEEPALIVE
void libremidi_devices_input(int port, double timestamp, int len, char* bytes);
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
namespace webmidi_helpers
{
@@ -37,25 +37,16 @@ public:
int input_count() const noexcept
{
if (!available())
return 0;
return EM_ASM_INT(return globalThis.__libreMidi_currentInputs.length;);
}
int output_count() const noexcept
{
if (!available())
return 0;
return EM_ASM_INT(return globalThis.__libreMidi_currentOutputs.length;);
}
void load_current_infos() noexcept
{
if (!available())
return;
#define get_js_string(variable_to_read, ...) \
(char*)EM_ASM_INT( \
{ \
@@ -215,9 +206,6 @@ public:
stdx::error send_message(int port_index, const char* bytes, int len)
{
if (!available())
return std::errc::operation_not_supported;
const auto& id = m_current_outputs[port_index].id;
EM_ASM(
{
@@ -228,8 +216,6 @@ public:
output.send(Array.from(bytes));
},
bytes, len, id.c_str());
return stdx::error{};
}
const std::vector<device_information>& inputs() const noexcept { return m_current_inputs; }
@@ -262,9 +248,6 @@ private:
void start_stream(int port_index)
{
if (!available())
return;
// Isn't life great...
// https://github.com/Planeshifter/emscripten-examples/tree/master/01_PassingArrays
const auto& id = m_current_inputs[port_index].id;
@@ -299,9 +282,6 @@ private:
void stop_stream(int port_index)
{
if (!available())
return;
const auto& id = m_current_inputs[port_index].id;
EM_ASM(const id = UTF8ToString($1);
@@ -5,7 +5,7 @@
#include <chrono>
NAMESPACE_LIBREMIDI
namespace libremidi
{
LIBREMIDI_INLINE midi_in_emscripten::midi_in_emscripten(
input_configuration&& conf, emscripten_input_configuration&& apiconf)
@@ -32,7 +32,8 @@ LIBREMIDI_INLINE stdx::error midi_in_emscripten::open_port(int portNumber, std::
if (portNumber < 0 || portNumber >= midi.input_count())
{
libremidi_handle_error(this->configuration, "no MIDI output sources found.");
libremidi_handle_error(
this->configuration, "no MIDI output sources found.");
return std::errc::invalid_argument;
}
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_in_emscripten final
: public midi1::in_api
@@ -2,7 +2,7 @@
#include <libremidi/backends/emscripten/midi_access.hpp>
#include <libremidi/backends/emscripten/midi_out.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
LIBREMIDI_INLINE midi_out_emscripten::midi_out_emscripten(
output_configuration&& conf, emscripten_output_configuration&& apiconf)
@@ -29,7 +29,8 @@ LIBREMIDI_INLINE stdx::error midi_out_emscripten::open_port(int portNumber, std:
if (portNumber >= midi.output_count())
{
libremidi_handle_error(this->configuration, "no MIDI output sources found.");
libremidi_handle_error(
this->configuration, "no MIDI output sources found.");
return std::errc::invalid_argument;
}
@@ -37,19 +38,16 @@ LIBREMIDI_INLINE stdx::error midi_out_emscripten::open_port(int portNumber, std:
return stdx::error{};
}
LIBREMIDI_INLINE stdx::error
midi_out_emscripten::open_port(const output_port& p, std::string_view nm)
LIBREMIDI_INLINE stdx::error midi_out_emscripten::open_port(const output_port& p, std::string_view nm)
{
return open_port(p.port, nm);
}
LIBREMIDI_INLINE stdx::error midi_out_emscripten::close_port()
{
LIBREMIDI_INLINE stdx::error midi_out_emscripten::close_port() {
return stdx::error{};
}
LIBREMIDI_INLINE stdx::error
midi_out_emscripten::send_message(const unsigned char* message, size_t size)
LIBREMIDI_INLINE stdx::error midi_out_emscripten::send_message(const unsigned char* message, size_t size)
{
if (m_portNumber < 0)
libremidi_handle_error(
@@ -3,7 +3,7 @@
#include <libremidi/backends/emscripten/helpers.hpp>
#include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_out_emscripten final
: public midi1::out_api
@@ -4,7 +4,7 @@
#include <cassert>
NAMESPACE_LIBREMIDI
namespace libremidi
{
LIBREMIDI_INLINE observer_emscripten::observer_emscripten(
observer_configuration&& conf, emscripten_observer_configuration&& apiconf)
@@ -42,8 +42,7 @@ static auto to_port_info(int index, const webmidi_helpers::device_information& d
-> std::conditional_t<Input, input_port, output_port>
{
return {
{.api = libremidi::API::WEBMIDI,
.client = 0,
{.client = 0,
.port = (uint64_t)index,
.manufacturer = "",
.device_name = "",
@@ -5,7 +5,7 @@
#include <vector>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class observer_emscripten final : public observer_api
{
+1 -1
View File
@@ -13,7 +13,7 @@
//
// *********************************************************************//
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct jack_backend
{
+1 -1
View File
@@ -11,7 +11,7 @@ typedef uint32_t jack_nframes_t;
typedef int (*JackProcessCallback)(jack_nframes_t nframes, void* arg);
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
using jack_callback_function = std::function<void(int nframes)>;
struct jack_callback
@@ -11,7 +11,7 @@
#include <jack/ringbuffer.h>
#endif
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct jack_error_domain : public stdx::error_domain
{
@@ -34,36 +34,36 @@ public:
stdx::string_ref message(const stdx::error& e) const noexcept override
{
const auto status = error_cast<jack_status_t>(e);
if (status == jack_status_t{})
if(status == jack_status_t{})
return "Success";
if (status & JackInvalidOption)
if(status & JackInvalidOption)
return "The operation contained an invalid or unsupported option";
if (status & JackServerFailed)
if(status & JackServerFailed)
return "Unable to connect to the JACK server";
if (status & JackServerError)
if(status & JackServerError)
return "Communication error with the JACK server";
if (status & JackNoSuchClient)
if(status & JackNoSuchClient)
return "Requested client does not exist";
if (status & JackLoadFailure)
if(status & JackLoadFailure)
return "Unable to load internal client";
if (status & JackInitFailure)
if(status & JackInitFailure)
return "Unable to initialize client";
if (status & JackShmFailure)
if(status & JackShmFailure)
return "Unable to access shared memory";
if (status & JackVersionError)
if(status & JackVersionError)
return "Client's protocol version does not match";
if (status & JackBackendError)
if(status & JackBackendError)
return "Backend error";
if (status & JackClientZombie)
if(status & JackClientZombie)
return "Client zombified failure";
if (status & JackFailure)
if(status & JackFailure)
return "Failure";
if (status & JackNameNotUnique)
if(status & JackNameNotUnique)
return "The desired client name was not unique";
// Can't happen in libremidi as we set JackNoStartServer
if (status & JackServerStarted)
if(status & JackServerStarted)
return "Server was started";
return "Unknown JACK status code";
+28 -27
View File
@@ -9,7 +9,7 @@
#include <cstring>
#include <memory>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct jack_client
{
@@ -41,22 +41,21 @@ struct jack_client
}
}
template <bool Input, libremidi::API Api>
template <bool Input>
static auto to_port_info(jack_client_t* client, jack_port_t* port)
-> std::conditional_t<Input, input_port, output_port>
{
return {{
.api = Api,
.client = reinterpret_cast<std::uintptr_t>(client),
.port = 0,
.manufacturer = "",
.device_name = jack_get_client_name(client),
.device_name = "",
.port_name = jack_port_name(port),
.display_name = get_port_display_name(port),
}};
}
template <bool Input, libremidi::API Api>
template <bool Input>
static auto get_ports(
jack_client_t* client, const char* pattern, const char* type, const JackPortFlags flags,
bool midi2) noexcept -> std::vector<std::conditional_t<Input, input_port, output_port>>
@@ -79,7 +78,7 @@ struct jack_client
if (port)
{
if (bool(midi2) == bool(jack_port_flags(port) & 0x20))
ret.push_back(to_port_info<Input, Api>(client, port));
ret.push_back(to_port_info<Input>(client, port));
}
i++;
}
@@ -138,13 +137,13 @@ struct jack_helpers : jack_client
configuration.set_process_func(
{.token = this_instance,
.callback = [&self, p = std::weak_ptr{this->port.impl}](jack_nframes_t nf) -> int {
if (auto pt = p.lock())
if (pt->load())
self.process(nf);
if (auto pt = p.lock())
if (pt->load())
self.process(nf);
self.thread_lock.check_client_released();
return 0;
}});
self.thread_lock.check_client_released();
return 0;
}});
this->client = configuration.context;
return jack_status_t{};
@@ -156,25 +155,26 @@ struct jack_helpers : jack_client
= jack_client_open(configuration.client_name.c_str(), JackNoStartServer, &status);
if (this->client != nullptr)
{
if (status & JackNameNotUnique)
{
self.libremidi_handle_warning(
self.configuration, "JACK client with the same name already exists, renamed.");
if(status & JackNameNotUnique) {
self.libremidi_handle_warning(self.configuration, "JACK client with the same name already exists, renamed.");
}
jack_set_process_callback(this->client, +[](jack_nframes_t nf, void* ctx) -> int {
auto& self = *static_cast<Self*>(ctx);
jack_port_t* port = self.port;
jack_set_process_callback(
this->client,
+[](jack_nframes_t nf, void* ctx) -> int {
auto& self = *static_cast<Self*>(ctx);
jack_port_t* port = self.port;
// Is port created?
if (port == nullptr)
return 0;
// Is port created?
if (port == nullptr)
return 0;
self.process(nf);
self.process(nf);
self.thread_lock.check_client_released();
return 0;
}, &self);
self.thread_lock.check_client_released();
return 0;
},
&self);
jack_activate(this->client);
}
return status;
@@ -208,7 +208,8 @@ struct jack_helpers : jack_client
if (self.configuration.client_name.size() + portName.size() + 2u
>= static_cast<size_t>(jack_port_name_size()))
{
self.libremidi_handle_error(self.configuration, "port name length limit exceeded");
self.libremidi_handle_error(
self.configuration, "port name length limit exceeded");
return std::errc::invalid_argument;
}
+3 -3
View File
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_in_jack final
: public midi1::in_api
@@ -53,8 +53,8 @@ public:
err != 0 && err != EEXIST)
{
libremidi_handle_error(
configuration,
"could not connect to port: " + port.port_name + " -> " + jack_port_name(this->port));
configuration, "could not connect to port: " + port.port_name + " -> "
+ jack_port_name(this->port));
return from_errc(err);
}
return stdx::error{};
+4 -3
View File
@@ -3,7 +3,7 @@
#include <libremidi/backends/jack/helpers.hpp>
#include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_out_jack
: public midi1::out_api
@@ -38,7 +38,8 @@ public:
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);
libremidi_handle_error(
configuration, "could not connect to port" + port.port_name);
return from_errc(err);
}
@@ -166,7 +167,7 @@ public:
};
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <>
inline std::unique_ptr<midi_out_api> make<midi_out_jack>(
+25 -29
View File
@@ -5,7 +5,7 @@
#include <unordered_set>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class observer_jack final
: public observer_api
@@ -70,8 +70,7 @@ public:
{
seen_input_ports.insert(ports[i]);
if (this->configuration.input_added && configuration.notify_in_constructor)
this->configuration.input_added(
to_port_info<true, libremidi::API::JACK_MIDI>(client, port));
this->configuration.input_added(to_port_info<true>(client, port));
}
i++;
}
@@ -102,8 +101,7 @@ public:
{
seen_output_ports.insert(ports[i]);
if (this->configuration.output_added && configuration.notify_in_constructor)
this->configuration.output_added(
to_port_info<false, libremidi::API::JACK_MIDI>(client, port));
this->configuration.output_added(to_port_info<false>(client, port));
}
i++;
}
@@ -140,15 +138,13 @@ public:
{
seen_input_ports.insert(name);
if (this->configuration.input_added)
this->configuration.input_added(
to_port_info<true, libremidi::API::JACK_MIDI>(client, port));
this->configuration.input_added(to_port_info<true>(client, port));
}
else if (flags & JackPortIsInput)
{
seen_output_ports.insert(name);
if (this->configuration.output_added)
this->configuration.output_added(
to_port_info<false, libremidi::API::JACK_MIDI>(client, port));
this->configuration.output_added(to_port_info<false>(client, port));
}
}
else
@@ -156,15 +152,13 @@ public:
if (auto it = seen_input_ports.find(name); it != seen_input_ports.end())
{
if (this->configuration.input_removed)
this->configuration.input_removed(
to_port_info<true, libremidi::API::JACK_MIDI>(client, port));
this->configuration.input_removed(to_port_info<true>(client, port));
seen_input_ports.erase(it);
}
if (auto it = seen_output_ports.find(name); it != seen_output_ports.end())
{
if (this->configuration.output_removed)
this->configuration.output_removed(
to_port_info<false, libremidi::API::JACK_MIDI>(client, port));
this->configuration.output_removed(to_port_info<false>(client, port));
seen_output_ports.erase(it);
}
}
@@ -177,37 +171,39 @@ public:
if (!configuration.has_callbacks())
return;
jack_set_port_registration_callback(this->client, +[](jack_port_id_t p, int r, void* arg) {
auto& self = *(observer_jack*)arg;
if (auto port = jack_port_by_id(self.client, p))
{
self.on_port_callback(port, r != 0);
}
}, this);
jack_set_port_registration_callback(
this->client,
+[](jack_port_id_t p, int r, void* arg) {
auto& self = *(observer_jack*)arg;
if (auto port = jack_port_by_id(self.client, p))
{
self.on_port_callback(port, r != 0);
}
},
this);
jack_set_port_rename_callback(
this->client,
+[](jack_port_id_t p, const char* /*old_name*/, const char* /*new_name*/, void* arg) {
const auto& self = *static_cast<observer_jack*>(arg);
const auto& self = *static_cast<observer_jack*>(arg);
auto port = jack_port_by_id(self.client, p);
if (!port)
return;
}, this);
auto port = jack_port_by_id(self.client, p);
if (!port)
return;
},
this);
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_MIDI; }
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
return get_ports<true, libremidi::API::JACK_MIDI>(
this->client, nullptr, port_type, JackPortIsOutput, false);
return get_ports<true>(this->client, nullptr, port_type, JackPortIsOutput, false);
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
return get_ports<false, libremidi::API::JACK_MIDI>(
this->client, nullptr, port_type, JackPortIsInput, false);
return get_ports<false>(this->client, nullptr, port_type, JackPortIsInput, false);
}
~observer_jack()
@@ -8,7 +8,7 @@
#include <variant>
NAMESPACE_LIBREMIDI::jack
namespace libremidi::jack
{
// Create a JACK client which will be shared across objects
@@ -23,10 +23,13 @@ struct shared_handler : public libremidi::shared_context
client = jack_client_open(v.data(), JackNoStartServer, &status);
assert(client);
assert(status == 0);
jack_set_process_callback(client, +[](jack_nframes_t cnt, void* ctx) -> int {
((shared_handler*)ctx)->jack_callback(cnt);
return 0;
}, this);
jack_set_process_callback(
client,
+[](jack_nframes_t cnt, void* ctx) -> int {
((shared_handler*)ctx)->jack_callback(cnt);
return 0;
},
this);
}
virtual void start_processing() override { jack_activate(client); }
@@ -70,10 +73,11 @@ struct shared_handler : public libremidi::shared_context
switch (ev.type)
{
case in_callback_added:
midiin_callbacks.push_back(std::move(*get_if<libremidi::jack_callback>(&ev.payload)));
midiin_callbacks.push_back(
std::move(*std::get_if<libremidi::jack_callback>(&ev.payload)));
break;
case in_callback_removed: {
auto idx = *get_if<int64_t>(&ev.payload);
auto idx = *std::get_if<int64_t>(&ev.payload);
for (auto it = midiin_callbacks.begin(); it != midiin_callbacks.end();)
{
if (it->token == idx)
@@ -89,10 +93,11 @@ struct shared_handler : public libremidi::shared_context
break;
}
case out_callback_added:
midiout_callbacks.push_back(std::move(*get_if<libremidi::jack_callback>(&ev.payload)));
midiout_callbacks.push_back(
std::move(*std::get_if<libremidi::jack_callback>(&ev.payload)));
break;
case out_callback_removed:
auto idx = *get_if<int64_t>(&ev.payload);
auto idx = *std::get_if<int64_t>(&ev.payload);
for (auto it = midiout_callbacks.begin(); it != midiout_callbacks.end();)
{
if (it->token == idx)
@@ -136,7 +141,7 @@ struct shared_handler : public libremidi::shared_context
struct event
{
event_type type;
libremidi_variant_alias::variant<libremidi::jack_callback, int64_t> payload;
std::variant<libremidi::jack_callback, int64_t> payload;
};
boost::lockfree::spsc_queue<event> events{16};
+1 -1
View File
@@ -5,7 +5,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI::jack_ump
namespace libremidi::jack_ump
{
struct backend
{
@@ -1,7 +1,7 @@
#pragma once
#include <libremidi/backends/jack/config.hpp>
NAMESPACE_LIBREMIDI::jack_ump
namespace libremidi::jack_ump
{
struct input_configuration
{
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI::jack_ump
namespace libremidi::jack_ump
{
class midi_in_jack final
: public midi2::in_api
@@ -3,7 +3,7 @@
#include <libremidi/backends/jack_ump/config.hpp>
#include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI::jack_ump
namespace libremidi::jack_ump
{
class midi_out_jack
: public midi2::out_api
@@ -170,7 +170,7 @@ public:
};
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <>
inline std::unique_ptr<midi_out_api>
@@ -5,7 +5,7 @@
#include <unordered_set>
NAMESPACE_LIBREMIDI::jack_ump
namespace libremidi::jack_ump
{
class observer_jack final
: public observer_api
@@ -77,8 +77,7 @@ public:
{
seen_input_ports.insert(ports[i]);
if (this->configuration.input_added && configuration.notify_in_constructor)
this->configuration.input_added(
to_port_info<true, libremidi::API::JACK_UMP>(client, port));
this->configuration.input_added(to_port_info<true>(client, port));
}
i++;
}
@@ -115,8 +114,7 @@ public:
{
seen_output_ports.insert(ports[i]);
if (this->configuration.output_added && configuration.notify_in_constructor)
this->configuration.output_added(
to_port_info<false, libremidi::API::JACK_UMP>(client, port));
this->configuration.output_added(to_port_info<false>(client, port));
}
i++;
}
@@ -156,15 +154,13 @@ public:
{
seen_input_ports.insert(name);
if (this->configuration.input_added)
this->configuration.input_added(
to_port_info<true, libremidi::API::JACK_UMP>(client, port));
this->configuration.input_added(to_port_info<true>(client, port));
}
else if (flags & JackPortIsInput)
{
seen_output_ports.insert(name);
if (this->configuration.output_added)
this->configuration.output_added(
to_port_info<false, libremidi::API::JACK_UMP>(client, port));
this->configuration.output_added(to_port_info<false>(client, port));
}
}
else
@@ -172,15 +168,13 @@ public:
if (auto it = seen_input_ports.find(name); it != seen_input_ports.end())
{
if (this->configuration.input_removed)
this->configuration.input_removed(
to_port_info<true, libremidi::API::JACK_UMP>(client, port));
this->configuration.input_removed(to_port_info<true>(client, port));
seen_input_ports.erase(it);
}
if (auto it = seen_output_ports.find(name); it != seen_output_ports.end())
{
if (this->configuration.output_removed)
this->configuration.output_removed(
to_port_info<false, libremidi::API::JACK_UMP>(client, port));
this->configuration.output_removed(to_port_info<false>(client, port));
seen_output_ports.erase(it);
}
}
@@ -216,14 +210,12 @@ public:
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
return get_ports<true, libremidi::API::JACK_UMP>(
this->client, nullptr, port_type, JackPortIsOutput, true);
return get_ports<true>(this->client, nullptr, port_type, JackPortIsOutput, true);
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
return get_ports<false, libremidi::API::JACK_UMP>(
this->client, nullptr, port_type, JackPortIsInput, true);
return get_ports<false>(this->client, nullptr, port_type, JackPortIsInput, true);
}
~observer_jack()
-43
View File
@@ -1,43 +0,0 @@
#pragma once
#include <libremidi/backends/kdmapi/midi_in.hpp>
#include <libremidi/backends/kdmapi/midi_out.hpp>
#include <libremidi/backends/kdmapi/observer.hpp>
#include <string_view>
//*********************************************************************//
// API: OmniMIDI KDMAPI (Keppy's Direct MIDI API)
//*********************************************************************//
// OmniMIDI is a low-latency MIDI synthesizer driver for Windows.
// KDMAPI provides a direct interface to OmniMIDI, bypassing the
// Windows Multimedia API for lower latency.
//
// Note: KDMAPI is output-only. The midi_in class is a placeholder
// that always fails - use a different backend for MIDI input.
//
// https://github.com/KeppySoftware/OmniMIDI
namespace libremidi
{
struct kdmapi_backend
{
using midi_in = kdmapi::midi_in;
using midi_out = kdmapi::midi_out;
using midi_observer = kdmapi::observer;
using midi_in_configuration = kdmapi::input_configuration;
using midi_out_configuration = kdmapi::output_configuration;
using midi_observer_configuration = kdmapi::observer_configuration;
static const constexpr auto API = libremidi::API::KDMAPI;
static const constexpr std::string_view name = "kdmapi";
static const constexpr std::string_view display_name = "OmniMIDI (KDMAPI)";
static inline bool available() noexcept
{
return kdmapi::kdmapi_loader::instance().is_available();
}
};
}
@@ -1,23 +0,0 @@
#pragma once
#include <libremidi/config.hpp>
namespace libremidi::kdmapi
{
// KDMAPI does not support MIDI input
struct input_configuration
{
};
struct output_configuration
{
// Use the no-buffer variant of SendDirectData for lowest latency
// This bypasses OmniMIDI's internal buffer
bool use_no_buffer = false;
};
struct observer_configuration
{
};
}
@@ -1,178 +0,0 @@
#pragma once
// clang-format off
#define NOMINMAX 1
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <mmsystem.h>
// clang-format on
#include <libremidi/detail/midi_api.hpp>
#include <atomic>
#include <mutex>
namespace libremidi::kdmapi
{
using IsKDMAPIAvailable_t = BOOL(WINAPI*)();
using InitializeKDMAPIStream_t = BOOL(WINAPI*)();
using TerminateKDMAPIStream_t = BOOL(WINAPI*)();
using ResetKDMAPIStream_t = VOID(WINAPI*)();
using SendDirectData_t = VOID(WINAPI*)(DWORD);
using SendDirectDataNoBuf_t = VOID(WINAPI*)(DWORD);
using SendDirectLongData_t = UINT(WINAPI*)(MIDIHDR*, UINT);
using SendDirectLongDataNoBuf_t = UINT(WINAPI*)(LPSTR, DWORD);
using PrepareLongData_t = UINT(WINAPI*)(MIDIHDR*, UINT);
using UnprepareLongData_t = UINT(WINAPI*)(MIDIHDR*, UINT);
using ReturnKDMAPIVer_t = BOOL(WINAPI*)(LPDWORD, LPDWORD, LPDWORD, LPDWORD);
class kdmapi_loader
{
public:
static kdmapi_loader& instance()
{
static kdmapi_loader loader;
return loader;
}
bool is_available() const noexcept { return m_available; }
HMODULE handle() const noexcept { return m_handle; }
// KDMAPI function pointers
IsKDMAPIAvailable_t IsKDMAPIAvailable{};
InitializeKDMAPIStream_t InitializeKDMAPIStream{};
TerminateKDMAPIStream_t TerminateKDMAPIStream{};
ResetKDMAPIStream_t ResetKDMAPIStream{};
SendDirectData_t SendDirectData{};
SendDirectDataNoBuf_t SendDirectDataNoBuf{};
SendDirectLongData_t SendDirectLongData{};
SendDirectLongDataNoBuf_t SendDirectLongDataNoBuf{};
PrepareLongData_t PrepareLongData{};
UnprepareLongData_t UnprepareLongData{};
ReturnKDMAPIVer_t ReturnKDMAPIVer{};
private:
kdmapi_loader()
{
// Try to get OmniMIDI if it's already loaded
m_handle = GetModuleHandleW(L"OmniMIDI");
if (!m_handle)
{
// Try to load it explicitly
m_handle = LoadLibraryW(L"OmniMIDI.dll");
}
if (!m_handle)
{
m_available = false;
return;
}
#if !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
// Load all function pointers
IsKDMAPIAvailable
= reinterpret_cast<IsKDMAPIAvailable_t>(GetProcAddress(m_handle, "IsKDMAPIAvailable"));
InitializeKDMAPIStream = reinterpret_cast<InitializeKDMAPIStream_t>(
GetProcAddress(m_handle, "InitializeKDMAPIStream"));
TerminateKDMAPIStream = reinterpret_cast<TerminateKDMAPIStream_t>(
GetProcAddress(m_handle, "TerminateKDMAPIStream"));
ResetKDMAPIStream
= reinterpret_cast<ResetKDMAPIStream_t>(GetProcAddress(m_handle, "ResetKDMAPIStream"));
SendDirectData
= reinterpret_cast<SendDirectData_t>(GetProcAddress(m_handle, "SendDirectData"));
SendDirectDataNoBuf
= reinterpret_cast<SendDirectDataNoBuf_t>(GetProcAddress(m_handle, "SendDirectDataNoBuf"));
SendDirectLongData
= reinterpret_cast<SendDirectLongData_t>(GetProcAddress(m_handle, "SendDirectLongData"));
SendDirectLongDataNoBuf = reinterpret_cast<SendDirectLongDataNoBuf_t>(
GetProcAddress(m_handle, "SendDirectLongDataNoBuf"));
PrepareLongData
= reinterpret_cast<PrepareLongData_t>(GetProcAddress(m_handle, "PrepareLongData"));
UnprepareLongData
= reinterpret_cast<UnprepareLongData_t>(GetProcAddress(m_handle, "UnprepareLongData"));
ReturnKDMAPIVer
= reinterpret_cast<ReturnKDMAPIVer_t>(GetProcAddress(m_handle, "ReturnKDMAPIVer"));
#if !defined(_MSC_VER)
#pragma GCC diagnostic pop
#endif
// Check if the minimum required functions are available
if (!IsKDMAPIAvailable || !InitializeKDMAPIStream || !TerminateKDMAPIStream || !SendDirectData)
{
m_available = false;
return;
}
// Call IsKDMAPIAvailable to activate KDMAPI mode in OmniMIDI
m_available = IsKDMAPIAvailable() != FALSE;
}
~kdmapi_loader()
{
}
kdmapi_loader(const kdmapi_loader&) = delete;
kdmapi_loader& operator=(const kdmapi_loader&) = delete;
HMODULE m_handle{};
bool m_available{false};
};
class kdmapi_stream_manager
{
public:
static kdmapi_stream_manager& instance()
{
static kdmapi_stream_manager mgr;
return mgr;
}
bool acquire()
{
std::lock_guard lock{m_mutex};
auto& loader = kdmapi_loader::instance();
if (!loader.is_available())
return false;
if (m_rc == 0)
{
if (!loader.InitializeKDMAPIStream())
return false;
}
++m_rc;
return true;
}
void release()
{
std::lock_guard lock{m_mutex};
if (m_rc == 0)
return;
--m_rc;
if (m_rc == 0)
{
auto& loader = kdmapi_loader::instance();
if (loader.TerminateKDMAPIStream)
loader.TerminateKDMAPIStream();
}
}
private:
kdmapi_stream_manager() = default;
~kdmapi_stream_manager() = default;
kdmapi_stream_manager(const kdmapi_stream_manager&) = delete;
kdmapi_stream_manager& operator=(const kdmapi_stream_manager&) = delete;
std::mutex m_mutex;
std::size_t m_rc{0};
};
}
@@ -1,11 +0,0 @@
#pragma once
#include <libremidi/backends/kdmapi/config.hpp>
#include <libremidi/backends/dummy.hpp>
#include <libremidi/detail/midi_in.hpp>
namespace libremidi::kdmapi
{
using midi_in = libremidi::midi_in_dummy;
}
@@ -1,173 +0,0 @@
#pragma once
#include <libremidi/backends/kdmapi/config.hpp>
#include <libremidi/backends/kdmapi/helpers.hpp>
#include <libremidi/backends/kdmapi/observer.hpp>
#include <libremidi/detail/midi_out.hpp>
namespace libremidi::kdmapi
{
class midi_out final
: public midi1::out_api
, public error_handler
{
public:
struct
: libremidi::output_configuration
, kdmapi::output_configuration
{
} configuration;
midi_out(libremidi::output_configuration&& conf, kdmapi::output_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
auto& loader = kdmapi_loader::instance();
if (!loader.is_available())
{
libremidi_handle_error(
configuration, "KDMAPI is not available. Is OmniMIDI installed?");
this->client_open_ = std::errc::no_such_device;
return;
}
this->client_open_ = stdx::error{};
}
~midi_out() override
{
midi_out::close_port();
this->client_open_ = std::errc::not_connected;
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::KDMAPI; }
stdx::error open_port(const output_port& port, std::string_view) override
{
if (port.api != libremidi::API::KDMAPI)
{
libremidi_handle_error(configuration, "port is not a KDMAPI port");
return std::errc::invalid_argument;
}
return do_open();
}
stdx::error do_open()
{
auto& mgr = kdmapi_stream_manager::instance();
if (!mgr.acquire())
{
libremidi_handle_error(configuration, "failed to initialize KDMAPI stream");
return std::errc::io_error;
}
connected_ = true;
return stdx::error{};
}
stdx::error close_port() override
{
if (connected_)
{
auto& mgr = kdmapi_stream_manager::instance();
mgr.release();
connected_ = false;
}
return stdx::error{};
}
stdx::error send_message(const unsigned char* message, size_t size) override
{
if (!connected_)
return std::errc::not_connected;
if (size == 0)
{
libremidi_handle_warning(configuration, "message argument is empty!");
return std::errc::invalid_argument;
}
auto& loader = kdmapi_loader::instance();
if (message[0] == 0xF0)
{
// SysEx message
return send_sysex(message, size);
}
else
{
// Channel or system message
if (size > 3)
{
libremidi_handle_warning(
configuration, "message size is greater than 3 bytes (and not sysex)!");
return std::errc::message_size;
}
// Pack MIDI bytes into DWORD
DWORD packet = 0;
std::copy_n(message, size, reinterpret_cast<unsigned char*>(&packet));
// Send the message
if (configuration.use_no_buffer && loader.SendDirectDataNoBuf)
loader.SendDirectDataNoBuf(packet);
else
loader.SendDirectData(packet);
}
return stdx::error{};
}
private:
stdx::error send_sysex(const unsigned char* message, size_t size)
{
auto& loader = kdmapi_loader::instance();
if (configuration.use_no_buffer && loader.SendDirectLongDataNoBuf)
{
// Use the no-buffer variant - it takes raw data directly
loader.SendDirectLongDataNoBuf(
const_cast<LPSTR>(reinterpret_cast<const char*>(message)), static_cast<DWORD>(size));
return stdx::error{};
}
// Standard path with MIDIHDR
buffer_.assign(message, message + size);
MIDIHDR sysex{};
sysex.lpData = reinterpret_cast<LPSTR>(buffer_.data());
sysex.dwBufferLength = static_cast<DWORD>(size);
sysex.dwFlags = 0;
if (loader.PrepareLongData)
{
UINT result = loader.PrepareLongData(&sysex, sizeof(MIDIHDR));
if (result != MMSYSERR_NOERROR)
{
libremidi_handle_error(configuration, "error preparing sysex header");
return std::errc::io_error;
}
}
if (loader.SendDirectLongData)
{
UINT result = loader.SendDirectLongData(&sysex, sizeof(MIDIHDR));
if (result != MMSYSERR_NOERROR)
{
libremidi_handle_error(configuration, "error sending sysex message");
// Still try to unprepare
}
}
if (loader.UnprepareLongData)
{
loader.UnprepareLongData(&sysex, sizeof(MIDIHDR));
}
return stdx::error{};
}
std::vector<char> buffer_;
};
}
@@ -1,86 +0,0 @@
#pragma once
#include <libremidi/backends/kdmapi/config.hpp>
#include <libremidi/backends/kdmapi/helpers.hpp>
#include <libremidi/detail/observer.hpp>
namespace libremidi::kdmapi
{
class observer final : public observer_api
{
public:
struct
: libremidi::observer_configuration
, kdmapi::observer_configuration
{
} configuration;
explicit observer(
libremidi::observer_configuration&& conf, kdmapi::observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
if (!configuration.has_callbacks())
return;
auto& loader = kdmapi_loader::instance();
if (!loader.is_available())
return;
// KDMAPI provides a single virtual output port
if (configuration.notify_in_constructor)
{
if (configuration.output_added)
{
auto ports = get_output_ports();
for (const auto& port : ports)
configuration.output_added(port);
}
}
}
~observer() = default;
libremidi::API get_current_api() const noexcept override { return libremidi::API::KDMAPI; }
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
// KDMAPI does not provide input ports
return {};
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
auto& loader = kdmapi_loader::instance();
if (!loader.is_available())
return {};
// Get KDMAPI version for display name
std::string display_name = "OmniMIDI";
if (loader.ReturnKDMAPIVer)
{
DWORD major{}, minor{}, build{}, rev{};
if (loader.ReturnKDMAPIVer(&major, &minor, &build, &rev))
{
display_name += " (KDMAPI ";
display_name += std::to_string(major);
display_name += ".";
display_name += std::to_string(minor);
display_name += ".";
display_name += std::to_string(build);
display_name += ")";
}
}
return {libremidi::output_port{
{.api = libremidi::API::KDMAPI,
.client = 0,
.port = 0,
.manufacturer = "KeppySoftware",
.device_name = "OmniMIDI",
.port_name = "OmniMIDI",
.display_name = std::move(display_name)},
}};
}
};
}
+1 -1
View File
@@ -4,7 +4,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct kbd_backend
{
@@ -4,7 +4,7 @@
#include <functional>
#include <map>
NAMESPACE_LIBREMIDI
namespace libremidi
{
enum kbd_event
{
@@ -36,7 +36,7 @@ enum kbd_event
// actually based on macOS virtual codes as it seems
// impossible to get raw scan codes.
// https://eastmanreference.com/complete-list-of-applescript-key-codes
LIBREMIDI_STATIC const std::map<int, int>& scancode_map_macos(){
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},
@@ -83,7 +83,7 @@ LIBREMIDI_STATIC const std::map<int, int>& scancode_map_macos(){
// Use: https://kbdlayout.info/KBDUSX/scancodes
// Also valid for windows
LIBREMIDI_STATIC const std::map<int, int>& scancode_map_linux() {
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},
@@ -5,7 +5,7 @@
#include <chrono>
#include <unordered_map>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_in_kbd final
: public midi1::in_api
+19 -97
View File
@@ -13,13 +13,9 @@
#if __has_include(<alsa/ump.h>) && SND_LIB_VERSION >= ((1 << 16) | (2 << 8) | 10)
#define LIBREMIDI_ALSA_HAS_UMP 1
#endif
#if __has_include(<alsa/ump.h>) && SND_LIB_VERSION >= ((1 << 16) | (2 << 8) | 14)
#define LIBREMIDI_ALSA_HAS_UMP_SEQ_EVENTS 1
#endif
#endif
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct libasound
@@ -282,7 +278,6 @@ struct libasound
LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_free)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_malloc)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_dest)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_sender)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_time_real)
@@ -297,12 +292,6 @@ struct libasound
LIBREMIDI_SYMBOL_INIT(snd_seq, set_queue_tempo)
LIBREMIDI_SYMBOL_INIT(snd_seq, subscribe_port)
LIBREMIDI_SYMBOL_INIT(snd_seq, unsubscribe_port)
#if LIBREMIDI_ALSA_HAS_UMP
LIBREMIDI_SYMBOL_INIT(snd_seq, set_client_midi_version)
LIBREMIDI_SYMBOL_INIT(snd_seq, get_ump_endpoint_info)
LIBREMIDI_SYMBOL_INIT(snd_seq, get_ump_block_info)
#endif
}
bool available{true};
@@ -347,7 +336,6 @@ struct libasound
LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_free)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_malloc)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_dest)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_sender)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_time_real)
@@ -364,12 +352,6 @@ struct libasound
LIBREMIDI_SYMBOL_DEF(snd_seq, subscribe_port)
LIBREMIDI_SYMBOL_DEF(snd_seq, unsubscribe_port)
#if LIBREMIDI_ALSA_HAS_UMP
LIBREMIDI_SYMBOL_DEF(snd_seq, set_client_midi_version)
LIBREMIDI_SYMBOL_DEF(snd_seq, get_ump_endpoint_info)
LIBREMIDI_SYMBOL_DEF(snd_seq, get_ump_block_info)
#endif
#if LIBREMIDI_ALSA_HAS_UMP
struct ump_t
{
@@ -380,6 +362,7 @@ struct libasound
available = false;
return;
}
LIBREMIDI_SYMBOL_INIT(snd_seq, set_client_midi_version)
LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_input)
LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output)
LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output_direct)
@@ -387,10 +370,10 @@ struct libasound
bool available{true};
LIBREMIDI_SYMBOL_DEF(snd_seq, set_client_midi_version)
LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_input)
LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output)
LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output_direct)
} ump;
#endif
} seq{library};
@@ -406,98 +389,39 @@ struct libasound
return;
}
// Core UMP functions
LIBREMIDI_SYMBOL_INIT(snd_ump, open)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_name)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_ump, close)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_name)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_ump, open)
LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors)
LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors_count)
LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors_revents)
LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi)
LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params)
LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params_current)
LIBREMIDI_SYMBOL_INIT(snd_ump, read)
LIBREMIDI_SYMBOL_INIT(snd_ump, tread)
LIBREMIDI_SYMBOL_INIT(snd_ump, write)
LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors)
LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors_count)
LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors_revents)
// Endpoint info functions
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_card)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_device)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_flags)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_protocol_caps)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_protocol)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_num_blocks)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_version)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_manufacturer_id)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_family_id)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_model_id)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_sw_revision)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_name)
LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_product_id)
// Block info functions
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_set_block_id)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_block_id)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_active)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_flags)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_direction)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_first_group)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_num_groups)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_midi_ci_version)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_sysex8_streams)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_ui_hint)
LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_name)
}
bool available{true};
// Core UMP functions
LIBREMIDI_SYMBOL_DEF(snd_ump, open)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_name)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_ump, close)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_name)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_ump, open)
LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors)
LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors_count)
LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors_revents)
LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi)
LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params)
LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params_current)
LIBREMIDI_SYMBOL_DEF(snd_ump, read)
LIBREMIDI_SYMBOL_DEF(snd_ump, tread)
LIBREMIDI_SYMBOL_DEF(snd_ump, write)
LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors)
LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors_count)
LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors_revents)
// Endpoint info functions
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_card)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_device)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_flags)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_protocol_caps)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_protocol)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_num_blocks)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_version)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_manufacturer_id)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_family_id)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_model_id)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_sw_revision)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_name)
LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_product_id)
// Block info functions
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_set_block_id)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_block_id)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_active)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_flags)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_direction)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_first_group)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_num_groups)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_midi_ci_version)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_sysex8_streams)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_ui_hint)
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_name)
} ump{library};
#endif
};
@@ -525,8 +449,6 @@ struct libasound
#define snd_seq_client_info_alloca(ptr) snd_dylib_alloca(ptr, seq, client_info)
#undef snd_seq_port_info_alloca
#define snd_seq_port_info_alloca(ptr) snd_dylib_alloca(ptr, seq, port_info)
#undef snd_seq_port_subscribe_alloca
#define snd_seq_port_subscribe_alloca(ptr) snd_dylib_alloca(ptr, seq, port_subscribe)
#undef snd_seq_queue_tempo_alloca
#define snd_seq_queue_tempo_alloca(ptr) snd_dylib_alloca(ptr, seq, queue_tempo)
@@ -1,11 +1,10 @@
#pragma once
#include <libremidi/config.hpp>
#if __has_include(<dlfcn.h>)
#include <dlfcn.h>
#include <cassert>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class dylib_loader
{
+1 -1
View File
@@ -7,7 +7,7 @@
#include <iostream>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct eventfd_notifier
{
@@ -4,7 +4,7 @@
#include <pipewire/pipewire.h>
NAMESPACE_LIBREMIDI
namespace libremidi
{
#pragma GCC diagnostic push
+7 -42
View File
@@ -8,7 +8,7 @@
#include <cassert>
#include <optional>
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct libudev
@@ -149,10 +149,7 @@ struct udev_soundcard_info
{
container_identifier container;
device_identifier path;
transport_type type{};
std::string vendor;
std::string product;
std::string serial;
port_information::port_type type{};
};
inline udev_soundcard_info get_udev_soundcard_info(const udev_helper& helper, int card)
@@ -177,50 +174,18 @@ inline udev_soundcard_info get_udev_soundcard_info(const udev_helper& helper, in
const auto usb_device
= udev.device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
const auto pci_device = udev.device_get_parent_with_subsystem_devtype(dev, "pci", nullptr);
transport_type type = transport_type::hardware;
port_information::port_type type = port_information::port_type::hardware;
if (usb_device)
type = (transport_type)(type | transport_type::usb);
else if (pci_device)
type = (transport_type)(type | transport_type::pci);
type = (port_information::port_type)(type | port_information::port_type::usb);
else
type = (port_information::port_type)(type | port_information::port_type::pci);
auto id_path = udev.device_get_property_value(dev, "ID_PATH");
if (!id_path)
id_path = "";
auto read_property = [&](const char* property) {
auto res = udev.device_get_property_value(dev, property);
if (!res)
res = "";
if (strlen(res) == 0)
{
if (usb_device)
{
res = udev.device_get_property_value(usb_device, property);
if (!res)
res = "";
}
else if (pci_device)
{
res = udev.device_get_property_value(pci_device, property);
if (!res)
res = "";
}
}
return res;
};
auto id_vendor = read_property("ID_VENDOR_FROM_DATABASE");
auto id_product = read_property("ID_MODEL_FROM_DATABASE");
auto id_serial = read_property("ID_SERIAL");
auto ret = udev_soundcard_info{
.container = id_path,
.path = path,
.type = type,
.vendor = id_vendor,
.product = id_product,
.serial = id_serial};
auto ret = udev_soundcard_info{.container = id_path, .path = path, .type = type};
udev.device_unref(dev);
return ret;
}
+2 -5
View File
@@ -2,15 +2,12 @@
#include <libremidi/config.hpp>
#include <string>
#if !defined(BOOST_ASIO_IO_CONTEXT_HPP)
namespace boost::asio
{
struct io_context;
}
#endif
NAMESPACE_LIBREMIDI::net
namespace libremidi::net
{
enum class protocol
{
@@ -49,7 +46,7 @@ struct dgram_observer_configuration
}
NAMESPACE_LIBREMIDI::net_ump
namespace libremidi::net_ump
{
enum class protocol
{
+2 -2
View File
@@ -23,7 +23,7 @@ public:
string_ref message(const error& e) const noexcept override {
auto err = e.m_value;
}
[[noreturn]] void throw_exception(const error& e) const override
@@ -35,7 +35,7 @@ public:
*/
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <typename T>
struct optionally_owned
+3 -3
View File
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_stream_decoder.hpp>
#include <thread>
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <typename Impl, typename F>
struct osc_parser
@@ -115,7 +115,7 @@ struct osc_parser
}
NAMESPACE_LIBREMIDI::net
namespace libremidi::net
{
struct osc_parser_midi1
@@ -264,7 +264,7 @@ public:
};
}
NAMESPACE_LIBREMIDI::net_ump
namespace libremidi::net_ump
{
struct osc_parser_midi2
{
+2 -2
View File
@@ -8,7 +8,7 @@
#include <boost/asio/ip/udp.hpp>
#include <boost/endian.hpp>
NAMESPACE_LIBREMIDI::net
namespace libremidi::net
{
struct osc_midi1_packet
@@ -146,7 +146,7 @@ public:
}
NAMESPACE_LIBREMIDI::net_ump
namespace libremidi::net_ump
{
struct osc_midi2_packet
{
+2 -2
View File
@@ -1,13 +1,13 @@
#pragma once
#include <libremidi/backends/dummy.hpp>
NAMESPACE_LIBREMIDI::net
namespace libremidi::net
{
using net_observer_configuration = libremidi::dummy_configuration;
using observer = libremidi::observer_dummy;
}
NAMESPACE_LIBREMIDI::net_ump
namespace libremidi::net_ump
{
using net_observer_configuration = libremidi::dummy_configuration;
using observer = libremidi::observer_dummy;
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI::net
namespace libremidi::net
{
struct backend
{
+1 -1
View File
@@ -6,7 +6,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI::net_ump
namespace libremidi::net_ump
{
struct backend
{
+1 -1
View File
@@ -9,7 +9,7 @@
#include <string_view>
NAMESPACE_LIBREMIDI::pipewire
namespace libremidi::pipewire
{
struct backend
{
@@ -12,7 +12,7 @@ struct pw_filter;
struct spa_io_position;
}
NAMESPACE_LIBREMIDI
namespace libremidi
{
using pipewire_callback_function = std::function<void(spa_io_position*)>;
struct pipewire_callback
@@ -25,7 +25,7 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
NAMESPACE_LIBREMIDI
namespace libremidi
{
template <typename K, typename V>
using hash_map = std::unordered_map<K, V>;
@@ -188,13 +188,13 @@ struct pipewire_context
pipewire_context& self = *(pipewire_context*)object;
if (strcmp(type, PW_TYPE_INTERFACE_Port) == 0)
self.register_port(id, type);
},
},
.global_remove =
[](void* object, uint32_t id) {
pipewire_context& self = *(pipewire_context*)object;
self.unregister_port(id);
},
};
},
};
// Start listening
pw_registry_add_listener(this->registry, &this->registry_listener, &registry_events, this);
@@ -245,8 +245,8 @@ struct pipewire_context
// Remove from the listeners
auto it
= std::find_if(port_listener.begin(), port_listener.end(), [&](const listened_port& l) {
return l.id == id;
});
return l.id == id;
});
if (it != port_listener.end())
{
pw.proxy_destroy((pw_proxy*)it->port);
@@ -267,14 +267,14 @@ struct pipewire_context
static constexpr struct pw_core_events core_events = {
.version = PW_VERSION_CORE_EVENTS,
.done =
[](void* object, uint32_t id, int seq) {
[](void* object, uint32_t id, int seq) {
auto& self = *(pipewire_context*)object;
if (id == PW_ID_CORE && seq == self.pending)
if(id == PW_ID_CORE && seq == self.pending)
{
self.done = 1;
libpipewire::instance().main_loop_quit(self.main_loop);
}
},
},
};
spa_zero(core_listener);
+13 -24
View File
@@ -11,12 +11,7 @@
#include <atomic>
#include <thread>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#endif
NAMESPACE_LIBREMIDI
namespace libremidi
{
struct pipewire_helpers
{
@@ -87,10 +82,10 @@ struct pipewire_helpers
static constexpr struct pw_filter_events filter_events
= {.version = PW_VERSION_FILTER_EVENTS,
.process = +[](void* _data, struct spa_io_position* position) -> void {
// FIXME likely we need the thread_lock check here too
Self& self = *static_cast<Self*>(_data);
self.process(position);
}};
// FIXME likely we need the thread_lock check here too
Self& self = *static_cast<Self*>(_data);
self.process(position);
}};
#pragma GCC diagnostic pop
this->filter->create_filter(self.configuration.client_name, filter_events, &self);
@@ -222,7 +217,6 @@ struct pipewire_helpers
return stdx::error{};
}
template <libremidi::API Api>
void add_callbacks(std::string format, const observer_configuration& conf)
{
assert(global_context);
@@ -238,12 +232,12 @@ struct pipewire_helpers
if (port.direction == SPA_DIRECTION_INPUT)
{
if (conf.output_added)
conf.output_added(to_port_info<SPA_DIRECTION_INPUT, Api>(port));
conf.output_added(to_port_info<SPA_DIRECTION_INPUT>(port));
}
else
{
if (conf.input_added)
conf.input_added(to_port_info<SPA_DIRECTION_OUTPUT, Api>(port));
conf.input_added(to_port_info<SPA_DIRECTION_OUTPUT>(port));
}
}
};
@@ -260,12 +254,12 @@ struct pipewire_helpers
if (port.direction == SPA_DIRECTION_INPUT)
{
if (conf.output_removed)
conf.output_removed(to_port_info<SPA_DIRECTION_INPUT, Api>(port));
conf.output_removed(to_port_info<SPA_DIRECTION_INPUT>(port));
}
else
{
if (conf.input_removed)
conf.input_removed(to_port_info<SPA_DIRECTION_OUTPUT, Api>(port));
conf.input_removed(to_port_info<SPA_DIRECTION_OUTPUT>(port));
}
}
};
@@ -413,7 +407,7 @@ struct pipewire_helpers
return stdx::error{};
}
template <spa_direction Direction, libremidi::API Api>
template <spa_direction Direction>
static auto to_port_info(const pipewire_context::port_info& port)
-> std::conditional_t<Direction == SPA_DIRECTION_OUTPUT, input_port, output_port>
{
@@ -430,7 +424,6 @@ struct pipewire_helpers
}
return {{
.api = Api,
.client = 0,
.port = port.id,
.manufacturer = "",
@@ -442,7 +435,7 @@ struct pipewire_helpers
// Note: keep in mind that an "input" port for us (e.g. a keyboard that goes to the computer)
// is an "output" port from the point of view of pipewire as data will come out of it
template <spa_direction Direction, libremidi::API Api>
template <spa_direction Direction>
static auto get_ports(
std::string_view format, const observer_configuration& conf,
const pipewire_context& ctx) noexcept
@@ -461,7 +454,7 @@ struct pipewire_helpers
(Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
{
if (port.format.find(format) != std::string::npos)
ret.push_back(to_port_info<Direction, Api>(port));
ret.push_back(to_port_info<Direction>(port));
}
}
@@ -472,7 +465,7 @@ struct pipewire_helpers
(Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
{
if (port.format.find(format) != std::string::npos)
ret.push_back(to_port_info<Direction, Api>(port));
ret.push_back(to_port_info<Direction>(port));
}
}
}
@@ -481,7 +474,3 @@ struct pipewire_helpers
}
};
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_in_pipewire final
: public midi1::in_api
@@ -74,7 +74,10 @@ public:
return do_close_port();
}
stdx::error set_port_name(std::string_view port_name) override { return rename_port(port_name); }
stdx::error set_port_name(std::string_view port_name) override
{
return rename_port(port_name);
}
timestamp absolute_timestamp() const noexcept override { return system_ns(); }
@@ -5,7 +5,7 @@
#include <readerwriterqueue.h>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class midi_out_pipewire
: public midi1::out_api
@@ -79,7 +79,10 @@ public:
return do_close_port();
}
stdx::error set_port_name(std::string_view port_name) override { return rename_port(port_name); }
stdx::error set_port_name(std::string_view port_name) override
{
return rename_port(port_name);
}
int process(spa_io_position* pos)
{
@@ -5,7 +5,7 @@
#include <unordered_set>
NAMESPACE_LIBREMIDI
namespace libremidi
{
class observer_pipewire final
: public observer_api
@@ -19,15 +19,14 @@ public:
{
} configuration;
explicit observer_pipewire(
observer_configuration&& conf, pipewire_observer_configuration&& apiconf)
explicit observer_pipewire(observer_configuration&& conf, pipewire_observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
create_context(*this);
// FIXME port rename callback
{
this->add_callbacks<libremidi::API::PIPEWIRE>("midi", configuration);
this->add_callbacks("midi", configuration);
this->start_thread();
}
@@ -47,14 +46,12 @@ public:
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
return get_ports<SPA_DIRECTION_OUTPUT, libremidi::API::PIPEWIRE>(
"midi", this->configuration, *this->global_context);
return get_ports<SPA_DIRECTION_OUTPUT>("midi", this->configuration, *this->global_context);
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
return get_ports<SPA_DIRECTION_INPUT, libremidi::API::PIPEWIRE>(
"midi", this->configuration, *this->global_context);
return get_ports<SPA_DIRECTION_INPUT>("midi", this->configuration, *this->global_context);
}
~observer_pipewire()
@@ -8,7 +8,7 @@
#include <variant>
NAMESPACE_LIBREMIDI::pipewire
namespace libremidi::pipewire
{
// Create a PipeWire client which will be shared across objects
@@ -23,10 +23,13 @@ struct shared_handler : public libremidi::shared_context
client = pipewire_client_open(v.data(), PipewireNoStartServer, &status);
assert(client);
assert(status == 0);
pipewire_set_process_callback(client, +[](pipewire_nframes_t cnt, void* ctx) -> int {
((shared_handler*)ctx)->pipewire_callback(cnt);
return 0;
}, this);
pipewire_set_process_callback(
client,
+[](pipewire_nframes_t cnt, void* ctx) -> int {
((shared_handler*)ctx)->pipewire_callback(cnt);
return 0;
},
this);
}
virtual void start_processing() override { pipewire_activate(client); }
@@ -71,10 +74,10 @@ struct shared_handler : public libremidi::shared_context
{
case in_callback_added:
midiin_callbacks.push_back(
std::move(*get_if<libremidi::pipewire_callback>(&ev.payload)));
std::move(*std::get_if<libremidi::pipewire_callback>(&ev.payload)));
break;
case in_callback_removed: {
auto idx = *get_if<int64_t>(&ev.payload);
auto idx = *std::get_if<int64_t>(&ev.payload);
for (auto it = midiin_callbacks.begin(); it != midiin_callbacks.end();)
{
if (it->token == idx)
@@ -91,10 +94,10 @@ struct shared_handler : public libremidi::shared_context
}
case out_callback_added:
midiout_callbacks.push_back(
std::move(*get_if<libremidi::pipewire_callback>(&ev.payload)));
std::move(*std::get_if<libremidi::pipewire_callback>(&ev.payload)));
break;
case out_callback_removed:
auto idx = *get_if<int64_t>(&ev.payload);
auto idx = *std::get_if<int64_t>(&ev.payload);
for (auto it = midiout_callbacks.begin(); it != midiout_callbacks.end();)
{
if (it->token == idx)
@@ -138,7 +141,7 @@ struct shared_handler : public libremidi::shared_context
struct event
{
event_type type;
libremidi_variant_alias::variant<libremidi::pipewire_callback, int64_t> payload;
std::variant<libremidi::pipewire_callback, int64_t> payload;
};
boost::lockfree::spsc_queue<event> events{16};

Some files were not shown because too many files have changed in this diff Show More