Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.22 FATAL_ERROR)
project(libremidi
VERSION 5.0.1
VERSION 5.1.0
DESCRIPTION "A cross-platform MIDI library"
LANGUAGES C CXX
HOMEPAGE_URL "https://github.com/jcelerier/libremidi"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ If you use this work as part of academic research, please kindly cite the [paper

## Changelog

### Since v5.1

* Report USB device identifiers with ALSA and udev
* PipeWire and JACK UMP support (requires PipeWire v1.4+)

### Since v5
* Use stdx::error for error reporting until C++26 and std::error are widely available :-)
* Hunt exceptions down
Expand Down
10 changes: 10 additions & 0 deletions bindings/python/pylibremidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ NB_MODULE(pylibremidi, m) {
.value("WINDOWS_MIDI_SERVICES", libremidi::API::WINDOWS_MIDI_SERVICES)
.value("KEYBOARD_UMP", libremidi::API::KEYBOARD_UMP)
.value("NETWORK_UMP", libremidi::API::NETWORK_UMP)
.value("JACK_UMP", libremidi::API::JACK_UMP)
.value("PIPEWIRE_UMP", libremidi::API::PIPEWIRE_UMP)

.value("DUMMY", libremidi::API::DUMMY)
.export_values();
Expand Down Expand Up @@ -345,10 +347,12 @@ NB_MODULE(pylibremidi, m) {
nb::class_<libremidi::coremidi_ump::input_configuration>(m, "CoremidiUmpInputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::coremidi_ump::input_configuration::client_name);
nb::class_<libremidi::emscripten_input_configuration>(m, "EmscriptenInputConfiguration").def(nb::init<>());
nb::class_<libremidi::jack_input_configuration>(m, "JackInputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::jack_input_configuration::client_name);
nb::class_<libremidi::jack_ump::input_configuration>(m, "JackUmpInputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::jack_ump::input_configuration::client_name);
nb::class_<libremidi::kbd_input_configuration>(m, "KeyboardInputConfiguration").def(nb::init<>());
nb::class_<libremidi::net::dgram_input_configuration>(m, "DatagramInputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::net::dgram_input_configuration::client_name);
nb::class_<libremidi::net_ump::dgram_input_configuration>(m, "DatagramUmpInputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::net_ump::dgram_input_configuration::client_name);
nb::class_<libremidi::pipewire_input_configuration>(m, "PipewireInputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::pipewire_input_configuration::client_name);
nb::class_<libremidi::pipewire_ump::input_configuration>(m, "PipewireUmpInputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::pipewire_ump::input_configuration::client_name);
nb::class_<libremidi::winmidi::input_configuration>(m, "WinmidiInputConfiguration").def(nb::init<>());
nb::class_<libremidi::winmm_input_configuration>(m, "WinmmInputConfiguration").def(nb::init<>());
nb::class_<libremidi::winuwp_input_configuration>(m, "WinuwpInputConfiguration").def(nb::init<>());
Expand All @@ -361,9 +365,11 @@ NB_MODULE(pylibremidi, m) {
nb::class_<libremidi::coremidi_ump::output_configuration>(m, "CoremidiUmpOutputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::coremidi_ump::output_configuration::client_name);
nb::class_<libremidi::emscripten_output_configuration>(m, "EmscriptenOutputConfiguration").def(nb::init<>());
nb::class_<libremidi::jack_output_configuration>(m, "JackOutputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::jack_output_configuration::client_name);
nb::class_<libremidi::jack_ump::output_configuration>(m, "JackUmpOutputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::jack_ump::output_configuration::client_name);
nb::class_<libremidi::net::dgram_output_configuration>(m, "DatagramOutputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::net::dgram_output_configuration::client_name);
nb::class_<libremidi::net_ump::dgram_output_configuration>(m, "DatagramUmpOutputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::net_ump::dgram_output_configuration::client_name);
nb::class_<libremidi::pipewire_output_configuration>(m, "PipewireOutputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::pipewire_output_configuration::client_name);
nb::class_<libremidi::pipewire_ump::output_configuration>(m, "PipewireUmpOutputConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::pipewire_ump::output_configuration::client_name);
nb::class_<libremidi::winmidi::output_configuration>(m, "WinmidiOutputConfiguration").def(nb::init<>());
nb::class_<libremidi::winmm_output_configuration>(m, "WinmmOutputConfiguration").def(nb::init<>());
nb::class_<libremidi::winuwp_output_configuration>(m, "WinuwpOutputConfiguration").def(nb::init<>());
Expand All @@ -380,11 +386,15 @@ NB_MODULE(pylibremidi, m) {
.def_rw("client_name", &libremidi::coremidi_ump::observer_configuration::client_name);
nb::class_<libremidi::emscripten_observer_configuration>(m, "EmscriptenObserverConfiguration").def(nb::init<>());
nb::class_<libremidi::jack_observer_configuration>(m, "JackObserverConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::jack_observer_configuration::client_name);
nb::class_<libremidi::jack_ump::observer_configuration>(m, "JackUmpObserverConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::jack_ump::observer_configuration::client_name);
nb::class_<libremidi::net::dgram_observer_configuration>(m, "DatagramObserverConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::net::dgram_observer_configuration::client_name);
nb::class_<libremidi::net_ump::dgram_observer_configuration>(m, "DatagramUmpObserverConfiguration")
.def(nb::init<>())
.def_rw("client_name", &libremidi::net_ump::dgram_observer_configuration::client_name);
nb::class_<libremidi::pipewire_observer_configuration>(m, "PipewireObserverConfiguration").def(nb::init<>()).def_rw("client_name", &libremidi::pipewire_observer_configuration::client_name);
nb::class_<libremidi::pipewire_ump::observer_configuration>(m, "PipewireObserverConfiguration")
.def(nb::init<>())
.def_rw("client_name", &libremidi::pipewire_ump::observer_configuration::client_name);
nb::class_<libremidi::winmidi::observer_configuration>(m, "WinmidiObserverConfiguration").def(nb::init<>());
nb::class_<libremidi::winmm_observer_configuration>(m, "WinmmObserverConfiguration").def(nb::init<>());
nb::class_<libremidi::winuwp_observer_configuration>(m, "WinuwpObserverConfiguration").def(nb::init<>());
Expand Down
7 changes: 7 additions & 0 deletions cmake/libremidi.examples.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,19 @@ endif()

if(LIBREMIDI_HAS_JACK)
add_example(jack_share)
add_backend_example(midi2_in_jack)
add_backend_example(midi2_out_jack)
endif()

if(LIBREMIDI_HAS_PIPEWIRE)
add_example(pipewire_share)
add_backend_example(midi1_in_pipewire)
add_backend_example(midi1_out_pipewire)

if(LIBREMIDI_HAS_PIPEWIRE_UMP)
add_backend_example(midi2_in_pipewire)
add_backend_example(midi2_out_pipewire)
endif()
endif()

if(LIBREMIDI_HAS_COREMIDI)
Expand Down
6 changes: 6 additions & 0 deletions cmake/libremidi.pipewire.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ if(PIPEWIRE_INCLUDEDIR AND SPA_INCLUDEDIR)
message(STATUS "libremidi: using PipeWire")
set(LIBREMIDI_HAS_PIPEWIRE 1)

set(CMAKE_REQUIRED_INCLUDES "${SPA_INCLUDEDIR}/spa-0.2")
check_cxx_source_compiles("#include <spa/control/control.h>\nint main() { return sizeof(SPA_CONTROL_UMP); }" LIBREMIDI_HAS_PIPEWIRE_UMP)

unset(CMAKE_REQUIRED_INCLUDES)

target_compile_options(libremidi
${_public}
$<$<BOOL:${LIBREMIDI_CXX_HAS_WNO_GNU_STATEMENT}>:-Wno-gnu-statement-expression-from-macro-expansion>
Expand All @@ -30,6 +35,7 @@ if(PIPEWIRE_INCLUDEDIR AND SPA_INCLUDEDIR)
target_compile_definitions(libremidi
${_public}
LIBREMIDI_PIPEWIRE
$<$<BOOL:${LIBREMIDI_HAS_PIPEWIRE_UMP}>:LIBREMIDI_PIPEWIRE_UMP>
)
target_include_directories(libremidi SYSTEM
${_public}
Expand Down
46 changes: 28 additions & 18 deletions cmake/libremidi.sources.cmake
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
target_sources(libremidi PRIVATE
include/libremidi/backends/alsa_seq/config.hpp
include/libremidi/backends/alsa_seq/helpers.hpp
include/libremidi/backends/alsa_seq/midi_in.hpp
include/libremidi/backends/alsa_seq/midi_out.hpp
include/libremidi/backends/alsa_seq/observer.hpp
include/libremidi/backends/alsa_seq/shared_handler.hpp

include/libremidi/backends/alsa_raw/config.hpp
include/libremidi/backends/alsa_raw/helpers.hpp
include/libremidi/backends/alsa_raw/midi_in.hpp
include/libremidi/backends/alsa_raw/midi_out.hpp
include/libremidi/backends/alsa_raw/observer.hpp
include/libremidi/backends/alsa_raw.hpp

include/libremidi/backends/alsa_raw_ump/config.hpp
include/libremidi/backends/alsa_raw_ump/helpers.hpp
include/libremidi/backends/alsa_raw_ump/midi_in.hpp
include/libremidi/backends/alsa_raw_ump/midi_out.hpp
include/libremidi/backends/alsa_raw_ump/observer.hpp
include/libremidi/backends/alsa_raw_ump.hpp

include/libremidi/backends/alsa_seq/config.hpp
include/libremidi/backends/alsa_seq/helpers.hpp
include/libremidi/backends/alsa_seq/midi_in.hpp
include/libremidi/backends/alsa_seq/midi_out.hpp
include/libremidi/backends/alsa_seq/observer.hpp
include/libremidi/backends/alsa_seq/shared_handler.hpp
include/libremidi/backends/alsa_seq.hpp

include/libremidi/backends/alsa_seq_ump/config.hpp
include/libremidi/backends/alsa_seq_ump/helpers.hpp
include/libremidi/backends/alsa_seq_ump/midi_out.hpp
include/libremidi/backends/alsa_seq_ump.hpp

include/libremidi/backends/coremidi/config.hpp
include/libremidi/backends/coremidi/helpers.hpp
include/libremidi/backends/coremidi/midi_in.hpp
include/libremidi/backends/coremidi/midi_out.hpp
include/libremidi/backends/coremidi/observer.hpp
include/libremidi/backends/coremidi.hpp

include/libremidi/backends/coremidi_ump/config.hpp
include/libremidi/backends/coremidi_ump/helpers.hpp
include/libremidi/backends/coremidi_ump/midi_in.hpp
include/libremidi/backends/coremidi_ump/midi_out.hpp
include/libremidi/backends/coremidi_ump/observer.hpp
include/libremidi/backends/coremidi_ump.hpp

include/libremidi/backends/dummy.hpp

include/libremidi/backends/emscripten/config.hpp
include/libremidi/backends/emscripten/helpers.hpp
Expand All @@ -44,13 +52,22 @@ target_sources(libremidi PRIVATE
include/libremidi/backends/emscripten/midi_out.hpp
include/libremidi/backends/emscripten/observer.cpp
include/libremidi/backends/emscripten/observer.hpp
include/libremidi/backends/emscripten.hpp

include/libremidi/backends/jack/config.hpp
include/libremidi/backends/jack/error_domain.hpp
include/libremidi/backends/jack/helpers.hpp
include/libremidi/backends/jack/midi_in.hpp
include/libremidi/backends/jack/midi_out.hpp
include/libremidi/backends/jack/observer.hpp
include/libremidi/backends/jack/shared_handler.hpp
include/libremidi/backends/jack.hpp

include/libremidi/backends/jack_ump/config.hpp
include/libremidi/backends/jack_ump/midi_in.hpp
include/libremidi/backends/jack_ump/midi_out.hpp
include/libremidi/backends/jack_ump/observer.hpp
include/libremidi/backends/jack_ump.hpp

include/libremidi/backends/keyboard/config.hpp
include/libremidi/backends/keyboard/midi_in.hpp
Expand All @@ -74,35 +91,28 @@ target_sources(libremidi PRIVATE
include/libremidi/backends/pipewire/midi_out.hpp
include/libremidi/backends/pipewire/observer.hpp
include/libremidi/backends/pipewire/shared_handler.hpp
include/libremidi/backends/pipewire.hpp
include/libremidi/backends/pipewire_ump.hpp

include/libremidi/backends/winmidi/config.hpp
include/libremidi/backends/winmidi/helpers.hpp
include/libremidi/backends/winmidi/midi_in.hpp
include/libremidi/backends/winmidi/midi_out.hpp
include/libremidi/backends/winmidi/observer.hpp
include/libremidi/backends/winmidi.hpp

include/libremidi/backends/winmm/config.hpp
include/libremidi/backends/winmm/helpers.hpp
include/libremidi/backends/winmm/midi_in.hpp
include/libremidi/backends/winmm/midi_out.hpp
include/libremidi/backends/winmm/observer.hpp
include/libremidi/backends/winmm.hpp

include/libremidi/backends/winuwp/config.hpp
include/libremidi/backends/winuwp/helpers.hpp
include/libremidi/backends/winuwp/midi_in.hpp
include/libremidi/backends/winuwp/midi_out.hpp
include/libremidi/backends/winuwp/observer.hpp

include/libremidi/backends/alsa_raw.hpp
include/libremidi/backends/alsa_raw_ump.hpp
include/libremidi/backends/alsa_seq.hpp
include/libremidi/backends/alsa_seq_ump.hpp
include/libremidi/backends/coremidi.hpp
include/libremidi/backends/coremidi_ump.hpp
include/libremidi/backends/dummy.hpp
include/libremidi/backends/emscripten.hpp
include/libremidi/backends/jack.hpp
include/libremidi/backends/winmm.hpp
include/libremidi/backends/winuwp.hpp

include/libremidi/detail/memory.hpp
Expand Down
43 changes: 43 additions & 0 deletions examples/backends/midi2_in_jack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "backend_test_utils.hpp"

#include <libremidi/backends/jack_ump.hpp>
#include <libremidi/libremidi.hpp>

using api = libremidi::jack_ump::backend;

int main(void)
try
{
std::cerr << "API: " << api::name << "\n";

libremidi::observer_configuration obs_config{.track_any = false};
api::midi_observer_configuration obs_api_config;
libremidi::observer obs{obs_config, obs_api_config};

auto ports = obs.get_input_ports();
for (const auto& port : ports)
{
std::cerr << "Port: " << port.display_name << " : " << port.port_name << "; "
<< port.device_name << "\n";
}

{
libremidi::ump_input_configuration in_config;
in_config.on_message = [](const libremidi::ump& m) { std::cerr << m << "\n"; };
api::midi_in_configuration in_api_config;
libremidi::midi_in midiin{in_config, in_api_config};

if (!ports.empty())
{
midiin.open_port(ports[0]);
std::this_thread::sleep_for(std::chrono::seconds(50));
}
}

return 0;
}
catch (const std::exception& error)
{
std::cerr << error.what() << std::endl;
exit(EXIT_FAILURE);
}
43 changes: 43 additions & 0 deletions examples/backends/midi2_in_pipewire.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "backend_test_utils.hpp"

#include <libremidi/backends/pipewire_ump.hpp>
#include <libremidi/libremidi.hpp>

using api = libremidi::pipewire_ump::backend;

int main(void)
try
{
std::cerr << "API: " << api::name << "\n";

libremidi::observer_configuration obs_config{.track_any = false};
api::midi_observer_configuration obs_api_config;
libremidi::observer obs{obs_config, obs_api_config};

auto ports = obs.get_input_ports();
for (const auto& port : ports)
{
std::cerr << "Port: " << port.display_name << " : " << port.port_name << "; "
<< port.device_name << "\n";
}

{
libremidi::ump_input_configuration in_config;
in_config.on_message = [](const libremidi::ump& m) { std::cerr << m << "\n"; };
api::midi_in_configuration in_api_config;
libremidi::midi_in midiin{in_config, in_api_config};

if (!ports.empty())
{
midiin.open_port(ports[0]);
std::this_thread::sleep_for(std::chrono::seconds(50));
}
}

return 0;
}
catch (const std::exception& error)
{
std::cerr << error.what() << std::endl;
exit(EXIT_FAILURE);
}
44 changes: 44 additions & 0 deletions examples/backends/midi2_out_jack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "backend_test_utils.hpp"

#include <libremidi/backends/jack_ump.hpp>
#include <libremidi/libremidi.hpp>

using api = libremidi::jack_ump::backend;

int main(void)
try
{
std::cerr << "API: " << api::name << "\n";

libremidi::observer_configuration obs_config{.track_any = true};
api::midi_observer_configuration obs_api_config;
libremidi::observer obs{obs_config, obs_api_config};

auto ports = obs.get_output_ports();
for (const auto& port : ports)
{
std::cerr << "Port: " << port.port_name << "\n";
}

libremidi::output_configuration out_config;
api::midi_out_configuration out_api_config;
libremidi::midi_out midiout{out_config, out_api_config};

{
midiout.open_virtual_port();

for (int n = 0; n < 100; n++)
{
for (int i = 81; i < 89; i++)
midiout.send_message(libremidi::channel_events::control_change(1, i, rand()));
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}

return 0;
}
catch (const std::exception& error)
{
std::cerr << error.what() << std::endl;
exit(EXIT_FAILURE);
}
Loading