import/export samples with nodes (both node files and copy/paste)
parent
f7f5e15070
commit
47591ea3d1
|
@ -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()));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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*);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue