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
239 changed files with 3096 additions and 9312 deletions
-3
View File
@@ -1,3 +0,0 @@
CompileFlags:
Add: [-I/home/sebastian/code/viseq/include, -std=c++23]
+23 -16
View File
@@ -1,25 +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++ LDLIBS = -lraylib -lasound -pthread
CXXFLAGS := -std=c++23 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -Wnarrowing -DLIBREMIDI_ALSA=1 -DLIBREMIDI_HEADER_ONLY=1 LDFLAGS = -Llib -L/usr/lib -L/usr/local/lib
CXXINC := -Iinclude -isysteminclude
LDLIBS := -lraylib -lasound -llibremidi -pthread hdr_files = ${wildcard src/*.h}
LDFLAGS := -Llib -L/usr/lib -L/usr/local/lib
SRCS := $(wildcard src/*.cpp) src_files = ${wildcard src/*.cpp}
OBJECTS := $(patsubst src/%.cpp,build/%.o, $(SRCS)) src_files += ${wildcard include/libremidi/*.cpp}
DEPENDS := $(patsubst src/%.cpp,build/%.d, $(SRCS))
viseq: $(OBJECTS) all: viseq
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS) $(LDLIBS)
-include $(DEPENDS) viseq: $(src_files) $(hdr_files)
mkdir -p build
$(CXX) ${wildcard build/*.o} -o viseq $(LDFLAGS) $(LDLIBS)
#$(OBJECTS): build/%.o : src/%.cpp $(src_files): %.cpp : %_src
build/%.o: src/%.cpp Makefile $(CXX) -c $@ -o build/$(*F).o $(CXXFLAGS) $(CXXINC)
@mkdir -p build
$(CXX) -c $< -o $@ $(CXXFLAGS) $(CXXINC) -MMD -MP $(hdr_files): %.h : %_hdr
@echo "G++ >---------- $(*F).h -----------<"
%_src:
@echo "G++ >---------- $(*F).cpp -----------<"
%_hdr:
@echo "G++ >---------- $(*F).h -----------<"
clean: clean:
rm -f build/* 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 #pragma once
#ifndef LIBREMIDI_API_C_H
#define LIBREMIDI_API_C_H
#if __cplusplus && !defined(LIBREMIDI_MODULE_BUILD) #if __cplusplus
extern "C" { extern "C" {
#endif #endif
//! MIDI API specifier arguments. //! MIDI API specifier arguments.
//! To get information on which feature is supported by each back-end, check their backend file //! To get information on which feature is supported by each back-end, check their backend file
//! in e.g. backends/winmm.hpp, etc. //! in e.g. backends/winmm.hpp, etc.
enum libremidi_api typedef enum libremidi_api
{ {
UNSPECIFIED = 0x0, /*!< Search for a working compiled API. */ UNSPECIFIED = 0x0, /*!< Search for a working compiled API. */
@@ -24,8 +22,6 @@ enum libremidi_api
PIPEWIRE, /*!< PipeWire */ PIPEWIRE, /*!< PipeWire */
KEYBOARD, /*!< Computer keyboard input */ KEYBOARD, /*!< Computer keyboard input */
NETWORK, /*!< MIDI over IP */ NETWORK, /*!< MIDI over IP */
ANDROID_AMIDI, /*!< Android AMidi API */
KDMAPI, /*!< OmniMIDI KDMAPI (Windows) */
// MIDI 2.0 APIs // MIDI 2.0 APIs
ALSA_RAW_UMP = 0x1000, /*!< Raw ALSA API for MIDI 2.0 */ 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+. */ PIPEWIRE_UMP, /*!< MIDI2 over PipeWire. Requires v1.4+. */
DUMMY = 0xFFFF /*!< A compilable but non-functional API. */ DUMMY = 0xFFFF /*!< A compilable but non-functional API. */
}; } libremidi_api;
typedef enum libremidi_api libremidi_api; #if __cplusplus
#if __cplusplus && !defined(LIBREMIDI_MODULE_BUILD)
} }
#endif #endif
#endif
+1 -3
View File
@@ -5,7 +5,7 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
//! MIDI API specifier arguments. //! MIDI API specifier arguments.
//! To get information on which feature is supported by each back-end, check their backend file //! 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; return API::ALSA_SEQ;
#elif defined(__emscripten__) #elif defined(__emscripten__)
return API::EMSCRIPTEN_WEBMIDI; return API::EMSCRIPTEN_WEBMIDI;
#elif defined(LIBREMIDI_ANDROID)
return API::ANDROID_AMIDI;
#else #else
return API::DUMMY; return API::DUMMY;
#endif #endif
+7 -25
View File
@@ -29,9 +29,7 @@
#if defined(LIBREMIDI_JACK) #if defined(LIBREMIDI_JACK)
#include <libremidi/backends/jack.hpp> #include <libremidi/backends/jack.hpp>
#if defined(LIBREMIDI_HAS_JACK_GET_VERSION)
#include <libremidi/backends/jack_ump.hpp> #include <libremidi/backends/jack_ump.hpp>
#endif
#endif #endif
#if defined(LIBREMIDI_PIPEWIRE) #if defined(LIBREMIDI_PIPEWIRE)
@@ -50,10 +48,6 @@
#include <libremidi/backends/winmm.hpp> #include <libremidi/backends/winmm.hpp>
#endif #endif
#if defined(LIBREMIDI_KDMAPI)
#include <libremidi/backends/kdmapi.hpp>
#endif
#if defined(LIBREMIDI_WINUWP) #if defined(LIBREMIDI_WINUWP)
#include <libremidi/backends/winuwp.hpp> #include <libremidi/backends/winuwp.hpp>
#endif #endif
@@ -77,11 +71,7 @@
#include <libremidi/backends/network_ump.hpp> #include <libremidi/backends/network_ump.hpp>
#endif #endif
#if defined(LIBREMIDI_ANDROID) namespace libremidi
#include <libremidi/backends/android/android.hpp>
#endif
NAMESPACE_LIBREMIDI
{ {
// The order here will control the order of the API search in // The order here will control the order of the API search in
// the constructor. // the constructor.
@@ -93,7 +83,7 @@ constexpr auto make_tl(unused, Args...)
namespace midi1 namespace midi1
{ {
LIBREMIDI_STATIC constexpr auto available_backends = make_tl( static constexpr auto available_backends = make_tl(
0 0
#if defined(LIBREMIDI_ALSA) #if defined(LIBREMIDI_ALSA)
, ,
@@ -111,10 +101,6 @@ LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
, ,
winmm_backend{} winmm_backend{}
#endif #endif
#if defined(LIBREMIDI_KDMAPI)
,
kdmapi_backend{}
#endif
#if defined(LIBREMIDI_WINUWP) #if defined(LIBREMIDI_WINUWP)
, ,
winuwp_backend{} winuwp_backend{}
@@ -138,10 +124,6 @@ LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
#if defined(LIBREMIDI_NETWORK) #if defined(LIBREMIDI_NETWORK)
, ,
net::backend{} net::backend{}
#endif
#if defined(LIBREMIDI_ANDROID)
,
android::backend{}
#endif #endif
, ,
dummy_backend{}); dummy_backend{});
@@ -167,7 +149,7 @@ auto for_backend(libremidi::API api, F&& f)
namespace midi2 namespace midi2
{ {
LIBREMIDI_STATIC constexpr auto available_backends = make_tl( static constexpr auto available_backends = make_tl(
0 0
#if defined(LIBREMIDI_ALSA) && LIBREMIDI_ALSA_HAS_UMP #if defined(LIBREMIDI_ALSA) && LIBREMIDI_ALSA_HAS_UMP
, ,
@@ -185,7 +167,7 @@ LIBREMIDI_STATIC constexpr auto available_backends = make_tl(
, ,
net_ump::backend{} net_ump::backend{}
#endif #endif
#if defined(LIBREMIDI_JACK) && defined(LIBREMIDI_HAS_JACK_GET_VERSION) #if defined(LIBREMIDI_JACK)
, ,
jack_ump::backend{} jack_ump::backend{}
#endif #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) void for_input_configuration(auto f, libremidi::input_api_configuration& api_conf)
{ {
auto from_api = [&]<typename T>(T& /*backend*/) mutable { 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); f(*conf);
return true; 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) void for_output_configuration(auto f, libremidi::output_api_configuration& api_conf)
{ {
auto from_api = [&]<typename T>(T& /*backend*/) mutable { 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); f(*conf);
return true; 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) void for_observer_configuration(auto f, libremidi::observer_api_configuration& api_conf)
{ {
auto from_api = [&]<typename T>(T& /*backend*/) mutable { 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); f(*conf);
return true; return true;
+1 -1
View File
@@ -9,7 +9,7 @@
// https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c // https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c
// Thanks Craig Stuart Sapp <craig@ccrma.stanford.edu> // Thanks Craig Stuart Sapp <craig@ccrma.stanford.edu>
NAMESPACE_LIBREMIDI::alsa_raw namespace libremidi::alsa_raw
{ {
struct backend struct backend
{ {
@@ -10,12 +10,12 @@
#if __has_include(<poll.h>) #if __has_include(<poll.h>)
#include <poll.h> #include <poll.h>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
using poll_descriptors = pollfd; using poll_descriptors = pollfd;
} }
#else #else
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct poll_descriptors struct poll_descriptors
{ {
@@ -26,7 +26,7 @@ struct poll_descriptors
} }
#endif #endif
NAMESPACE_LIBREMIDI namespace libremidi
{ {
/** /**
@@ -20,7 +20,9 @@
// https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c // https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c
// Thanks Craig Stuart Sapp <craig@ccrma.stanford.edu> // Thanks Craig Stuart Sapp <craig@ccrma.stanford.edu>
NAMESPACE_LIBREMIDI namespace libremidi
{
namespace
{ {
struct alsa_raw_port_id 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({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})).dev == 7);
static_assert(raw_from_port_handle(raw_to_port_handle({12, 7, 3})).port == 3); static_assert(raw_from_port_handle(raw_to_port_handle({12, 7, 3})).port == 3);
}
namespace alsa_raw namespace alsa_raw
{ {
struct alsa_raw_port_info struct alsa_raw_port_info
{ {
// hw:1,2,0
std::string device; std::string device;
std::string card_name; std::string card_name;
std::string device_name; std::string device_name;
@@ -96,6 +98,7 @@ struct enumerator
: handler{self} : handler{self}
, configuration{self.configuration} , configuration{self.configuration}
{ {
} }
// 1: is an input / output // 1: is an input / output
@@ -118,9 +121,8 @@ struct enumerator
else if (status < 0 && status != -ENXIO) else if (status < 0 && status != -ENXIO)
{ {
handler.libremidi_handle_error( handler.libremidi_handle_error(
configuration, configuration, "Cannot get rawmidi information: " + device_identifier(card, device, sub)
"Cannot get rawmidi information: " + device_identifier(card, device, sub) + " : " + " : " + snd.strerror(status));
+ snd.strerror(status));
return status; return status;
} }
else else
@@ -206,8 +208,7 @@ inline snd_ctl_wrapper::snd_ctl_wrapper(enumerator& self, const char* name)
if (status < 0) if (status < 0)
{ {
self.handler.libremidi_handle_error( self.handler.libremidi_handle_error(
self.configuration, self.configuration, "cannot open control for card"s + name + " : " + snd.strerror(status));
"cannot open control for card"s + name + " : " + snd.strerror(status));
} }
} }
@@ -10,7 +10,7 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
NAMESPACE_LIBREMIDI::alsa_raw namespace libremidi::alsa_raw
{ {
class midi_in_impl class midi_in_impl
: public midi1::in_api : public midi1::in_api
@@ -264,7 +264,8 @@ private:
using namespace std::literals; using namespace std::literals;
libremidi_handle_error( 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 e.code();
} }
return stdx::error{}; return stdx::error{};
@@ -315,8 +316,7 @@ private:
{ {
if (configuration.timestamps == timestamp_mode::NoTimestamp) if (configuration.timestamps == timestamp_mode::NoTimestamp)
{ {
configuration.manual_poll( configuration.manual_poll(manual_poll_parameters{
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()}, .fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) { .callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer, fds); return do_read_events(&midi_in_impl::read_input_buffer, fds);
@@ -324,8 +324,7 @@ private:
} }
else else
{ {
configuration.manual_poll( configuration.manual_poll(manual_poll_parameters{
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()}, .fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) { .callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds); return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
@@ -343,7 +342,7 @@ private:
}; };
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <> template <>
inline std::unique_ptr<midi_in_api> make<alsa_raw::midi_in_impl>( inline std::unique_ptr<midi_in_api> make<alsa_raw::midi_in_impl>(
@@ -8,7 +8,7 @@
#include <atomic> #include <atomic>
#include <thread> #include <thread>
NAMESPACE_LIBREMIDI::alsa_raw namespace libremidi::alsa_raw
{ {
class midi_out_impl final class midi_out_impl final
: public midi1::out_api : public midi1::out_api
@@ -44,7 +44,8 @@ public:
int status = snd.rawmidi.open(NULL, &midiport_, portname, mode); int status = snd.rawmidi.open(NULL, &midiport_, portname, mode);
if (status < 0) if (status < 0)
{ {
libremidi_handle_error(this->configuration, "cannot open device."); libremidi_handle_error(
this->configuration, "cannot open device.");
return from_errc(status); return from_errc(status);
} }
return stdx::error{}; return stdx::error{};
@@ -86,7 +87,8 @@ public:
{ {
if (auto err = snd.rawmidi.write(midiport_, message, size); err < 0) 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); return from_errc(err);
} }
@@ -9,7 +9,7 @@
#endif #endif
#include <libremidi/detail/observer.hpp> #include <libremidi/detail/observer.hpp>
NAMESPACE_LIBREMIDI::alsa_raw namespace libremidi::alsa_raw
{ {
template <typename Enumerator> template <typename Enumerator>
class observer_impl_base class observer_impl_base
@@ -28,21 +28,6 @@ public:
explicit observer_impl_base( explicit observer_impl_base(
observer_configuration&& conf, alsa_raw_observer_configuration&& apiconf) observer_configuration&& conf, alsa_raw_observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(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()) if (!configuration.has_callbacks())
return; return;
@@ -63,6 +48,16 @@ public:
#endif #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> get_input_ports() const noexcept override
{ {
std::vector<libremidi::input_port> ret; std::vector<libremidi::input_port> ret;
@@ -89,6 +84,7 @@ public:
return ret; return ret;
} }
private:
#if LIBREMIDI_HAS_UDEV #if LIBREMIDI_HAS_UDEV
void run() void run()
{ {
@@ -102,12 +98,6 @@ public:
return; return;
} }
// Check eventfd to see if we need to exit
if (m_fds[1].revents & POLLIN)
{
break;
}
// Check udev // Check udev
if (m_fds[0].revents & POLLIN) if (m_fds[0].revents & POLLIN)
{ {
@@ -132,6 +122,12 @@ public:
m_fds[0].revents = 0; m_fds[0].revents = 0;
} }
// Check eventfd
if (m_fds[1].revents & POLLIN)
{
break;
}
// Check timer // Check timer
if (m_fds[2].revents & POLLIN) if (m_fds[2].revents & POLLIN)
{ {
@@ -150,35 +146,26 @@ public:
-> std::conditional_t<Input, input_port, output_port> -> std::conditional_t<Input, input_port, output_port>
{ {
#if LIBREMIDI_HAS_UDEV #if LIBREMIDI_HAS_UDEV
auto [container, device, type, manufacturer, product, serial] auto [container, device, type] = get_udev_soundcard_info(m_udev, p.card);
= get_udev_soundcard_info(m_udev, p.card);
#else #else
container_identifier container{}; container_identifier container{};
device_identifier device{}; device_identifier device{};
libremidi::transport_type type{}; libremidi::port_information::port_type type{};
std::string manufacturer;
std::string product;
std::string serial;
#endif #endif
std::string display_name = p.subdevice_name; std::string display_name = p.subdevice_name;
if (display_name.empty()) if (display_name.empty())
display_name = p.device_name; display_name = p.device_name;
if (display_name.empty()) if (display_name.empty())
display_name = p.card_name; display_name = p.card_name;
if (display_name.empty())
display_name = product;
if (display_name.empty()) if (display_name.empty())
display_name = "unknown"; display_name = "unknown";
return { return {
{.api = get_current_api(), {.client = 0,
.client = 0,
.container = container, .container = container,
.device = device, .device = device,
.port = raw_to_port_handle({p.card, p.dev, p.sub}), .port = raw_to_port_handle({p.card, p.dev, p.sub}),
.manufacturer = manufacturer, .manufacturer = p.card_name,
.product = product,
.serial = serial,
.device_name = p.device_name, .device_name = p.device_name,
.port_name = p.subdevice_name, .port_name = p.subdevice_name,
.display_name = std::move(display_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> struct observer_impl : observer_impl_base<alsa_raw::midi1_enumerator>
{ {
observer_impl(observer_configuration&& conf, alsa_raw_observer_configuration&& apiconf) using alsa_raw::observer_impl_base<midi1_enumerator>::observer_impl_base;
: observer_impl_base<alsa_raw::midi1_enumerator>{std::move(conf), std::move(apiconf)}
{
finish_init();
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::ALSA_RAW; } libremidi::API get_current_api() const noexcept override { return libremidi::API::ALSA_RAW; }
}; };
} }
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI::alsa_raw_ump namespace libremidi::alsa_raw_ump
{ {
struct backend struct backend
{ {
@@ -4,7 +4,7 @@
#include <chrono> #include <chrono>
#include <functional> #include <functional>
NAMESPACE_LIBREMIDI::alsa_raw_ump namespace libremidi::alsa_raw_ump
{ {
struct input_configuration struct input_configuration
{ {
@@ -5,7 +5,7 @@
#include <cstdio> #include <cstdio>
NAMESPACE_LIBREMIDI::alsa_raw_ump namespace libremidi::alsa_raw_ump
{ {
struct midi2_enumerator : alsa_raw::enumerator struct midi2_enumerator : alsa_raw::enumerator
{ {
@@ -10,7 +10,7 @@
#include <atomic> #include <atomic>
#include <thread> #include <thread>
NAMESPACE_LIBREMIDI::alsa_raw_ump namespace libremidi::alsa_raw_ump
{ {
class midi_in_impl class midi_in_impl
: public midi2::in_api : public midi2::in_api
@@ -44,8 +44,7 @@ public:
SND_RAWMIDI_NONBLOCK; // fixme SND_RAWMIDI_NONBLOCK; // fixme
if (int err = snd.ump.open(&midiport_, 0, portname, mode); err < 0) if (int err = snd.ump.open(&midiport_, 0, portname, mode); err < 0)
{ {
libremidi_handle_error( libremidi_handle_error(this->configuration, "alsa_raw_ump::ump::open_port: cannot open device.");
this->configuration, "alsa_raw_ump::ump::open_port: cannot open device.");
return from_errc(err); return from_errc(err);
} }
@@ -144,8 +143,7 @@ public:
while ((err = snd.ump.read(this->midiport_, words, nwords * 4)) > 0) while ((err = snd.ump.read(this->midiport_, words, nwords * 4)) > 0)
{ {
const auto to_ns = [this] { return absolute_timestamp(); }; const auto to_ns = [this] { return absolute_timestamp(); };
m_processing.on_bytes( m_processing.on_bytes({words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
{words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
} }
return err; return err;
} }
@@ -168,8 +166,7 @@ public:
const auto to_ns = [ts] { const auto to_ns = [ts] {
return static_cast<int64_t>(ts.tv_sec) * 1'000'000'000 + static_cast<int64_t>(ts.tv_nsec); return static_cast<int64_t>(ts.tv_sec) * 1'000'000'000 + static_cast<int64_t>(ts.tv_nsec);
}; };
m_processing.on_bytes( m_processing.on_bytes({words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
{words, words + err / 4}, m_processing.timestamp<timestamp_info>(to_ns, 0));
} }
return err; return err;
} }
@@ -260,7 +257,8 @@ private:
using namespace std::literals; using namespace std::literals;
libremidi_handle_error( 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 e.code();
} }
return stdx::error{}; return stdx::error{};
@@ -311,8 +309,7 @@ private:
{ {
if (configuration.timestamps == timestamp_mode::NoTimestamp) if (configuration.timestamps == timestamp_mode::NoTimestamp)
{ {
configuration.manual_poll( configuration.manual_poll(manual_poll_parameters{
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()}, .fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) { .callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer, fds); return do_read_events(&midi_in_impl::read_input_buffer, fds);
@@ -320,8 +317,7 @@ private:
} }
else else
{ {
configuration.manual_poll( configuration.manual_poll(manual_poll_parameters{
manual_poll_parameters{
.fds = {this->fds_.data(), this->fds_.size()}, .fds = {this->fds_.data(), this->fds_.size()},
.callback = [this](std::span<pollfd> fds) { .callback = [this](std::span<pollfd> fds) {
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds); return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
@@ -339,7 +335,7 @@ private:
}; };
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <> template <>
inline std::unique_ptr<midi_in_api> make<alsa_raw_ump::midi_in_impl>( inline std::unique_ptr<midi_in_api> make<alsa_raw_ump::midi_in_impl>(
@@ -13,7 +13,7 @@
#include <system_error> #include <system_error>
#include <utility> #include <utility>
NAMESPACE_LIBREMIDI::alsa_raw_ump namespace libremidi::alsa_raw_ump
{ {
class midi_out_impl final class midi_out_impl final
: public midi2::out_api : public midi2::out_api
@@ -2,18 +2,13 @@
#include <libremidi/backends/alsa_raw/observer.hpp> #include <libremidi/backends/alsa_raw/observer.hpp>
#include <libremidi/backends/alsa_raw_ump/helpers.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> class observer_impl : public alsa_raw::observer_impl_base<midi2_enumerator>
{ {
public: using alsa_raw::observer_impl_base<midi2_enumerator>::observer_impl_base;
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();
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::ALSA_RAW_UMP; } 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> #include <string_view>
NAMESPACE_LIBREMIDI::alsa_seq namespace libremidi::alsa_seq
{ {
struct backend struct backend
@@ -42,7 +42,7 @@ struct backend
if (!snd.available || !snd.seq.available) if (!snd.available || !snd.seq.available)
return false; 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 #endif
NAMESPACE_LIBREMIDI::alsa_seq namespace libremidi::alsa_seq
{ {
// Possible parameters: // Possible parameters:
@@ -9,7 +9,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
NAMESPACE_LIBREMIDI::alsa_seq namespace libremidi::alsa_seq
{ {
struct event_handle struct event_handle
{ {
@@ -42,6 +42,8 @@ struct event_handle
~event_handle() { snd.seq.free_event(ev); } ~event_handle() { snd.seq.free_event(ev); }
}; };
namespace
{
inline constexpr port_handle seq_to_port_handle(uint64_t client, uint64_t port) noexcept inline constexpr port_handle seq_to_port_handle(uint64_t client, uint64_t port) noexcept
{ {
return (port << 32) + client; 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}; 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( inline void for_all_ports(
const libasound& snd, snd_seq_t* seq, const libasound& snd, snd_seq_t* seq,
const std::function<void(snd_seq_client_info_t&, snd_seq_port_info_t&)>& func) 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 // This function is used to count or get the pinfo structure for a given port
// number. // number.
inline unsigned int iterate_port_info( inline unsigned int iterate_port_info(
@@ -156,6 +125,7 @@ inline unsigned int iterate_port_info(
return count; return count;
return 0; return 0;
} }
}
// A structure to hold variables related to the ALSA API // A structure to hold variables related to the ALSA API
// implementation. // implementation.
@@ -189,15 +159,15 @@ struct alsa_data
snd.seq.set_client_name(seq, configuration.client_name.data()); snd.seq.set_client_name(seq, configuration.client_name.data());
#if __has_include(<alsa/ump.h>) #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) switch (configuration.midi_version)
{ {
case 1: 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; break;
case 2: 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; break;
} }
} }
@@ -302,7 +272,8 @@ struct alsa_data
// Make subscription // Make subscription
if (int err = snd.seq.port_subscribe_malloc(&this->subscription); err < 0) 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; return err;
} }
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_in.hpp> #include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI::alsa_seq namespace libremidi::alsa_seq
{ {
struct dummy_processing struct dummy_processing
{ {
@@ -195,6 +195,11 @@ public:
return stdx::error{}; 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 stdx::error set_port_name(std::string_view portName) override
{ {
return alsa_data::set_port_name(portName); return alsa_data::set_port_name(portName);
@@ -525,7 +530,7 @@ public:
}; };
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <> template <>
inline std::unique_ptr<midi_in_api> inline std::unique_ptr<midi_in_api>
@@ -3,7 +3,7 @@
#include <libremidi/backends/alsa_seq/helpers.hpp> #include <libremidi/backends/alsa_seq/helpers.hpp>
#include <libremidi/detail/midi_out.hpp> #include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI::alsa_seq namespace libremidi::alsa_seq
{ {
class midi_out_impl final class midi_out_impl final
@@ -110,6 +110,11 @@ public:
return stdx::error{}; 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 stdx::error set_port_name(std::string_view portName) override
{ {
return alsa_data::set_port_name(portName); return alsa_data::set_port_name(portName);
+62 -177
View File
@@ -13,26 +13,19 @@
#include <map> #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 struct port_info
{ {
std::string client_name; std::string client_name;
std::string port_name; std::string port_name;
int client{}; int client{};
int port{}; int port{};
bool is_input{}; bool isInput{};
bool is_output{}; bool isOutput{};
std::optional<int> card{}; std::optional<int> card{};
libremidi::transport_type type{}; libremidi::port_information::port_type type{};
}; };
template <typename ConfigurationImpl> template <typename ConfigurationImpl>
@@ -100,69 +93,12 @@ public:
} }
} }
std::optional<client_info> std::optional<port_info> get_info(int client, int port) const noexcept
get_client_info(int client, snd_seq_client_info_t& cinfo) 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; port_info p;
p.client = client; p.client = client;
p.port = port; 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_t* cinfo;
snd_seq_client_info_alloca(&cinfo); snd_seq_client_info_alloca(&cinfo);
if (int err = snd.seq.get_any_client_info(seq, client, cinfo); err < 0) 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) if (int err = snd.seq.get_any_port_info(seq, client, port, pinfo); err < 0)
return std::nullopt; 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> template <bool Input>
@@ -185,10 +153,7 @@ public:
container_identifier container{}; container_identifier container{};
device_identifier device{}; device_identifier device{};
libremidi::transport_type type = p.type; libremidi::port_information::port_type type = p.type;
std::string manufacturer;
std::string product;
std::string serial;
#if LIBREMIDI_HAS_UDEV #if LIBREMIDI_HAS_UDEV
if (p.card) if (p.card)
{ {
@@ -196,20 +161,14 @@ public:
container = res.container; container = res.container;
device = res.path; device = res.path;
type = res.type; type = res.type;
manufacturer = res.vendor;
product = res.product;
serial = res.serial;
} }
#endif #endif
return { return {
{.api = get_current_api(), {.client = std::uintptr_t(this->seq),
.client = std::uintptr_t(this->seq),
.container = container, .container = container,
.device = device, .device = device,
.port = alsa_seq::seq_to_port_handle(p.client, p.port), .port = alsa_seq::seq_to_port_handle(p.client, p.port),
.manufacturer = manufacturer, .manufacturer = "",
.product = product,
.serial = serial,
.device_name = p.client_name, .device_name = p.client_name,
.port_name = p.port_name, .port_name = p.port_name,
.display_name = p.port_name, .display_name = p.port_name,
@@ -241,8 +200,8 @@ public:
snd, this->seq, [this, &ret](snd_seq_client_info_t& client, snd_seq_port_info_t& port) { 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 clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port); int pt = snd.seq.port_info_get_port(&port);
if (auto p = get_info(clt, pt, client, port)) if (auto p = get_info(clt, pt))
if (p->is_input) if (p->isInput)
ret.push_back(to_port_info<true>(*p)); ret.push_back(to_port_info<true>(*p));
}); });
return ret; return ret;
@@ -255,8 +214,8 @@ public:
snd, this->seq, [this, &ret](snd_seq_client_info_t& client, snd_seq_port_info_t& port) { 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 clt = snd.seq.client_info_get_client(&client);
int pt = snd.seq.port_info_get_port(&port); int pt = snd.seq.port_info_get_port(&port);
if (auto p = get_info(clt, pt, client, port)) if (auto p = get_info(clt, pt))
if (p->is_output) if (p->isOutput)
ret.push_back(to_port_info<false>(*p)); ret.push_back(to_port_info<false>(*p));
}); });
return ret; return ret;
@@ -272,12 +231,12 @@ public:
return; return;
m_knownClients[{p.client, p.port}] = p; 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)); 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)); configuration.output_added(to_port_info<false>(p));
} }
@@ -291,55 +250,31 @@ public:
auto p = it->second; auto p = it->second;
m_knownClients.erase(it); 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)); 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)); 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) 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: { 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; break;
} }
case SND_SEQ_EVENT_PORT_EXIT: { 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; break;
} }
case SND_SEQ_EVENT_PORT_CHANGE: case SND_SEQ_EVENT_PORT_CHANGE:
// TODO
break;
default: default:
break; break;
} }
@@ -374,87 +309,38 @@ public:
{ {
// Create relevant descriptors // Create relevant descriptors
auto& snd = alsa_data::snd; auto& snd = alsa_data::snd;
// 1. Descriptor count
const auto n = snd.seq.poll_descriptors_count(this->seq, POLLIN); const auto n = snd.seq.poll_descriptors_count(this->seq, POLLIN);
int total_descriptors = n; descriptors_.resize(n + 1);
total_descriptors++; // eventfd for terminating the thread snd.seq.poll_descriptors(this->seq, descriptors_.data(), n, POLLIN);
descriptors_.back() = this->termination_event;
// 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;
// Start the listening thread // Start the listening thread
thread = std::thread{[this, n] { thread = std::thread{[this] {
auto& snd = alsa_data::snd; auto& snd = alsa_data::snd;
const auto period const auto period
= std::chrono::duration_cast<std::chrono::milliseconds>(this->configuration.poll_period) = std::chrono::duration_cast<std::chrono::milliseconds>(this->configuration.poll_period)
.count(); .count();
for (;;) 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) if (err >= 0)
{ {
// We got our stop-thread signal // We got our stop-thread signal
if (descriptors[n].revents & POLLIN) if (descriptors_.back().revents & POLLIN)
break; break;
// Put ALSA event in our queue
snd_seq_event_t* ev{}; snd_seq_event_t* ev{};
event_handle handle{snd}; event_handle handle{snd};
while (snd.seq.event_input(this->seq, &ev) >= 0) while (snd.seq.event_input(this->seq, &ev) >= 0)
{ {
handle.reset(ev); handle.reset(ev);
this->handle_event_delayed(*ev); this->handle_event(*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;
}
} }
} }
} }
}}; }};
} }
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() ~observer_threaded()
{ {
termination_event.notify(); termination_event.notify();
@@ -465,8 +351,7 @@ public:
eventfd_notifier termination_event{}; eventfd_notifier termination_event{};
std::thread thread; std::thread thread;
std::vector<pollfd> descriptors; std::vector<pollfd> descriptors_;
std::vector<std::pair<snd_seq_event_t, std::chrono::steady_clock::time_point>> queued_events;
}; };
template <typename ConfigurationImpl> template <typename ConfigurationImpl>
@@ -478,7 +363,7 @@ public:
{ {
this->configuration.manual_poll( this->configuration.manual_poll(
poll_parameters{.addr = this->vaddr, .callback = [this](const auto& v) { poll_parameters{.addr = this->vaddr, .callback = [this](const auto& v) {
this->handle_event_direct(v); this->handle_event(v);
return 0; return 0;
}}); }});
} }
@@ -487,7 +372,7 @@ public:
}; };
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <> template <>
inline std::unique_ptr<observer_api> inline std::unique_ptr<observer_api>
@@ -9,7 +9,7 @@
#include <variant> #include <variant>
NAMESPACE_LIBREMIDI::alsa_seq namespace libremidi::alsa_seq
{ {
struct shared_handler : public libremidi::shared_context struct shared_handler : public libremidi::shared_context
@@ -127,13 +127,13 @@ struct shared_handler : public libremidi::shared_context
{ {
case callback_added: { case callback_added: {
auto [addr, cb] 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); addresses.push_back(addr);
callbacks.push_back(std::move(cb)); callbacks.push_back(std::move(cb));
break; break;
} }
case callback_removed: 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) if (auto index = index_of_address(addr); index >= 0)
{ {
addresses.erase(addresses.begin() + index); addresses.erase(addresses.begin() + index);
@@ -179,7 +179,7 @@ struct shared_handler : public libremidi::shared_context
struct event struct event
{ {
event_type type; 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{}; snd_seq_t* client{};
+3 -3
View File
@@ -9,7 +9,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <> template <>
inline std::unique_ptr<observer_api> 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 struct backend
{ {
@@ -63,7 +63,7 @@ struct backend
if (!snd.available || !snd.seq.available || !snd.seq.ump.available || !snd.ump.available) if (!snd.available || !snd.seq.available || !snd.seq.ump.available || !snd.ump.available)
return false; 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; typedef struct snd_seq_ump_event snd_seq_ump_event_t;
} }
NAMESPACE_LIBREMIDI::alsa_seq_ump namespace libremidi::alsa_seq_ump
{ {
struct poll_parameters struct poll_parameters
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_out.hpp> #include <libremidi/detail/midi_out.hpp>
#include <libremidi/detail/ump_stream.hpp> #include <libremidi/detail/ump_stream.hpp>
NAMESPACE_LIBREMIDI::alsa_seq_ump namespace libremidi::alsa_seq_ump
{ {
class midi_out_impl final class midi_out_impl final
@@ -106,6 +106,11 @@ public:
return stdx::error{}; 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 stdx::error set_port_name(std::string_view portName) override
{ {
return alsa_data::set_port_name(portName); return alsa_data::set_port_name(portName);
@@ -132,7 +137,7 @@ public:
static_assert(std::errc{0} == std::errc{}); static_assert(std::errc{0} == std::errc{});
return 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); snd.seq.drain_output(this->seq);
return stdx::error{}; 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> #include <string_view>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct core_backend struct core_backend
{ {
@@ -18,7 +18,7 @@ typedef MIDIObjectRef MIDIClientRef;
using MIDIClientRef = uint32_t; using MIDIClientRef = uint32_t;
using MIDIObjectRef = uint32_t; using MIDIObjectRef = uint32_t;
#endif #endif
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct coremidi_input_configuration struct coremidi_input_configuration
@@ -3,7 +3,7 @@
#include <CoreMIDI/CoreMIDI.h> #include <CoreMIDI/CoreMIDI.h>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct coremidi_error_domain : public stdx::error_domain struct coremidi_error_domain : public stdx::error_domain
{ {
+14 -10
View File
@@ -1,9 +1,9 @@
#pragma once #pragma once
#include <libremidi/backends/coremidi/error_domain.hpp>
#include <libremidi/detail/memory.hpp> #include <libremidi/detail/memory.hpp>
#include <libremidi/detail/midi_api.hpp> #include <libremidi/detail/midi_api.hpp>
#include <libremidi/error_handler.hpp> #include <libremidi/error_handler.hpp>
#include <libremidi/input_configuration.hpp> #include <libremidi/input_configuration.hpp>
#include <libremidi/backends/coremidi/error_domain.hpp>
#include <CoreMIDI/CoreMIDI.h> #include <CoreMIDI/CoreMIDI.h>
#include <CoreServices/CoreServices.h> #include <CoreServices/CoreServices.h>
@@ -21,12 +21,13 @@
#define LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME AudioGetCurrentHostTime #define LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME AudioGetCurrentHostTime
#endif #endif
NAMESPACE_LIBREMIDI namespace libremidi
{ {
using CFString_handle = unique_handle<const __CFString, CFRelease>; using CFString_handle = unique_handle<const __CFString, CFRelease>;
using CFStringMutable_handle = unique_handle<__CFString, CFRelease>; using CFStringMutable_handle = unique_handle<__CFString, CFRelease>;
namespace
LIBREMIDI_STATIC std::string get_string_property(MIDIObjectRef object, CFStringRef property) noexcept {
static inline std::string get_string_property(MIDIObjectRef object, CFStringRef property) noexcept
{ {
CFStringRef res; CFStringRef res;
if (MIDIObjectGetStringProperty(object, property, &res) || !res) if (MIDIObjectGetStringProperty(object, property, &res) || !res)
@@ -38,14 +39,14 @@ LIBREMIDI_STATIC std::string get_string_property(MIDIObjectRef object, CFStringR
return name; 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; SInt32 res;
MIDIObjectGetIntegerProperty(object, property, &res); MIDIObjectGetIntegerProperty(object, property, &res);
return 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)}; return CFString_handle{CFStringCreateWithCString(nullptr, str.data(), kCFStringEncodingASCII)};
} }
@@ -71,12 +72,12 @@ inline uint64_t AudioConvertHostTimeToNanos(uint64_t hostTime)
} }
#endif #endif
LIBREMIDI_STATIC auto get_cfstring_property(MIDIObjectRef prop, CFStringRef name) static inline auto get_cfstring_property(MIDIObjectRef prop, CFStringRef name)
{ {
CFStringRef str = nullptr; CFStringRef str = nullptr;
MIDIObjectGetStringProperty(prop, name, &str); MIDIObjectGetStringProperty(prop, name, &str);
return CFString_handle{str}; return CFString_handle{str};
} };
// This function was submitted by Douglas Casey Tucker and apparently // This function was submitted by Douglas Casey Tucker and apparently
// derived largely from PortMidi. // derived largely from PortMidi.
@@ -219,19 +220,22 @@ locate_object(auto& self, const port_information& info, MIDIObjectType requested
auto ret = MIDIObjectFindByUniqueID(uid, &object, &type); auto ret = MIDIObjectFindByUniqueID(uid, &object, &type);
if (ret != noErr) 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; return 0;
} }
if (type != requested_type || object == 0) if (type != requested_type || object == 0)
{ {
self.libremidi_handle_error( 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 0;
} }
return object; return object;
} }
}
// A structure to hold variables related to the CoreMIDI API // A structure to hold variables related to the CoreMIDI API
// implementation. // implementation.
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp> #include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_in_core final class midi_in_core final
: public midi1::in_api : public midi1::in_api
@@ -25,7 +25,8 @@ public:
if (auto result = init_client(configuration); result != noErr) if (auto result = init_client(configuration); result != noErr)
{ {
libremidi_handle_error( 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); client_open_ = from_osstatus(result);
return; return;
} }
@@ -63,7 +64,8 @@ public:
{ {
close_client(*this); close_client(*this);
libremidi_handle_error( 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); return from_osstatus(result);
} }
@@ -72,7 +74,8 @@ public:
{ {
MIDIPortDispose(port); MIDIPortDispose(port);
close_client(*this); 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); return from_osstatus(result);
} }
@@ -102,7 +105,10 @@ public:
return stdx::error{}; 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 timestamp absolute_timestamp() const noexcept override
{ {
@@ -3,7 +3,7 @@
#include <libremidi/backends/coremidi/helpers.hpp> #include <libremidi/backends/coremidi/helpers.hpp>
#include <libremidi/detail/midi_out.hpp> #include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_out_core final class midi_out_core final
: public midi1::out_api : public midi1::out_api
@@ -24,7 +24,8 @@ public:
if (auto result = init_client(configuration); result != noErr) if (auto result = init_client(configuration); result != noErr)
{ {
libremidi_handle_error( 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); client_open_ = from_osstatus(result);
return; return;
} }
@@ -59,7 +60,8 @@ public:
if (result != noErr) if (result != noErr)
{ {
close_client(*this); 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); return from_osstatus(result);
} }
@@ -78,7 +80,9 @@ public:
if (result != noErr) 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); return from_osstatus(result);
} }
@@ -88,7 +92,10 @@ public:
return stdx::error{}; 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 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 MIDITimeStamp timestamp = LIBREMIDI_AUDIO_GET_CURRENT_HOST_TIME();
const ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes; const ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes;
Byte* buffer = (Byte*)alloca(bufsize + 16); // pad for other struct members Byte buffer[bufsize + 16]; // pad for other struct members
ByteCount listSize = bufsize + 16; ByteCount listSize = sizeof(buffer);
MIDIPacketList* packetList = (MIDIPacketList*)buffer; MIDIPacketList* packetList = (MIDIPacketList*)buffer;
ByteCount remainingBytes = nBytes; ByteCount remainingBytes = nBytes;
@@ -130,7 +137,8 @@ public:
if (!packet) 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; return std::errc::message_size;
} }
@@ -155,7 +163,9 @@ public:
auto result = MIDISend(this->port, this->destinationId, packetList); auto result = MIDISend(this->port, this->destinationId, packetList);
if (result != noErr) 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; return std::errc::io_error;
} }
} }
@@ -7,7 +7,7 @@
#include <bit> #include <bit>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class observer_core class observer_core
: public observer_api : public observer_api
@@ -22,10 +22,6 @@ public:
explicit observer_core(observer_configuration&& conf, coremidi_observer_configuration&& apiconf) explicit observer_core(observer_configuration&& conf, coremidi_observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)} : configuration{std::move(conf), std::move(apiconf)}
{
}
void finish_init()
{ {
if (configuration.client_name.empty()) if (configuration.client_name.empty())
configuration.client_name = "libremidi observer"; configuration.client_name = "libremidi observer";
@@ -37,12 +33,14 @@ public:
toCFString(configuration.client_name).get(), toCFString(configuration.client_name).get(),
+[](const MIDINotification* message, void* ctx) { +[](const MIDINotification* message, void* ctx) {
((observer_core*)ctx)->notify(message); ((observer_core*)ctx)->notify(message);
}, this, &client); },
this, &client);
if (result != noErr) if (result != noErr)
{ {
libremidi_handle_error( 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; return;
} }
@@ -81,7 +79,7 @@ public:
return {}; return {};
// Get the MIDI device from the entity // Get the MIDI device from the entity
libremidi::transport_type type{}; libremidi::port_information::port_type type{};
libremidi::container_identifier usb_location_id{}; libremidi::container_identifier usb_location_id{};
libremidi::device_identifier usb_vendor_product{}; libremidi::device_identifier usb_vendor_product{};
{ {
@@ -90,11 +88,11 @@ public:
{ {
if (device) if (device)
{ {
using enum libremidi::transport_type; using enum libremidi::port_information::port_type;
SInt32 devid_res{}; SInt32 devid_res{};
if (MIDIObjectGetIntegerProperty(device, CFSTR("USBLocationID"), &devid_res) == noErr) 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; usb_location_id = (uint64_t)devid_res;
} }
@@ -102,26 +100,25 @@ public:
if (MIDIObjectGetIntegerProperty(device, CFSTR("USBVendorProduct"), &vendor_res) if (MIDIObjectGetIntegerProperty(device, CFSTR("USBVendorProduct"), &vendor_res)
== noErr) == noErr)
{ {
type = (libremidi::transport_type)(hardware | usb); type = (libremidi::port_information::port_type)(hardware | usb);
usb_vendor_product = (uint64_t)vendor_res; usb_vendor_product = (uint64_t)vendor_res;
} }
const auto driver = get_string_property(device, kMIDIPropertyDriverOwner); const auto driver = get_string_property(device, kMIDIPropertyDriverOwner);
if (driver == "com.apple.AppleMIDIUSBDriver") if (driver == "com.apple.AppleMIDIUSBDriver")
type = (libremidi::transport_type)(hardware | usb); type = (libremidi::port_information::port_type)(hardware | usb);
if (driver == "com.apple.AppleMIDIBluetoothDriver") if (driver == "com.apple.AppleMIDIBluetoothDriver")
type = (libremidi::transport_type)(hardware | bluetooth); type = (libremidi::port_information::port_type)(hardware | bluetooth);
if (driver == "com.apple.AppleMIDIIACDriver") if (driver == "com.apple.AppleMIDIIACDriver")
type = (libremidi::transport_type)(software); type = (libremidi::port_information::port_type)(software);
if (driver == "com.apple.AppleMIDIRTPDriver") 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>{ 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, .container = usb_location_id,
.device = usb_vendor_product, .device = usb_vendor_product,
.port = std::bit_cast<uint32_t>(get_int_property(obj, kMIDIPropertyUniqueID)), .port = std::bit_cast<uint32_t>(get_int_property(obj, kMIDIPropertyUniqueID)),
@@ -218,17 +215,3 @@ private:
MIDIClientRef client{}; 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> #include <string_view>
NAMESPACE_LIBREMIDI::coremidi_ump namespace libremidi::coremidi_ump
{ {
struct backend struct backend
{ {
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <libremidi/backends/coremidi/config.hpp> #include <libremidi/backends/coremidi/config.hpp>
NAMESPACE_LIBREMIDI::coremidi_ump namespace libremidi::coremidi_ump
{ {
struct input_configuration struct input_configuration
{ {
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp> #include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI::coremidi_ump namespace libremidi::coremidi_ump
{ {
class midi_in_impl final class midi_in_impl final
@@ -26,7 +26,8 @@ public:
if (auto result = init_client(configuration); result != noErr) if (auto result = init_client(configuration); result != noErr)
{ {
libremidi_handle_error( 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); client_open_ = from_osstatus(result);
return; return;
} }
@@ -68,7 +69,8 @@ public:
{ {
close_client(*this); close_client(*this);
libremidi_handle_error( 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); return from_osstatus(result);
} }
@@ -77,7 +79,8 @@ public:
{ {
MIDIPortDispose(port); MIDIPortDispose(port);
close_client(*this); 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); return from_osstatus(result);
} }
@@ -110,7 +113,10 @@ public:
return stdx::error{}; 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 timestamp absolute_timestamp() const noexcept override
{ {
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_out.hpp> #include <libremidi/detail/midi_out.hpp>
#include <libremidi/detail/ump_stream.hpp> #include <libremidi/detail/ump_stream.hpp>
NAMESPACE_LIBREMIDI::coremidi_ump namespace libremidi::coremidi_ump
{ {
class midi_out_impl final class midi_out_impl final
: public midi2::out_api : public midi2::out_api
@@ -27,7 +27,8 @@ public:
if (auto result = init_client(configuration); result != noErr) if (auto result = init_client(configuration); result != noErr)
{ {
libremidi_handle_error( 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); client_open_ = from_osstatus(result);
return; return;
} }
@@ -62,7 +63,8 @@ public:
if (result != noErr) if (result != noErr)
{ {
close_client(*this); 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); return from_osstatus(result);
} }
@@ -82,7 +84,9 @@ public:
if (result != noErr) if (result != noErr)
{ {
this->endpoint = 0; 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); return from_osstatus(result);
} }
@@ -90,7 +94,10 @@ public:
return stdx::error{}; 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 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); auto result = MIDISendEventList(this->port, this->destinationId, eventList);
if (result != noErr) 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; return std::errc::io_error;
} }
} }
@@ -1,20 +1,17 @@
#pragma once #pragma once
#include <libremidi/backends/coremidi/observer.hpp>
#include <libremidi/backends/coremidi_ump/config.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 class observer_impl final : public libremidi::observer_core
{ {
public: public:
explicit observer_impl( explicit observer_impl(libremidi::observer_configuration&& conf, coremidi_ump::observer_configuration&& apiconf)
libremidi::observer_configuration&& conf, coremidi_ump::observer_configuration&& apiconf) : observer_core{std::move(conf), coremidi_observer_configuration{apiconf.client_name, apiconf.on_create_context}}
: 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; } libremidi::API get_current_api() const noexcept override { return libremidi::API::COREMIDI_UMP; }
+25 -5
View File
@@ -7,7 +7,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class observer_dummy : public observer_api class observer_dummy : public observer_api
{ {
@@ -40,9 +40,19 @@ public:
{ {
return stdx::error{}; 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 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; } timestamp absolute_timestamp() const noexcept override { return 0; }
}; };
@@ -62,10 +72,20 @@ public:
{ {
return stdx::error{}; 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 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 stdx::error send_message(const unsigned char* /*message*/, size_t /*size*/) override
{ {
return stdx::error{}; return stdx::error{};
+1 -1
View File
@@ -9,7 +9,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct emscripten_backend struct emscripten_backend
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <libremidi/config.hpp> #include <libremidi/config.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct emscripten_input_configuration struct emscripten_input_configuration
@@ -3,7 +3,7 @@
#include <string> #include <string>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
namespace webmidi_helpers namespace webmidi_helpers
{ {
@@ -16,7 +16,7 @@ EMSCRIPTEN_KEEPALIVE
void libremidi_devices_input(int port, double timestamp, int len, char* bytes); void libremidi_devices_input(int port, double timestamp, int len, char* bytes);
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
namespace webmidi_helpers namespace webmidi_helpers
{ {
@@ -37,25 +37,16 @@ public:
int input_count() const noexcept int input_count() const noexcept
{ {
if (!available())
return 0;
return EM_ASM_INT(return globalThis.__libreMidi_currentInputs.length;); return EM_ASM_INT(return globalThis.__libreMidi_currentInputs.length;);
} }
int output_count() const noexcept int output_count() const noexcept
{ {
if (!available())
return 0;
return EM_ASM_INT(return globalThis.__libreMidi_currentOutputs.length;); return EM_ASM_INT(return globalThis.__libreMidi_currentOutputs.length;);
} }
void load_current_infos() noexcept void load_current_infos() noexcept
{ {
if (!available())
return;
#define get_js_string(variable_to_read, ...) \ #define get_js_string(variable_to_read, ...) \
(char*)EM_ASM_INT( \ (char*)EM_ASM_INT( \
{ \ { \
@@ -215,9 +206,6 @@ public:
stdx::error send_message(int port_index, const char* bytes, int len) 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; const auto& id = m_current_outputs[port_index].id;
EM_ASM( EM_ASM(
{ {
@@ -228,8 +216,6 @@ public:
output.send(Array.from(bytes)); output.send(Array.from(bytes));
}, },
bytes, len, id.c_str()); bytes, len, id.c_str());
return stdx::error{};
} }
const std::vector<device_information>& inputs() const noexcept { return m_current_inputs; } const std::vector<device_information>& inputs() const noexcept { return m_current_inputs; }
@@ -262,9 +248,6 @@ private:
void start_stream(int port_index) void start_stream(int port_index)
{ {
if (!available())
return;
// Isn't life great... // Isn't life great...
// https://github.com/Planeshifter/emscripten-examples/tree/master/01_PassingArrays // https://github.com/Planeshifter/emscripten-examples/tree/master/01_PassingArrays
const auto& id = m_current_inputs[port_index].id; const auto& id = m_current_inputs[port_index].id;
@@ -299,9 +282,6 @@ private:
void stop_stream(int port_index) void stop_stream(int port_index)
{ {
if (!available())
return;
const auto& id = m_current_inputs[port_index].id; const auto& id = m_current_inputs[port_index].id;
EM_ASM(const id = UTF8ToString($1); EM_ASM(const id = UTF8ToString($1);
@@ -5,7 +5,7 @@
#include <chrono> #include <chrono>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
LIBREMIDI_INLINE midi_in_emscripten::midi_in_emscripten( LIBREMIDI_INLINE midi_in_emscripten::midi_in_emscripten(
input_configuration&& conf, emscripten_input_configuration&& apiconf) 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()) 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; return std::errc::invalid_argument;
} }
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp> #include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_in_emscripten final class midi_in_emscripten final
: public midi1::in_api : public midi1::in_api
@@ -2,7 +2,7 @@
#include <libremidi/backends/emscripten/midi_access.hpp> #include <libremidi/backends/emscripten/midi_access.hpp>
#include <libremidi/backends/emscripten/midi_out.hpp> #include <libremidi/backends/emscripten/midi_out.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
LIBREMIDI_INLINE midi_out_emscripten::midi_out_emscripten( LIBREMIDI_INLINE midi_out_emscripten::midi_out_emscripten(
output_configuration&& conf, emscripten_output_configuration&& apiconf) 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()) 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; return std::errc::invalid_argument;
} }
@@ -37,19 +38,16 @@ LIBREMIDI_INLINE stdx::error midi_out_emscripten::open_port(int portNumber, std:
return stdx::error{}; return stdx::error{};
} }
LIBREMIDI_INLINE stdx::error LIBREMIDI_INLINE stdx::error midi_out_emscripten::open_port(const output_port& p, std::string_view nm)
midi_out_emscripten::open_port(const output_port& p, std::string_view nm)
{ {
return open_port(p.port, 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{}; return stdx::error{};
} }
LIBREMIDI_INLINE stdx::error LIBREMIDI_INLINE stdx::error midi_out_emscripten::send_message(const unsigned char* message, size_t size)
midi_out_emscripten::send_message(const unsigned char* message, size_t size)
{ {
if (m_portNumber < 0) if (m_portNumber < 0)
libremidi_handle_error( libremidi_handle_error(
@@ -3,7 +3,7 @@
#include <libremidi/backends/emscripten/helpers.hpp> #include <libremidi/backends/emscripten/helpers.hpp>
#include <libremidi/detail/midi_out.hpp> #include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_out_emscripten final class midi_out_emscripten final
: public midi1::out_api : public midi1::out_api
@@ -4,7 +4,7 @@
#include <cassert> #include <cassert>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
LIBREMIDI_INLINE observer_emscripten::observer_emscripten( LIBREMIDI_INLINE observer_emscripten::observer_emscripten(
observer_configuration&& conf, emscripten_observer_configuration&& apiconf) 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> -> std::conditional_t<Input, input_port, output_port>
{ {
return { return {
{.api = libremidi::API::WEBMIDI, {.client = 0,
.client = 0,
.port = (uint64_t)index, .port = (uint64_t)index,
.manufacturer = "", .manufacturer = "",
.device_name = "", .device_name = "",
@@ -5,7 +5,7 @@
#include <vector> #include <vector>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class observer_emscripten final : public observer_api class observer_emscripten final : public observer_api
{ {
+1 -1
View File
@@ -13,7 +13,7 @@
// //
// *********************************************************************// // *********************************************************************//
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct jack_backend 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); typedef int (*JackProcessCallback)(jack_nframes_t nframes, void* arg);
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
using jack_callback_function = std::function<void(int nframes)>; using jack_callback_function = std::function<void(int nframes)>;
struct jack_callback struct jack_callback
@@ -11,7 +11,7 @@
#include <jack/ringbuffer.h> #include <jack/ringbuffer.h>
#endif #endif
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct jack_error_domain : public stdx::error_domain struct jack_error_domain : public stdx::error_domain
{ {
@@ -34,36 +34,36 @@ public:
stdx::string_ref message(const stdx::error& e) const noexcept override stdx::string_ref message(const stdx::error& e) const noexcept override
{ {
const auto status = error_cast<jack_status_t>(e); const auto status = error_cast<jack_status_t>(e);
if (status == jack_status_t{}) if(status == jack_status_t{})
return "Success"; return "Success";
if (status & JackInvalidOption) if(status & JackInvalidOption)
return "The operation contained an invalid or unsupported option"; return "The operation contained an invalid or unsupported option";
if (status & JackServerFailed) if(status & JackServerFailed)
return "Unable to connect to the JACK server"; return "Unable to connect to the JACK server";
if (status & JackServerError) if(status & JackServerError)
return "Communication error with the JACK server"; return "Communication error with the JACK server";
if (status & JackNoSuchClient) if(status & JackNoSuchClient)
return "Requested client does not exist"; return "Requested client does not exist";
if (status & JackLoadFailure) if(status & JackLoadFailure)
return "Unable to load internal client"; return "Unable to load internal client";
if (status & JackInitFailure) if(status & JackInitFailure)
return "Unable to initialize client"; return "Unable to initialize client";
if (status & JackShmFailure) if(status & JackShmFailure)
return "Unable to access shared memory"; return "Unable to access shared memory";
if (status & JackVersionError) if(status & JackVersionError)
return "Client's protocol version does not match"; return "Client's protocol version does not match";
if (status & JackBackendError) if(status & JackBackendError)
return "Backend error"; return "Backend error";
if (status & JackClientZombie) if(status & JackClientZombie)
return "Client zombified failure"; return "Client zombified failure";
if (status & JackFailure) if(status & JackFailure)
return "Failure"; return "Failure";
if (status & JackNameNotUnique) if(status & JackNameNotUnique)
return "The desired client name was not unique"; return "The desired client name was not unique";
// Can't happen in libremidi as we set JackNoStartServer // Can't happen in libremidi as we set JackNoStartServer
if (status & JackServerStarted) if(status & JackServerStarted)
return "Server was started"; return "Server was started";
return "Unknown JACK status code"; return "Unknown JACK status code";
+14 -13
View File
@@ -9,7 +9,7 @@
#include <cstring> #include <cstring>
#include <memory> #include <memory>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct jack_client 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) static auto to_port_info(jack_client_t* client, jack_port_t* port)
-> std::conditional_t<Input, input_port, output_port> -> std::conditional_t<Input, input_port, output_port>
{ {
return {{ return {{
.api = Api,
.client = reinterpret_cast<std::uintptr_t>(client), .client = reinterpret_cast<std::uintptr_t>(client),
.port = 0, .port = 0,
.manufacturer = "", .manufacturer = "",
.device_name = jack_get_client_name(client), .device_name = "",
.port_name = jack_port_name(port), .port_name = jack_port_name(port),
.display_name = get_port_display_name(port), .display_name = get_port_display_name(port),
}}; }};
} }
template <bool Input, libremidi::API Api> template <bool Input>
static auto get_ports( static auto get_ports(
jack_client_t* client, const char* pattern, const char* type, const JackPortFlags flags, 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>> bool midi2) noexcept -> std::vector<std::conditional_t<Input, input_port, output_port>>
@@ -79,7 +78,7 @@ struct jack_client
if (port) if (port)
{ {
if (bool(midi2) == bool(jack_port_flags(port) & 0x20)) 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++; i++;
} }
@@ -156,13 +155,13 @@ struct jack_helpers : jack_client
= jack_client_open(configuration.client_name.c_str(), JackNoStartServer, &status); = jack_client_open(configuration.client_name.c_str(), JackNoStartServer, &status);
if (this->client != nullptr) if (this->client != nullptr)
{ {
if (status & JackNameNotUnique) if(status & JackNameNotUnique) {
{ self.libremidi_handle_warning(self.configuration, "JACK client with the same name already exists, renamed.");
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 { jack_set_process_callback(
this->client,
+[](jack_nframes_t nf, void* ctx) -> int {
auto& self = *static_cast<Self*>(ctx); auto& self = *static_cast<Self*>(ctx);
jack_port_t* port = self.port; jack_port_t* port = self.port;
@@ -174,7 +173,8 @@ struct jack_helpers : jack_client
self.thread_lock.check_client_released(); self.thread_lock.check_client_released();
return 0; return 0;
}, &self); },
&self);
jack_activate(this->client); jack_activate(this->client);
} }
return status; return status;
@@ -208,7 +208,8 @@ struct jack_helpers : jack_client
if (self.configuration.client_name.size() + portName.size() + 2u if (self.configuration.client_name.size() + portName.size() + 2u
>= static_cast<size_t>(jack_port_name_size())) >= 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; return std::errc::invalid_argument;
} }
+3 -3
View File
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp> #include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_in_jack final class midi_in_jack final
: public midi1::in_api : public midi1::in_api
@@ -53,8 +53,8 @@ public:
err != 0 && err != EEXIST) err != 0 && err != EEXIST)
{ {
libremidi_handle_error( libremidi_handle_error(
configuration, configuration, "could not connect to port: " + port.port_name + " -> "
"could not connect to port: " + port.port_name + " -> " + jack_port_name(this->port)); + jack_port_name(this->port));
return from_errc(err); return from_errc(err);
} }
return stdx::error{}; return stdx::error{};
+4 -3
View File
@@ -3,7 +3,7 @@
#include <libremidi/backends/jack/helpers.hpp> #include <libremidi/backends/jack/helpers.hpp>
#include <libremidi/detail/midi_out.hpp> #include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_out_jack class midi_out_jack
: public midi1::out_api : 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()); if (int err = jack_connect(this->client, jack_port_name(this->port), port.port_name.c_str());
err != 0 && err != EEXIST) 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); return from_errc(err);
} }
@@ -166,7 +167,7 @@ public:
}; };
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <> template <>
inline std::unique_ptr<midi_out_api> make<midi_out_jack>( inline std::unique_ptr<midi_out_api> make<midi_out_jack>(
+16 -20
View File
@@ -5,7 +5,7 @@
#include <unordered_set> #include <unordered_set>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class observer_jack final class observer_jack final
: public observer_api : public observer_api
@@ -70,8 +70,7 @@ public:
{ {
seen_input_ports.insert(ports[i]); seen_input_ports.insert(ports[i]);
if (this->configuration.input_added && configuration.notify_in_constructor) if (this->configuration.input_added && configuration.notify_in_constructor)
this->configuration.input_added( this->configuration.input_added(to_port_info<true>(client, port));
to_port_info<true, libremidi::API::JACK_MIDI>(client, port));
} }
i++; i++;
} }
@@ -102,8 +101,7 @@ public:
{ {
seen_output_ports.insert(ports[i]); seen_output_ports.insert(ports[i]);
if (this->configuration.output_added && configuration.notify_in_constructor) if (this->configuration.output_added && configuration.notify_in_constructor)
this->configuration.output_added( this->configuration.output_added(to_port_info<false>(client, port));
to_port_info<false, libremidi::API::JACK_MIDI>(client, port));
} }
i++; i++;
} }
@@ -140,15 +138,13 @@ public:
{ {
seen_input_ports.insert(name); seen_input_ports.insert(name);
if (this->configuration.input_added) if (this->configuration.input_added)
this->configuration.input_added( this->configuration.input_added(to_port_info<true>(client, port));
to_port_info<true, libremidi::API::JACK_MIDI>(client, port));
} }
else if (flags & JackPortIsInput) else if (flags & JackPortIsInput)
{ {
seen_output_ports.insert(name); seen_output_ports.insert(name);
if (this->configuration.output_added) if (this->configuration.output_added)
this->configuration.output_added( this->configuration.output_added(to_port_info<false>(client, port));
to_port_info<false, libremidi::API::JACK_MIDI>(client, port));
} }
} }
else else
@@ -156,15 +152,13 @@ public:
if (auto it = seen_input_ports.find(name); it != seen_input_ports.end()) if (auto it = seen_input_ports.find(name); it != seen_input_ports.end())
{ {
if (this->configuration.input_removed) if (this->configuration.input_removed)
this->configuration.input_removed( this->configuration.input_removed(to_port_info<true>(client, port));
to_port_info<true, libremidi::API::JACK_MIDI>(client, port));
seen_input_ports.erase(it); seen_input_ports.erase(it);
} }
if (auto it = seen_output_ports.find(name); it != seen_output_ports.end()) if (auto it = seen_output_ports.find(name); it != seen_output_ports.end())
{ {
if (this->configuration.output_removed) if (this->configuration.output_removed)
this->configuration.output_removed( this->configuration.output_removed(to_port_info<false>(client, port));
to_port_info<false, libremidi::API::JACK_MIDI>(client, port));
seen_output_ports.erase(it); seen_output_ports.erase(it);
} }
} }
@@ -177,13 +171,16 @@ public:
if (!configuration.has_callbacks()) if (!configuration.has_callbacks())
return; return;
jack_set_port_registration_callback(this->client, +[](jack_port_id_t p, int r, void* arg) { jack_set_port_registration_callback(
this->client,
+[](jack_port_id_t p, int r, void* arg) {
auto& self = *(observer_jack*)arg; auto& self = *(observer_jack*)arg;
if (auto port = jack_port_by_id(self.client, p)) if (auto port = jack_port_by_id(self.client, p))
{ {
self.on_port_callback(port, r != 0); self.on_port_callback(port, r != 0);
} }
}, this); },
this);
jack_set_port_rename_callback( jack_set_port_rename_callback(
this->client, this->client,
@@ -193,21 +190,20 @@ public:
auto port = jack_port_by_id(self.client, p); auto port = jack_port_by_id(self.client, p);
if (!port) if (!port)
return; return;
}, this); },
this);
} }
libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_MIDI; } libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_MIDI; }
std::vector<libremidi::input_port> get_input_ports() const noexcept override std::vector<libremidi::input_port> get_input_ports() const noexcept override
{ {
return get_ports<true, libremidi::API::JACK_MIDI>( return get_ports<true>(this->client, nullptr, port_type, JackPortIsOutput, false);
this->client, nullptr, port_type, JackPortIsOutput, false);
} }
std::vector<libremidi::output_port> get_output_ports() const noexcept override std::vector<libremidi::output_port> get_output_ports() const noexcept override
{ {
return get_ports<false, libremidi::API::JACK_MIDI>( return get_ports<false>(this->client, nullptr, port_type, JackPortIsInput, false);
this->client, nullptr, port_type, JackPortIsInput, false);
} }
~observer_jack() ~observer_jack()
@@ -8,7 +8,7 @@
#include <variant> #include <variant>
NAMESPACE_LIBREMIDI::jack namespace libremidi::jack
{ {
// Create a JACK client which will be shared across objects // 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); client = jack_client_open(v.data(), JackNoStartServer, &status);
assert(client); assert(client);
assert(status == 0); assert(status == 0);
jack_set_process_callback(client, +[](jack_nframes_t cnt, void* ctx) -> int { jack_set_process_callback(
client,
+[](jack_nframes_t cnt, void* ctx) -> int {
((shared_handler*)ctx)->jack_callback(cnt); ((shared_handler*)ctx)->jack_callback(cnt);
return 0; return 0;
}, this); },
this);
} }
virtual void start_processing() override { jack_activate(client); } virtual void start_processing() override { jack_activate(client); }
@@ -70,10 +73,11 @@ struct shared_handler : public libremidi::shared_context
switch (ev.type) switch (ev.type)
{ {
case in_callback_added: 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; break;
case in_callback_removed: { 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();) for (auto it = midiin_callbacks.begin(); it != midiin_callbacks.end();)
{ {
if (it->token == idx) if (it->token == idx)
@@ -89,10 +93,11 @@ struct shared_handler : public libremidi::shared_context
break; break;
} }
case out_callback_added: 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; break;
case out_callback_removed: 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();) for (auto it = midiout_callbacks.begin(); it != midiout_callbacks.end();)
{ {
if (it->token == idx) if (it->token == idx)
@@ -136,7 +141,7 @@ struct shared_handler : public libremidi::shared_context
struct event struct event
{ {
event_type type; 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}; boost::lockfree::spsc_queue<event> events{16};
+1 -1
View File
@@ -5,7 +5,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI::jack_ump namespace libremidi::jack_ump
{ {
struct backend struct backend
{ {
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <libremidi/backends/jack/config.hpp> #include <libremidi/backends/jack/config.hpp>
NAMESPACE_LIBREMIDI::jack_ump namespace libremidi::jack_ump
{ {
struct input_configuration struct input_configuration
{ {
@@ -4,7 +4,7 @@
#include <libremidi/detail/midi_in.hpp> #include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI::jack_ump namespace libremidi::jack_ump
{ {
class midi_in_jack final class midi_in_jack final
: public midi2::in_api : public midi2::in_api
@@ -3,7 +3,7 @@
#include <libremidi/backends/jack_ump/config.hpp> #include <libremidi/backends/jack_ump/config.hpp>
#include <libremidi/detail/midi_out.hpp> #include <libremidi/detail/midi_out.hpp>
NAMESPACE_LIBREMIDI::jack_ump namespace libremidi::jack_ump
{ {
class midi_out_jack class midi_out_jack
: public midi2::out_api : public midi2::out_api
@@ -170,7 +170,7 @@ public:
}; };
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <> template <>
inline std::unique_ptr<midi_out_api> inline std::unique_ptr<midi_out_api>
@@ -5,7 +5,7 @@
#include <unordered_set> #include <unordered_set>
NAMESPACE_LIBREMIDI::jack_ump namespace libremidi::jack_ump
{ {
class observer_jack final class observer_jack final
: public observer_api : public observer_api
@@ -77,8 +77,7 @@ public:
{ {
seen_input_ports.insert(ports[i]); seen_input_ports.insert(ports[i]);
if (this->configuration.input_added && configuration.notify_in_constructor) if (this->configuration.input_added && configuration.notify_in_constructor)
this->configuration.input_added( this->configuration.input_added(to_port_info<true>(client, port));
to_port_info<true, libremidi::API::JACK_UMP>(client, port));
} }
i++; i++;
} }
@@ -115,8 +114,7 @@ public:
{ {
seen_output_ports.insert(ports[i]); seen_output_ports.insert(ports[i]);
if (this->configuration.output_added && configuration.notify_in_constructor) if (this->configuration.output_added && configuration.notify_in_constructor)
this->configuration.output_added( this->configuration.output_added(to_port_info<false>(client, port));
to_port_info<false, libremidi::API::JACK_UMP>(client, port));
} }
i++; i++;
} }
@@ -156,15 +154,13 @@ public:
{ {
seen_input_ports.insert(name); seen_input_ports.insert(name);
if (this->configuration.input_added) if (this->configuration.input_added)
this->configuration.input_added( this->configuration.input_added(to_port_info<true>(client, port));
to_port_info<true, libremidi::API::JACK_UMP>(client, port));
} }
else if (flags & JackPortIsInput) else if (flags & JackPortIsInput)
{ {
seen_output_ports.insert(name); seen_output_ports.insert(name);
if (this->configuration.output_added) if (this->configuration.output_added)
this->configuration.output_added( this->configuration.output_added(to_port_info<false>(client, port));
to_port_info<false, libremidi::API::JACK_UMP>(client, port));
} }
} }
else else
@@ -172,15 +168,13 @@ public:
if (auto it = seen_input_ports.find(name); it != seen_input_ports.end()) if (auto it = seen_input_ports.find(name); it != seen_input_ports.end())
{ {
if (this->configuration.input_removed) if (this->configuration.input_removed)
this->configuration.input_removed( this->configuration.input_removed(to_port_info<true>(client, port));
to_port_info<true, libremidi::API::JACK_UMP>(client, port));
seen_input_ports.erase(it); seen_input_ports.erase(it);
} }
if (auto it = seen_output_ports.find(name); it != seen_output_ports.end()) if (auto it = seen_output_ports.find(name); it != seen_output_ports.end())
{ {
if (this->configuration.output_removed) if (this->configuration.output_removed)
this->configuration.output_removed( this->configuration.output_removed(to_port_info<false>(client, port));
to_port_info<false, libremidi::API::JACK_UMP>(client, port));
seen_output_ports.erase(it); seen_output_ports.erase(it);
} }
} }
@@ -216,14 +210,12 @@ public:
std::vector<libremidi::input_port> get_input_ports() const noexcept override std::vector<libremidi::input_port> get_input_ports() const noexcept override
{ {
return get_ports<true, libremidi::API::JACK_UMP>( return get_ports<true>(this->client, nullptr, port_type, JackPortIsOutput, true);
this->client, nullptr, port_type, JackPortIsOutput, true);
} }
std::vector<libremidi::output_port> get_output_ports() const noexcept override std::vector<libremidi::output_port> get_output_ports() const noexcept override
{ {
return get_ports<false, libremidi::API::JACK_UMP>( return get_ports<false>(this->client, nullptr, port_type, JackPortIsInput, true);
this->client, nullptr, port_type, JackPortIsInput, true);
} }
~observer_jack() ~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> #include <string_view>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct kbd_backend struct kbd_backend
{ {
@@ -4,7 +4,7 @@
#include <functional> #include <functional>
#include <map> #include <map>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
enum kbd_event enum kbd_event
{ {
@@ -36,7 +36,7 @@ enum kbd_event
// actually based on macOS virtual codes as it seems // actually based on macOS virtual codes as it seems
// impossible to get raw scan codes. // impossible to get raw scan codes.
// https://eastmanreference.com/complete-list-of-applescript-key-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{ static const std::map<int, int> ret{
{ 0, NOTE_0}, // C0 { 0, NOTE_0}, // C0
{ 13, NOTE_0 + 1}, { 13, NOTE_0 + 1},
@@ -83,7 +83,7 @@ LIBREMIDI_STATIC const std::map<int, int>& scancode_map_macos(){
// Use: https://kbdlayout.info/KBDUSX/scancodes // Use: https://kbdlayout.info/KBDUSX/scancodes
// Also valid for windows // 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{ static const std::map<int, int> ret{
{ 0x1E, NOTE_0}, // C0 { 0x1E, NOTE_0}, // C0
{ 0x11, NOTE_0 + 1}, { 0x11, NOTE_0 + 1},
@@ -5,7 +5,7 @@
#include <chrono> #include <chrono>
#include <unordered_map> #include <unordered_map>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_in_kbd final class midi_in_kbd final
: public midi1::in_api : 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) #if __has_include(<alsa/ump.h>) && SND_LIB_VERSION >= ((1 << 16) | (2 << 8) | 10)
#define LIBREMIDI_ALSA_HAS_UMP 1 #define LIBREMIDI_ALSA_HAS_UMP 1
#endif #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 #endif
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct libasound struct libasound
@@ -282,7 +278,6 @@ struct libasound
LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_sizeof) LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_free) LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_free)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_malloc) 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_dest)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_sender) LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_sender)
LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_time_real) 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, set_queue_tempo)
LIBREMIDI_SYMBOL_INIT(snd_seq, subscribe_port) LIBREMIDI_SYMBOL_INIT(snd_seq, subscribe_port)
LIBREMIDI_SYMBOL_INIT(snd_seq, unsubscribe_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}; bool available{true};
@@ -347,7 +336,6 @@ struct libasound
LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_sizeof) LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_free) LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_free)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_malloc) 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_dest)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_sender) LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_sender)
LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_time_real) 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, subscribe_port)
LIBREMIDI_SYMBOL_DEF(snd_seq, unsubscribe_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 #if LIBREMIDI_ALSA_HAS_UMP
struct ump_t struct ump_t
{ {
@@ -380,6 +362,7 @@ struct libasound
available = false; available = false;
return; 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_input)
LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output) LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output)
LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output_direct) LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output_direct)
@@ -387,10 +370,10 @@ struct libasound
bool available{true}; 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_input)
LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output) LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output)
LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output_direct) LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output_direct)
} ump; } ump;
#endif #endif
} seq{library}; } seq{library};
@@ -406,98 +389,39 @@ struct libasound
return; return;
} }
// Core UMP functions LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_name)
LIBREMIDI_SYMBOL_INIT(snd_ump, open) LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_sizeof)
LIBREMIDI_SYMBOL_INIT(snd_ump, close) 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)
LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params) LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params)
LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params_current) LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params_current)
LIBREMIDI_SYMBOL_INIT(snd_ump, read) LIBREMIDI_SYMBOL_INIT(snd_ump, read)
LIBREMIDI_SYMBOL_INIT(snd_ump, tread) LIBREMIDI_SYMBOL_INIT(snd_ump, tread)
LIBREMIDI_SYMBOL_INIT(snd_ump, write) 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}; bool available{true};
LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_name)
// Core UMP functions LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_sizeof)
LIBREMIDI_SYMBOL_DEF(snd_ump, open)
LIBREMIDI_SYMBOL_DEF(snd_ump, close) 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)
LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params) LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params)
LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params_current) LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params_current)
LIBREMIDI_SYMBOL_DEF(snd_ump, read) LIBREMIDI_SYMBOL_DEF(snd_ump, read)
LIBREMIDI_SYMBOL_DEF(snd_ump, tread) LIBREMIDI_SYMBOL_DEF(snd_ump, tread)
LIBREMIDI_SYMBOL_DEF(snd_ump, write) 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}; } ump{library};
#endif #endif
}; };
@@ -525,8 +449,6 @@ struct libasound
#define snd_seq_client_info_alloca(ptr) snd_dylib_alloca(ptr, seq, client_info) #define snd_seq_client_info_alloca(ptr) snd_dylib_alloca(ptr, seq, client_info)
#undef snd_seq_port_info_alloca #undef snd_seq_port_info_alloca
#define snd_seq_port_info_alloca(ptr) snd_dylib_alloca(ptr, seq, port_info) #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 #undef snd_seq_queue_tempo_alloca
#define snd_seq_queue_tempo_alloca(ptr) snd_dylib_alloca(ptr, seq, queue_tempo) #define snd_seq_queue_tempo_alloca(ptr) snd_dylib_alloca(ptr, seq, queue_tempo)
@@ -1,11 +1,10 @@
#pragma once #pragma once
#include <libremidi/config.hpp>
#if __has_include(<dlfcn.h>) #if __has_include(<dlfcn.h>)
#include <dlfcn.h> #include <dlfcn.h>
#include <cassert> #include <cassert>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class dylib_loader class dylib_loader
{ {
+1 -1
View File
@@ -7,7 +7,7 @@
#include <iostream> #include <iostream>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct eventfd_notifier struct eventfd_notifier
{ {
@@ -4,7 +4,7 @@
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
#pragma GCC diagnostic push #pragma GCC diagnostic push
+7 -42
View File
@@ -8,7 +8,7 @@
#include <cassert> #include <cassert>
#include <optional> #include <optional>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
struct libudev struct libudev
@@ -149,10 +149,7 @@ struct udev_soundcard_info
{ {
container_identifier container; container_identifier container;
device_identifier path; device_identifier path;
transport_type type{}; port_information::port_type type{};
std::string vendor;
std::string product;
std::string serial;
}; };
inline udev_soundcard_info get_udev_soundcard_info(const udev_helper& helper, int card) 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 const auto usb_device
= udev.device_get_parent_with_subsystem_devtype(dev, "usb", "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) if (usb_device)
type = (transport_type)(type | transport_type::usb); type = (port_information::port_type)(type | port_information::port_type::usb);
else if (pci_device) else
type = (transport_type)(type | transport_type::pci); type = (port_information::port_type)(type | port_information::port_type::pci);
auto id_path = udev.device_get_property_value(dev, "ID_PATH"); auto id_path = udev.device_get_property_value(dev, "ID_PATH");
if (!id_path) if (!id_path)
id_path = ""; id_path = "";
auto read_property = [&](const char* property) { auto ret = udev_soundcard_info{.container = id_path, .path = path, .type = type};
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};
udev.device_unref(dev); udev.device_unref(dev);
return ret; return ret;
} }
+2 -5
View File
@@ -2,15 +2,12 @@
#include <libremidi/config.hpp> #include <libremidi/config.hpp>
#include <string> #include <string>
#if !defined(BOOST_ASIO_IO_CONTEXT_HPP)
namespace boost::asio namespace boost::asio
{ {
struct io_context; struct io_context;
} }
#endif
NAMESPACE_LIBREMIDI::net namespace libremidi::net
{ {
enum class protocol enum class protocol
{ {
@@ -49,7 +46,7 @@ struct dgram_observer_configuration
} }
NAMESPACE_LIBREMIDI::net_ump namespace libremidi::net_ump
{ {
enum class protocol enum class protocol
{ {
+1 -1
View File
@@ -35,7 +35,7 @@ public:
*/ */
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <typename T> template <typename T>
struct optionally_owned struct optionally_owned
+3 -3
View File
@@ -5,7 +5,7 @@
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
#include <thread> #include <thread>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <typename Impl, typename F> template <typename Impl, typename F>
struct osc_parser struct osc_parser
@@ -115,7 +115,7 @@ struct osc_parser
} }
NAMESPACE_LIBREMIDI::net namespace libremidi::net
{ {
struct osc_parser_midi1 struct osc_parser_midi1
@@ -264,7 +264,7 @@ public:
}; };
} }
NAMESPACE_LIBREMIDI::net_ump namespace libremidi::net_ump
{ {
struct osc_parser_midi2 struct osc_parser_midi2
{ {
+2 -2
View File
@@ -8,7 +8,7 @@
#include <boost/asio/ip/udp.hpp> #include <boost/asio/ip/udp.hpp>
#include <boost/endian.hpp> #include <boost/endian.hpp>
NAMESPACE_LIBREMIDI::net namespace libremidi::net
{ {
struct osc_midi1_packet struct osc_midi1_packet
@@ -146,7 +146,7 @@ public:
} }
NAMESPACE_LIBREMIDI::net_ump namespace libremidi::net_ump
{ {
struct osc_midi2_packet struct osc_midi2_packet
{ {
+2 -2
View File
@@ -1,13 +1,13 @@
#pragma once #pragma once
#include <libremidi/backends/dummy.hpp> #include <libremidi/backends/dummy.hpp>
NAMESPACE_LIBREMIDI::net namespace libremidi::net
{ {
using net_observer_configuration = libremidi::dummy_configuration; using net_observer_configuration = libremidi::dummy_configuration;
using observer = libremidi::observer_dummy; using observer = libremidi::observer_dummy;
} }
NAMESPACE_LIBREMIDI::net_ump namespace libremidi::net_ump
{ {
using net_observer_configuration = libremidi::dummy_configuration; using net_observer_configuration = libremidi::dummy_configuration;
using observer = libremidi::observer_dummy; using observer = libremidi::observer_dummy;
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI::net namespace libremidi::net
{ {
struct backend struct backend
{ {
+1 -1
View File
@@ -6,7 +6,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI::net_ump namespace libremidi::net_ump
{ {
struct backend struct backend
{ {
+1 -1
View File
@@ -9,7 +9,7 @@
#include <string_view> #include <string_view>
NAMESPACE_LIBREMIDI::pipewire namespace libremidi::pipewire
{ {
struct backend struct backend
{ {
@@ -12,7 +12,7 @@ struct pw_filter;
struct spa_io_position; struct spa_io_position;
} }
NAMESPACE_LIBREMIDI namespace libremidi
{ {
using pipewire_callback_function = std::function<void(spa_io_position*)>; using pipewire_callback_function = std::function<void(spa_io_position*)>;
struct pipewire_callback struct pipewire_callback
@@ -25,7 +25,7 @@
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers" #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
NAMESPACE_LIBREMIDI namespace libremidi
{ {
template <typename K, typename V> template <typename K, typename V>
using hash_map = std::unordered_map<K, V>; using hash_map = std::unordered_map<K, V>;
@@ -269,7 +269,7 @@ struct pipewire_context
.done = .done =
[](void* object, uint32_t id, int seq) { [](void* object, uint32_t id, int seq) {
auto& self = *(pipewire_context*)object; auto& self = *(pipewire_context*)object;
if (id == PW_ID_CORE && seq == self.pending) if(id == PW_ID_CORE && seq == self.pending)
{ {
self.done = 1; self.done = 1;
libpipewire::instance().main_loop_quit(self.main_loop); libpipewire::instance().main_loop_quit(self.main_loop);
@@ -11,12 +11,7 @@
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#if defined(__clang__) namespace libremidi
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#endif
NAMESPACE_LIBREMIDI
{ {
struct pipewire_helpers struct pipewire_helpers
{ {
@@ -222,7 +217,6 @@ struct pipewire_helpers
return stdx::error{}; return stdx::error{};
} }
template <libremidi::API Api>
void add_callbacks(std::string format, const observer_configuration& conf) void add_callbacks(std::string format, const observer_configuration& conf)
{ {
assert(global_context); assert(global_context);
@@ -238,12 +232,12 @@ struct pipewire_helpers
if (port.direction == SPA_DIRECTION_INPUT) if (port.direction == SPA_DIRECTION_INPUT)
{ {
if (conf.output_added) 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 else
{ {
if (conf.input_added) 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 (port.direction == SPA_DIRECTION_INPUT)
{ {
if (conf.output_removed) 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 else
{ {
if (conf.input_removed) 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{}; 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) static auto to_port_info(const pipewire_context::port_info& port)
-> std::conditional_t<Direction == SPA_DIRECTION_OUTPUT, input_port, output_port> -> std::conditional_t<Direction == SPA_DIRECTION_OUTPUT, input_port, output_port>
{ {
@@ -430,7 +424,6 @@ struct pipewire_helpers
} }
return {{ return {{
.api = Api,
.client = 0, .client = 0,
.port = port.id, .port = port.id,
.manufacturer = "", .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) // 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 // 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( static auto get_ports(
std::string_view format, const observer_configuration& conf, std::string_view format, const observer_configuration& conf,
const pipewire_context& ctx) noexcept const pipewire_context& ctx) noexcept
@@ -461,7 +454,7 @@ struct pipewire_helpers
(Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs)) (Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
{ {
if (port.format.find(format) != std::string::npos) 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)) (Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
{ {
if (port.format.find(format) != std::string::npos) 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_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp> #include <libremidi/detail/midi_stream_decoder.hpp>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_in_pipewire final class midi_in_pipewire final
: public midi1::in_api : public midi1::in_api
@@ -74,7 +74,10 @@ public:
return do_close_port(); 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(); } timestamp absolute_timestamp() const noexcept override { return system_ns(); }
@@ -5,7 +5,7 @@
#include <readerwriterqueue.h> #include <readerwriterqueue.h>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class midi_out_pipewire class midi_out_pipewire
: public midi1::out_api : public midi1::out_api
@@ -79,7 +79,10 @@ public:
return do_close_port(); 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) int process(spa_io_position* pos)
{ {
@@ -5,7 +5,7 @@
#include <unordered_set> #include <unordered_set>
NAMESPACE_LIBREMIDI namespace libremidi
{ {
class observer_pipewire final class observer_pipewire final
: public observer_api : public observer_api
@@ -19,15 +19,14 @@ public:
{ {
} configuration; } configuration;
explicit observer_pipewire( explicit observer_pipewire(observer_configuration&& conf, pipewire_observer_configuration&& apiconf)
observer_configuration&& conf, pipewire_observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)} : configuration{std::move(conf), std::move(apiconf)}
{ {
create_context(*this); create_context(*this);
// FIXME port rename callback // FIXME port rename callback
{ {
this->add_callbacks<libremidi::API::PIPEWIRE>("midi", configuration); this->add_callbacks("midi", configuration);
this->start_thread(); this->start_thread();
} }
@@ -47,14 +46,12 @@ public:
std::vector<libremidi::input_port> get_input_ports() const noexcept override std::vector<libremidi::input_port> get_input_ports() const noexcept override
{ {
return get_ports<SPA_DIRECTION_OUTPUT, libremidi::API::PIPEWIRE>( return get_ports<SPA_DIRECTION_OUTPUT>("midi", this->configuration, *this->global_context);
"midi", this->configuration, *this->global_context);
} }
std::vector<libremidi::output_port> get_output_ports() const noexcept override std::vector<libremidi::output_port> get_output_ports() const noexcept override
{ {
return get_ports<SPA_DIRECTION_INPUT, libremidi::API::PIPEWIRE>( return get_ports<SPA_DIRECTION_INPUT>("midi", this->configuration, *this->global_context);
"midi", this->configuration, *this->global_context);
} }
~observer_pipewire() ~observer_pipewire()
@@ -8,7 +8,7 @@
#include <variant> #include <variant>
NAMESPACE_LIBREMIDI::pipewire namespace libremidi::pipewire
{ {
// Create a PipeWire client which will be shared across objects // 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); client = pipewire_client_open(v.data(), PipewireNoStartServer, &status);
assert(client); assert(client);
assert(status == 0); assert(status == 0);
pipewire_set_process_callback(client, +[](pipewire_nframes_t cnt, void* ctx) -> int { pipewire_set_process_callback(
client,
+[](pipewire_nframes_t cnt, void* ctx) -> int {
((shared_handler*)ctx)->pipewire_callback(cnt); ((shared_handler*)ctx)->pipewire_callback(cnt);
return 0; return 0;
}, this); },
this);
} }
virtual void start_processing() override { pipewire_activate(client); } virtual void start_processing() override { pipewire_activate(client); }
@@ -71,10 +74,10 @@ struct shared_handler : public libremidi::shared_context
{ {
case in_callback_added: case in_callback_added:
midiin_callbacks.push_back( midiin_callbacks.push_back(
std::move(*get_if<libremidi::pipewire_callback>(&ev.payload))); std::move(*std::get_if<libremidi::pipewire_callback>(&ev.payload)));
break; break;
case in_callback_removed: { 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();) for (auto it = midiin_callbacks.begin(); it != midiin_callbacks.end();)
{ {
if (it->token == idx) if (it->token == idx)
@@ -91,10 +94,10 @@ struct shared_handler : public libremidi::shared_context
} }
case out_callback_added: case out_callback_added:
midiout_callbacks.push_back( midiout_callbacks.push_back(
std::move(*get_if<libremidi::pipewire_callback>(&ev.payload))); std::move(*std::get_if<libremidi::pipewire_callback>(&ev.payload)));
break; break;
case out_callback_removed: 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();) for (auto it = midiout_callbacks.begin(); it != midiout_callbacks.end();)
{ {
if (it->token == idx) if (it->token == idx)
@@ -138,7 +141,7 @@ struct shared_handler : public libremidi::shared_context
struct event struct event
{ {
event_type type; 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}; boost::lockfree::spsc_queue<event> events{16};

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