This commit is contained in:
Joey Yakimowich-Payne 2026-01-27 19:34:30 -07:00
commit ecfb59501a
5 changed files with 146 additions and 4 deletions

View file

@ -19,6 +19,7 @@
#include <spa/param/audio/format-utils.h>
#include <spa/param/audio/raw.h>
#include <spa/utils/dict.h>
#include <spa/utils/defs.h>
#include <spa/utils/type-info.h>
namespace Potato {
@ -76,6 +77,56 @@ static bool readRingLatest(spa_ringbuffer *ring, std::vector<uint8_t> &buffer, f
return true;
}
bool PipeWireController::createVirtualDevice(const QString &name,
const QString &description,
const char *factoryName,
const char *mediaClass,
int channels,
int rate)
{
if (!m_threadLoop || !m_core || name.isEmpty()) {
return false;
}
channels = channels > 0 ? channels : 2;
rate = rate > 0 ? rate : 48000;
const QByteArray nameBytes = name.toUtf8();
const QByteArray descBytes = description.isEmpty() ? nameBytes : description.toUtf8();
const QByteArray channelsBytes = QByteArray::number(channels);
const QByteArray rateBytes = QByteArray::number(rate);
struct spa_dict_item items[] = {
{ PW_KEY_FACTORY_NAME, factoryName },
{ PW_KEY_NODE_NAME, nameBytes.constData() },
{ PW_KEY_NODE_DESCRIPTION, descBytes.constData() },
{ PW_KEY_MEDIA_CLASS, mediaClass },
{ PW_KEY_AUDIO_CHANNELS, channelsBytes.constData() },
{ PW_KEY_AUDIO_RATE, rateBytes.constData() },
{ "object.linger", "true" },
{ PW_KEY_APP_NAME, "Potato-Manager" }
};
struct spa_dict dict = SPA_DICT_INIT(items, SPA_N_ELEMENTS(items));
lock();
auto *proxy = static_cast<struct pw_proxy*>(pw_core_create_object(
m_core,
"adapter",
PW_TYPE_INTERFACE_Node,
PW_VERSION_NODE,
&dict,
0));
unlock();
if (!proxy) {
return false;
}
m_virtualDevices.push_back(proxy);
return true;
}
static QString toQString(const char *value)
{
if (!value) {
@ -382,6 +433,13 @@ void PipeWireController::shutdown()
}
m_nodeMeters.clear();
}
for (auto *proxy : m_virtualDevices) {
if (proxy) {
pw_proxy_destroy(proxy);
}
}
m_virtualDevices.clear();
if (m_core) {
pw_core_disconnect(m_core);
@ -480,6 +538,16 @@ bool PipeWireController::setNodeVolume(uint32_t nodeId, float volume, bool mute)
return true;
}
bool PipeWireController::createVirtualSink(const QString &name, const QString &description, int channels, int rate)
{
return createVirtualDevice(name, description, "support.null-audio-sink", "Audio/Sink", channels, rate);
}
bool PipeWireController::createVirtualSource(const QString &name, const QString &description, int channels, int rate)
{
return createVirtualDevice(name, description, "support.null-audio-source", "Audio/Source", channels, rate);
}
float PipeWireController::nodeMeterPeak(uint32_t nodeId) const
{
QMutexLocker lock(&m_meterMutex);
@ -829,10 +897,12 @@ void PipeWireController::handlePortInfo(uint32_t id, const struct spa_dict *prop
uint32_t nodeId = nodeIdStr ? static_cast<uint32_t>(atoi(nodeIdStr)) : 0;
PortInfo port(id, nodeId, portName, direction);
bool emitChanged = false;
NodeInfo nodeSnapshot;
{
QMutexLocker lock(&m_nodesMutex);
m_ports.insert(id, port);
if (nodeId != 0 && m_nodes.contains(nodeId)) {
NodeInfo &node = m_nodes[nodeId];
auto &ports = (direction == PW_DIRECTION_INPUT) ? node.inputPorts : node.outputPorts;
@ -843,9 +913,15 @@ void PipeWireController::handlePortInfo(uint32_t id, const struct spa_dict *prop
}
}
ports.append(port);
nodeSnapshot = node;
emitChanged = true;
}
}
if (emitChanged) {
emit nodeChanged(nodeSnapshot);
}
qDebug() << "Port added:" << id << portName << "direction:" << direction;
}