warp-pipe/gui/PresetManager.cpp

216 lines
6.6 KiB
C++

#include "PresetManager.h"
#include "WarpGraphModel.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
bool PresetManager::savePreset(const QString &path, warppipe::Client *client,
const WarpGraphModel *model) {
if (!client || !model)
return false;
QJsonArray devicesArray;
auto nodesResult = client->ListNodes();
if (nodesResult.ok()) {
for (const auto &node : nodesResult.value) {
if (!node.is_virtual)
continue;
QJsonObject dev;
dev["name"] = QString::fromStdString(node.name);
dev["description"] = QString::fromStdString(node.description);
dev["media_class"] = QString::fromStdString(node.media_class);
auto portsResult = client->ListPorts(node.id);
int channels = 0;
if (portsResult.ok()) {
for (const auto &port : portsResult.value) {
if (port.is_input)
++channels;
}
if (channels == 0) {
for (const auto &port : portsResult.value) {
if (!port.is_input)
++channels;
}
}
}
dev["channels"] = channels > 0 ? channels : 2;
devicesArray.append(dev);
}
}
QJsonArray routingArray;
auto linksResult = client->ListLinks();
if (linksResult.ok() && nodesResult.ok()) {
std::unordered_map<uint32_t, std::pair<std::string, std::string>> portMap;
for (const auto &node : nodesResult.value) {
auto portsResult = client->ListPorts(node.id);
if (!portsResult.ok())
continue;
for (const auto &port : portsResult.value) {
portMap[port.id.value] = {node.name, port.name};
}
}
for (const auto &link : linksResult.value) {
auto outIt = portMap.find(link.output_port.value);
auto inIt = portMap.find(link.input_port.value);
if (outIt == portMap.end() || inIt == portMap.end())
continue;
QJsonObject route;
route["out_node"] = QString::fromStdString(outIt->second.first);
route["out_port"] = QString::fromStdString(outIt->second.second);
route["in_node"] = QString::fromStdString(inIt->second.first);
route["in_port"] = QString::fromStdString(inIt->second.second);
routingArray.append(route);
}
}
QJsonArray layoutArray;
for (auto qtId : model->allNodeIds()) {
const WarpNodeData *data = model->warpNodeData(qtId);
if (!data)
continue;
QPointF pos =
model->nodeData(qtId, QtNodes::NodeRole::Position).toPointF();
QJsonObject nodeLayout;
nodeLayout["name"] = QString::fromStdString(data->info.name);
nodeLayout["x"] = pos.x();
nodeLayout["y"] = pos.y();
layoutArray.append(nodeLayout);
}
QJsonArray volumesArray;
for (auto qtId : model->allNodeIds()) {
const WarpNodeData *data = model->warpNodeData(qtId);
if (!data)
continue;
auto vs = model->nodeVolumeState(qtId);
if (vs.volume != 1.0f || vs.mute) {
QJsonObject volObj;
volObj["name"] = QString::fromStdString(data->info.name);
volObj["volume"] = static_cast<double>(vs.volume);
volObj["mute"] = vs.mute;
volumesArray.append(volObj);
}
}
QJsonObject root;
root["version"] = 1;
root["virtual_devices"] = devicesArray;
root["routing"] = routingArray;
root["layout"] = layoutArray;
if (!volumesArray.isEmpty())
root["volumes"] = volumesArray;
QFileInfo fi(path);
QDir dir = fi.absoluteDir();
if (!dir.exists())
dir.mkpath(".");
QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
return false;
file.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
return true;
}
bool PresetManager::loadPreset(const QString &path, warppipe::Client *client,
WarpGraphModel *model) {
if (!client || !model)
return false;
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
return false;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
if (!doc.isObject())
return false;
QJsonObject root = doc.object();
int version = root["version"].toInt();
if (version < 1 || version > 1)
return false;
auto existingNodes = client->ListNodes();
std::unordered_set<std::string> existingNames;
if (existingNodes.ok()) {
for (const auto &node : existingNodes.value) {
existingNames.insert(node.name);
}
}
QJsonArray devicesArray = root["virtual_devices"].toArray();
for (const auto &val : devicesArray) {
QJsonObject dev = val.toObject();
std::string name = dev["name"].toString().toStdString();
std::string mediaClass = dev["media_class"].toString().toStdString();
int channels = dev["channels"].toInt(2);
if (existingNames.count(name))
continue;
warppipe::VirtualNodeOptions opts;
opts.format.channels = static_cast<uint32_t>(channels);
bool isSink = mediaClass == "Audio/Sink" || mediaClass == "Audio/Duplex";
if (isSink) {
client->CreateVirtualSink(name, opts);
} else {
client->CreateVirtualSource(name, opts);
}
}
QJsonArray layoutArray = root["layout"].toArray();
for (const auto &val : layoutArray) {
QJsonObject obj = val.toObject();
std::string name = obj["name"].toString().toStdString();
double x = obj["x"].toDouble();
double y = obj["y"].toDouble();
model->setPendingPosition(name, QPointF(x, y));
}
model->refreshFromClient();
QJsonArray routingArray = root["routing"].toArray();
for (const auto &val : routingArray) {
QJsonObject route = val.toObject();
std::string outNode = route["out_node"].toString().toStdString();
std::string outPort = route["out_port"].toString().toStdString();
std::string inNode = route["in_node"].toString().toStdString();
std::string inPort = route["in_port"].toString().toStdString();
client->CreateLinkByName(outNode, outPort, inNode, inPort,
warppipe::LinkOptions{.linger = true});
}
model->refreshFromClient();
if (root.contains("volumes")) {
QJsonArray volumesArray = root["volumes"].toArray();
for (const auto &val : volumesArray) {
QJsonObject obj = val.toObject();
std::string name = obj["name"].toString().toStdString();
float volume = static_cast<float>(obj["volume"].toDouble(1.0));
bool mute = obj["mute"].toBool(false);
for (auto qtId : model->allNodeIds()) {
const WarpNodeData *data = model->warpNodeData(qtId);
if (data && data->info.name == name) {
WarpGraphModel::NodeVolumeState vs;
vs.volume = volume;
vs.mute = mute;
model->setNodeVolumeState(qtId, vs);
break;
}
}
}
}
return true;
}