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
4 changes: 2 additions & 2 deletions .github/workflows/build_cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ jobs:
examples: 1
}
- {
name: "Ubuntu clang-20 libstdc++",
name: "Ubuntu clang-22 libstdc++",
os: ubuntu-latest,
generator: "",
cmakeflags: "-DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_CXX_FLAGS='-Werror=return-type -fsanitize=address -fsanitize=undefined' -DBOOST_ROOT=$PWD/boost_1_90_0",
cmakeflags: "-DCMAKE_CXX_COMPILER=clang++-22 -DCMAKE_CXX_FLAGS='-Werror=return-type -fsanitize=address -fsanitize=undefined' -DBOOST_ROOT=$PWD/boost_1_90_0",
test_target: "test",
tests: 1,
examples: 1
Expand Down
2 changes: 2 additions & 0 deletions cmake/libremidi.examples.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ endif()

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

if(LIBREMIDI_HAS_PIPEWIRE_UMP)
add_backend_example(midi2_in_pipewire)
Expand Down
15 changes: 12 additions & 3 deletions cmake/libremidi.sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,15 @@ target_sources(libremidi PRIVATE
include/libremidi/backends/linux/alsa.hpp
include/libremidi/backends/linux/dylib_loader.hpp
include/libremidi/backends/linux/helpers.hpp
include/libremidi/backends/linux/pipewire.hpp
include/libremidi/backends/linux/pipewire/context.hpp
include/libremidi/backends/linux/pipewire/drm_modifiers.hpp
include/libremidi/backends/linux/pipewire/filter.hpp
include/libremidi/backends/linux/pipewire/format.hpp
include/libremidi/backends/linux/pipewire/instance.hpp
include/libremidi/backends/linux/pipewire/loader.hpp
include/libremidi/backends/linux/pipewire/stream.hpp
include/libremidi/backends/linux/pipewire/subscription.hpp
include/libremidi/backends/linux/pipewire/types.hpp
include/libremidi/backends/linux/udev.hpp

include/libremidi/backends/net/config.hpp
Expand All @@ -88,12 +96,10 @@ target_sources(libremidi PRIVATE
include/libremidi/backends/net/observer.hpp

include/libremidi/backends/pipewire/config.hpp
include/libremidi/backends/pipewire/context.hpp
include/libremidi/backends/pipewire/helpers.hpp
include/libremidi/backends/pipewire/midi_in.hpp
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

Expand Down Expand Up @@ -164,6 +170,9 @@ if(NOT LIBREMIDI_HEADER_ONLY AND NOT LIBREMIDI_MODULE_BUILD)
include/libremidi/backends/emscripten/midi_out.cpp
include/libremidi/backends/emscripten/observer.cpp

include/libremidi/backends/linux/pipewire/instance.cpp
include/libremidi/backends/linux/pipewire/context.cpp

include/libremidi/libremidi-c.cpp
)
endif()
37 changes: 17 additions & 20 deletions examples/backends/midi1_in_pipewire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,28 @@ using api = libremidi::pipewire::backend;
int main(void)
try
{
for (int i = 0; i < 100; i++)
{
std::cerr << "API: " << api::name << "\n";
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};
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_input_ports();
for (const auto& port : ports)
{
std::cerr << "Port: " << port.display_name << "\n";
}
auto ports = obs.get_input_ports();
for (const auto& port : ports)
{
std::cerr << "Port: " << port.display_name << "\n";
}

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

midiin.open_port(ports[1]);
midiin.open_port(ports[2]);

//std::this_thread::sleep_for(std::chrono::seconds(60));
}
std::this_thread::sleep_for(std::chrono::seconds(5));
}
return 0;
}
Expand Down
127 changes: 127 additions & 0 deletions examples/backends/shared_pipewire_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: BSL-1.0
//
// Connect to the pipewire daemon, print the initial graph, then watch
// live additions / removals for --duration seconds.

#include <libremidi/backends/linux/pipewire/context.hpp>
#include <libremidi/backends/linux/pipewire/filter.hpp>
#include <libremidi/backends/linux/pipewire/format.hpp>
#include <libremidi/backends/linux/pipewire/instance.hpp>
#include <libremidi/backends/linux/pipewire/loader.hpp>
#include <libremidi/backends/linux/pipewire/stream.hpp>
#include <libremidi/backends/linux/pipewire/subscription.hpp>
#include <libremidi/backends/linux/pipewire/types.hpp>

