Monitor refactor; link_nodes PW utility
This commit is contained in:
70
pipewire.cpp
70
pipewire.cpp
@@ -9,31 +9,6 @@
|
|||||||
namespace Pipewire
|
namespace Pipewire
|
||||||
{
|
{
|
||||||
|
|
||||||
bool link_ports(int out_port_id, int in_port_id)
|
|
||||||
{
|
|
||||||
QProcess process;
|
|
||||||
process.start("pw-link", { QString::number(out_port_id), QString::number(in_port_id) });
|
|
||||||
return process.waitForFinished() && process.exitCode() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool destroy_link(int link_id)
|
|
||||||
{
|
|
||||||
QProcess process;
|
|
||||||
process.start("pw-cli", { "destroy", QString::number(link_id) });
|
|
||||||
return process.waitForFinished() && process.exitCode() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_volume(const QString& target_node, int volume_percent)
|
|
||||||
{
|
|
||||||
volume_percent = std::max(0, std::min(100, volume_percent));
|
|
||||||
|
|
||||||
QProcess process;
|
|
||||||
process.startDetached("pactl", {
|
|
||||||
"set-sink-volume", target_node,
|
|
||||||
QString::number(volume_percent) + "%"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Node::get_display_name() const
|
QString Node::get_display_name() const
|
||||||
{
|
{
|
||||||
if (!application_name.isEmpty()) return application_name;
|
if (!application_name.isEmpty()) return application_name;
|
||||||
@@ -259,4 +234,49 @@ void Sink::destroy(int pulse_audio_id)
|
|||||||
process.waitForFinished();
|
process.waitForFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool link_ports(int out_port_id, int in_port_id)
|
||||||
|
{
|
||||||
|
QProcess process;
|
||||||
|
process.start("pw-link", { QString::number(out_port_id), QString::number(in_port_id) });
|
||||||
|
return process.waitForFinished() && process.exitCode() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool link_nodes(int out_node_id, int in_node_id, const Graph& graph)
|
||||||
|
{
|
||||||
|
auto out_ports = graph.get_ports_for_node(out_node_id, "out");
|
||||||
|
auto in_ports = graph.get_ports_for_node(in_node_id, "in");
|
||||||
|
|
||||||
|
for (const Port& out_port : out_ports) {
|
||||||
|
for (const Port& in_port : in_ports) {
|
||||||
|
if (out_port.channel != in_port.channel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!link_ports(out_port.id, in_port.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool destroy_link(int link_id)
|
||||||
|
{
|
||||||
|
QProcess process;
|
||||||
|
process.start("pw-cli", { "destroy", QString::number(link_id) });
|
||||||
|
return process.waitForFinished() && process.exitCode() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_volume(const QString& target_node, int volume_percent)
|
||||||
|
{
|
||||||
|
volume_percent = std::max(0, std::min(100, volume_percent));
|
||||||
|
|
||||||
|
QProcess process;
|
||||||
|
process.startDetached("pactl", {
|
||||||
|
"set-sink-volume", target_node,
|
||||||
|
QString::number(volume_percent) + "%"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Pipewire
|
} // namespace Pipewire
|
||||||
|
|||||||
@@ -9,10 +9,6 @@
|
|||||||
namespace Pipewire
|
namespace Pipewire
|
||||||
{
|
{
|
||||||
|
|
||||||
bool link_ports(int out_port_id, int in_port_id);
|
|
||||||
bool destroy_link(int link_id);
|
|
||||||
void set_volume(const QString& target_node, int volume_percent);
|
|
||||||
|
|
||||||
struct Port
|
struct Port
|
||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
@@ -91,4 +87,9 @@ private:
|
|||||||
int m_pipewire_id = -1;
|
int m_pipewire_id = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool link_ports(int out_port_id, int in_port_id);
|
||||||
|
bool link_nodes(int out_node_id, int in_node_id, const Graph& graph);
|
||||||
|
bool destroy_link(int link_id);
|
||||||
|
void set_volume(const QString& target_node, int volume_percent);
|
||||||
|
|
||||||
} // namespace Pipewire
|
} // namespace Pipewire
|
||||||
|
|||||||
@@ -99,57 +99,43 @@ Soundboard::~Soundboard()
|
|||||||
|
|
||||||
void Soundboard::on_app_selected(int index)
|
void Soundboard::on_app_selected(int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= m_apps.size() || !m_sink) {
|
if (!m_sink) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_graph.refresh();
|
m_graph.refresh();
|
||||||
|
|
||||||
int target_app_id = m_apps[index].id;
|
|
||||||
int sink_id = m_sink->get_id();
|
|
||||||
|
|
||||||
// Destroy old output links from our sink
|
// Destroy old output links from our sink
|
||||||
auto existing_links = m_graph.get_links_from_node(sink_id);
|
auto existing_links = m_graph.get_links_from_node(m_sink->get_id());
|
||||||
for (const auto& link : existing_links) {
|
for (const auto& link : existing_links) {
|
||||||
Pipewire::destroy_link(link.id);
|
Pipewire::destroy_link(link.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sink_out_ports = m_graph.get_ports_for_node(sink_id, "out");
|
// Recover monitor state
|
||||||
auto target_in_ports = m_graph.get_ports_for_node(target_app_id, "in");
|
|
||||||
|
|
||||||
// Link sink to target app
|
|
||||||
// TODO(1): create a wrapper function to link all channels
|
|
||||||
for (const auto& out_port : sink_out_ports) {
|
|
||||||
for (const auto& in_port : target_in_ports) {
|
|
||||||
if (out_port.channel == in_port.channel &&
|
|
||||||
!Pipewire::link_ports(out_port.id, in_port.id)) {
|
|
||||||
std::cerr << "[ERROR] Failed to link sink with target node" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link to monitor only if checked
|
|
||||||
if (m_monitor_toggle->isChecked()) {
|
if (m_monitor_toggle->isChecked()) {
|
||||||
|
int sink_id = m_sink->get_id();
|
||||||
auto sinks = m_graph.get_all_sinks();
|
auto sinks = m_graph.get_all_sinks();
|
||||||
|
|
||||||
for (const auto& system_sink : sinks) {
|
for (const auto& system_sink : sinks) {
|
||||||
// Skip our own virtual sink
|
// Skip our own virtual sink
|
||||||
if (system_sink.id == sink_id) {
|
if (system_sink.id == sink_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto output_ids = m_graph.get_ports_for_node(system_sink.id, "in");
|
Pipewire::link_nodes(sink_id, system_sink.id, m_graph);
|
||||||
// TODO(1): create a wrapper function to link all channels
|
|
||||||
for (const auto& out_port : sink_out_ports) {
|
|
||||||
for (const auto& in_port : output_ids) {
|
|
||||||
if (out_port.channel == in_port.channel) {
|
|
||||||
Pipewire::link_ports(out_port.id, in_port.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "[INFO] Monitor: " << m_monitor_toggle->isChecked() << std::endl;
|
||||||
|
|
||||||
|
if (index < 0 || index >= m_apps.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link sink to target app
|
||||||
|
int target_app_id = m_apps[index].id;
|
||||||
|
Pipewire::link_nodes(m_sink->get_id(), target_app_id, m_graph);
|
||||||
|
|
||||||
std::cout << "[INFO] Updated routing" << std::endl;
|
std::cout << "[INFO] Updated routing" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user