Compare commits
2 Commits
minimal-ui
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 96bd5f30e6 | |||
| bd642576bb |
@@ -1,3 +0,0 @@
|
|||||||
CompileFlags:
|
|
||||||
Add: [-I/home/sebastian/code/viseq/include, -std=c++23]
|
|
||||||
|
|
||||||
@@ -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/*
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -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, ×tamp);
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//
|
//
|
||||||
// *********************************************************************//
|
// *********************************************************************//
|
||||||
|
|
||||||
NAMESPACE_LIBREMIDI
|
namespace libremidi
|
||||||
{
|
{
|
||||||
struct jack_backend
|
struct jack_backend
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -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>(
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)},
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
NAMESPACE_LIBREMIDI
|
namespace libremidi
|
||||||
{
|
{
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct optionally_owned
|
struct optionally_owned
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
NAMESPACE_LIBREMIDI::net
|
namespace libremidi::net
|
||||||
{
|
{
|
||||||
struct backend
|
struct backend
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
NAMESPACE_LIBREMIDI::net_ump
|
namespace libremidi::net_ump
|
||||||
{
|
{
|
||||||
struct backend
|
struct backend
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user