#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <thread>

namespace lpw = libremidi::pipewire;

static const char* kind_name(lpw::media_class k)
{
switch (k)
{
case lpw::media_class::audio:
return "audio";
case lpw::media_class::midi:
return "midi";
case lpw::media_class::ump:
return "ump";
case lpw::media_class::video:
return "video";
default:
return "other";
}
}

static void print_node(const lpw::node_info& n)
{
std::printf(
" node id=%u kind=%-5s name=%s%s%s\n", n.id, kind_name(n.kind),
n.name.c_str(), n.description.empty() ? "" : " desc=",
n.description.c_str());
}

int main(int argc, char** argv)
{
int seconds = 5;
for (int i = 1; i < argc - 1; ++i)
{
if (std::string{argv[i]} == "--duration")
seconds = std::atoi(argv[i + 1]);
}

auto& pw = lpw::load();
if (!pw.available)
{
std::fprintf(
stderr,
"libpipewire-0.3 not available at runtime; aborting.\n"
"(Install pipewire and re-run; the executable itself has no\n"
" DT_NEEDED for libpipewire, so it can start anywhere.)\n");
return 1;
}
std::printf(
"loaded libpipewire-0.3 (%s)\n",
pw.get_library_version ? pw.get_library_version() : "version unknown");
std::printf(
" core_available=%d thread_available=%d stream_available=%d "
"filter_available=%d\n",
pw.core_available, pw.thread_available, pw.stream_available,
pw.filter_available);

auto ctx = lpw::shared_context();
if (!ctx)
{
std::fprintf(stderr, "shared_context() returned empty\n");
return 2;
}
if (!ctx->ok())
{
std::fprintf(
stderr,
"context not in connected state (state=%d) — daemon down?\n",
static_cast<int>(ctx->state()));
return 3;
}
std::printf("connected to pipewire daemon\n");

// Subscribe before snapshot or we miss late globals.
auto sub_state = ctx->on_state_changed([](lpw::connection_state s) {
const char* names[] = {"connecting", "connected", "broken",
"disconnected"};
std::printf(
"[state] -> %s\n",
names[static_cast<int>(s)]);
});
auto sub_add = ctx->on_node_added([](const lpw::node_info& n) {
std::printf("[+node] id=%u %s (%s)\n", n.id, n.name.c_str(),
kind_name(n.kind));
});
auto sub_rm = ctx->on_node_removed([](std::uint32_t id) {
std::printf("[-node] id=%u\n", id);
});

auto snap = ctx->snapshot();
std::printf("\n== Initial graph snapshot (%zu nodes) ==\n", snap.nodes.size());
for (auto k : {lpw::media_class::audio, lpw::media_class::midi,
lpw::media_class::ump, lpw::media_class::video,
lpw::media_class::other})
{
auto subset = snap.nodes_of(k);
if (subset.empty())
continue;
std::printf("[%s] %zu nodes\n", kind_name(k), subset.size());
for (const auto& n : subset)
print_node(n);
}

std::printf("\n== Watching for live changes for %d seconds ==\n", seconds);
std::this_thread::sleep_for(std::chrono::seconds(seconds));

std::printf("\nclean shutdown\n");
return 0;
}
50 changes: 50 additions & 0 deletions examples/example_virtual_ports.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <libremidi/libremidi.hpp>
#include "utils.hpp"
#include <iostream>
#include <chrono>
#include <thread>

int main()
{
try {
auto in_conf = libremidi::midi1::in_default_configuration();
auto out_conf = libremidi::midi1::out_default_configuration();

libremidi::midi_in midi_in{
{
.on_message = [](const libremidi::message& message) {
std::cout << "Received: " << message << std::endl;
}
},
in_conf
};

libremidi::midi_out midi_out{
libremidi::output_configuration{},
out_conf
};

midi_in.open_virtual_port("my in");
midi_out.open_virtual_port("my out");

std::cout << "MIDI virtual ports created:\n";
std::cout << "Client name: my app\n";
std::cout << "Input port: my in\n";
std::cout << "Output port: my out\n";
std::cout << "Press Ctrl+C to exit...\n";

libremidi::message test_note{0x90, 60, 127};
midi_out.send_message(test_note);
std::cout << "Sent test note: " << test_note << std::endl;

while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}

return 0;
}
Loading
Loading