import/export samples with nodes (both node files and copy/paste)

portability/boost
zetaPRIME 2019-06-23 03:57:40 -04:00
parent f7f5e15070
commit 47591ea3d1
9 changed files with 88 additions and 2 deletions

View File

@ -1,6 +1,7 @@
#include "node.h"
using namespace Xybrid::Data;
#include "data/project.h"
#include "data/graph.h"
#include "data/porttypes.h"
@ -15,6 +16,8 @@ using namespace Xybrid::Audio;
#include "util/strings.h"
#include "util/ycombinator.h"
#include "uisocket.h"
#include <algorithm>
#include <QDebug>
@ -108,6 +111,8 @@ std::shared_ptr<Node> Node::fromCbor(const QCborValue& m, std::shared_ptr<Graph>
QCborMap Node::multiToCbor(std::vector<std::shared_ptr<Node>>& v) {
QCborMap m;
Sample::startExport();
std::unordered_map<Node*, int> indices;
{ /* nodes */ } {
QCborArray nm;
@ -120,6 +125,13 @@ QCborMap Node::multiToCbor(std::vector<std::shared_ptr<Node>>& v) {
m[qs("nodes")] = nm;
}
// exported samples
if (auto v = Sample::finishExport(); !v.empty()) {
QCborMap smp;
for (auto s : v) smp[QCborValue(s->uuid)] = s->toCbor();
m[qs("samples")] = smp;
}
{ /* connections */ } {
QCborArray cm;
@ -176,6 +188,19 @@ std::vector<std::shared_ptr<Node>> Node::multiFromCbor(const QCborMap& m, std::s
center.setY(static_cast<int>(c.at(1).toInteger()));
}
{ /* exported samples */ } {
QCborMap smp = m.value("samples").toMap();
auto project = parent->project;
for (auto it = smp.constBegin(), end = smp.constEnd(); it != end; ++it) {
auto uuid = it.key().toUuid();
if (project->samples.find(uuid) != project->samples.end()) continue; // we already have this; next
auto s = Sample::fromCbor(it.value(), uuid);
s->project = project;
project->samples.insert(s->uuid, s);
}
emit project->socket->updatePatternLists();
}
{ /* nodes */ } {
QCborArray n = m.value("nodes").toArray();
v.reserve(static_cast<size_t>(n.size()));

View File

@ -154,3 +154,30 @@ std::shared_ptr<Sample> Sample::fromFile(QString fileName) {
return smp;
}
namespace {
bool exporting = false;
std::unordered_map<Sample*, bool> exportMap;
}
void Sample::startExport() {
exporting = true;
exportMap.reserve(16);
}
std::vector<std::shared_ptr<Sample>> Sample::finishExport() {
std::vector<std::shared_ptr<Sample>> v;
if (exporting) {
exporting = false;
v.reserve(exportMap.size());
for (auto it : exportMap) v.push_back(it.first->shared_from_this());
exportMap.clear();
}
return v;
}
void Sample::markForExport() {
if (exporting) {
exportMap[this] = true;
}
}

View File

@ -49,5 +49,9 @@ namespace Xybrid::Data {
static std::shared_ptr<Sample> fromFile(QString);
static void startExport();
static std::vector<std::shared_ptr<Sample>> finishExport();
void markForExport();
};
}

View File

@ -267,10 +267,19 @@ bool FileOps::saveNode(std::shared_ptr<Node> node, QString fileName) {
QFile file(fileName);
if (!file.open(QFile::WriteOnly)) return false;
Sample::startExport();
// we handle header and let the node handle the rest of its conversion
QCborArray root;
root << "xybrid:node" << XYBRID_VERSION << node->toCbor();
// and write in any exported samples
if (auto v = Sample::finishExport(); !v.empty()) {
QCborMap smp;
for (auto s : v) smp[QCborValue(s->uuid)] = s->toCbor();
root << smp;
}
// write out
QCborStreamWriter w(&file);
root.toCborValue().toCbor(w);
@ -296,6 +305,19 @@ std::shared_ptr<Node> FileOps::loadNode(QString fileName, std::shared_ptr<Graph>
if (auto v = root.at(1); !v.isInteger() || v.toInteger() > XYBRID_VERSION) return nullptr; // invalid version or too new
if (!root.at(2).isMap()) return nullptr; // so close, but... nope
if (root.at(3).isMap()) { // node file has samples, load in any we don't already have
auto smp = root.at(3).toMap();
auto project = parent->project;
for (auto it = smp.constBegin(), end = smp.constEnd(); it != end; ++it) {
auto uuid = it.key().toUuid();
if (project->samples.find(uuid) != project->samples.end()) continue; // we already have this; next
auto s = Sample::fromCbor(it.value(), uuid);
s->project = project;
project->samples.insert(s->uuid, s);
}
emit project->socket->updatePatternLists();
}
return Node::fromCbor(root.at(2), parent); // let Node handle the rest
}

View File

@ -71,6 +71,7 @@ namespace {
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
socket = new UISocket(); // create this first
ui->setupUi(this);
// remove tab containing system widgets
@ -337,7 +338,6 @@ MainWindow::MainWindow(QWidget *parent) :
}
// Set up signaling from project to UI
socket = new UISocket();
socket->setParent(this);
socket->window = this;
socket->undoStack = undoStack;

View File

@ -53,6 +53,8 @@ namespace Xybrid {
void playbackPosition(int seq, int row);
inline UISocket* uiSocket() { return socket; }
protected:
void closeEvent(QCloseEvent*) override;
bool eventFilter(QObject *obj, QEvent *event) override;

View File

@ -130,7 +130,6 @@ void BeatPad::release() { core.release(); }
void BeatPad::process() { core.process(this); }
void BeatPad::saveData(QCborMap& m) const {
// TODO: mark samples for inclusion in export
QCborMap cm;
for (auto c : cfg) {
if (auto smp = c.second->smp.lock(); smp) {
@ -139,6 +138,7 @@ void BeatPad::saveData(QCborMap& m) const {
e[qs("start")] = static_cast<qint64>(c.second->start);
e[qs("end")] = static_cast<qint64>(c.second->end);
cm[c.first] = e;
smp->markForExport();
}
}
m[qs("notecfg")] = cm;

View File

@ -6,6 +6,8 @@ using Xybrid::Data::Project;
using Xybrid::Data::Pattern;
using Xybrid::Data::Sample;
#include "uisocket.h"
#include "editing/projectcommands.h"
#include "editing/patterncommands.h"
using namespace Xybrid::Editing;
@ -48,6 +50,8 @@ SampleListModel::SampleListModel(QObject* parent, MainWindow* window) : QAbstrac
menu->popup(view->mapToGlobal(pt));
});
connect(window->uiSocket(), &UISocket::updatePatternLists, this, [this] { refresh(); });
}
std::shared_ptr<Sample> SampleListModel::itemAt(const QModelIndex& ind) {

View File

@ -24,6 +24,8 @@ namespace Xybrid {
void patternUpdated(Data::Pattern* pattern);
void rowUpdated(Data::Pattern* pattern, int channel, int row);
void sampleListUpdated();
void openGraph(Data::Graph*);
void openNodeUI(Data::Node*);
};