Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save and Load individual parts. #1371

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src-ui/app/shared/HeaderRegion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,42 @@ void HeaderRegion::doLoadMulti()
});
}

void HeaderRegion::doSaveSelectedPart()
{
fileChooser = std::make_unique<juce::FileChooser>(
"Save Selected Part", juce::File(editor->browser.patchIODirectory.u8string()), "*.scp");
fileChooser->launchAsync(juce::FileBrowserComponent::canSelectFiles |
juce::FileBrowserComponent::saveMode |
juce::FileBrowserComponent::warnAboutOverwriting,
[w = juce::Component::SafePointer(this)](const juce::FileChooser &c) {
auto result = c.getResults();
if (result.isEmpty() || result.size() > 1)
{
return;
}
// send a 'save multi' message
w->sendToSerialization(cmsg::SaveSelectedPart(
result[0].getFullPathName().toStdString()));
});
}

void HeaderRegion::doLoadIntoSelectedPart()
{
fileChooser = std::make_unique<juce::FileChooser>(
"Load Part", juce::File(editor->browser.patchIODirectory.u8string()), "*.scp");
fileChooser->launchAsync(
juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::openMode,
[w = juce::Component::SafePointer(this)](const juce::FileChooser &c) {
auto result = c.getResults();
if (result.isEmpty() || result.size() > 1)
{
return;
}
w->sendToSerialization(cmsg::LoadPartInto(
{result[0].getFullPathName().toStdString(), w->editor->selectedPart}));
});
}

void HeaderRegion::showSaveMenu()
{
auto p = juce::PopupMenu();
Expand All @@ -313,10 +349,22 @@ void HeaderRegion::showSaveMenu()
if (w)
w->doSaveMulti();
});
p.addItem("Save Part " + std::to_string(editor->selectedPart + 1),
[w = juce::Component::SafePointer(this)]() {
if (w)
w->doSaveSelectedPart();
});

p.addSeparator();
p.addItem("Load Multi", [w = juce::Component::SafePointer(this)]() {
if (w)
w->doLoadMulti();
});
p.addItem("Load Part Into " + std::to_string(editor->selectedPart + 1),
[w = juce::Component::SafePointer(this)]() {
if (w)
w->doLoadIntoSelectedPart();
});
p.addSeparator();
p.addItem("Reset Engine To Blank", [w = juce::Component::SafePointer(this)]() {
if (w)
Expand Down
2 changes: 2 additions & 0 deletions src-ui/app/shared/HeaderRegion.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ struct HeaderRegion : juce::Component, HasEditor, juce::FileDragAndDropTarget
void showSaveMenu();
void doSaveMulti();
void doLoadMulti();
void doSaveSelectedPart();
void doLoadIntoSelectedPart();

void showMultiSelectionMenu();

Expand Down
1 change: 1 addition & 0 deletions src/engine/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ struct Engine : MoveableOnly<Engine>, SampleRateSupport
{
IN_PROCESS,
FOR_MULTI,
FOR_PART,
FOR_DAW
};
static thread_local StreamReason streamReason;
Expand Down
16 changes: 16 additions & 0 deletions src/engine/part.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,20 @@ Part::zoneMappingSummary_t Part::getZoneMappingSummary()
}
return res;
}

std::vector<SampleID> Part::getSamplesUsedByPart() const
{
std::unordered_set<SampleID> resSet;
for (const auto &g : groups)
{
for (const auto &z : g->getZones())
{
for (const auto &var : z->variantData.variants)
{
resSet.insert(var.sampleID);
}
}
}
return std::vector<SampleID>(resSet.begin(), resSet.end());
}
} // namespace scxt::engine
2 changes: 2 additions & 0 deletions src/engine/part.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ struct Part : MoveableOnly<Part>, SampleRateSupport
groupContainer_t::iterator end() noexcept { return groups.end(); }
groupContainer_t::const_iterator cend() const noexcept { return groups.cend(); }

std::vector<SampleID> getSamplesUsedByPart() const;

private:
groupContainer_t groups;
};
Expand Down
29 changes: 29 additions & 0 deletions src/json/engine_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,40 @@ SC_STREAMDEF(
scxt::engine::Part, SC_FROM({
// TODO: Do a non-empty part stream with the If variant
v = {{"config", from.configuration}, {"groups", from.getGroups()}, {"macros", from.macros}};
if (SC_STREAMING_FOR_PART)
{
addToObject<val_t>(v, "streamingVersion", currentStreamingVersion);
addToObject<val_t>(v, "streamedForPart", true);
assert(from.parentPatch->parentEngine);
addToObject<val_t>(
v, "samplesUsedByPart",
from.parentPatch->parentEngine->getSampleManager()->getSampleAddressesFor(
from.getSamplesUsedByPart()));
SCLOG("ToDo - stream sample manager subset");
}
}),
SC_TO({
auto &part = to;
part.clearGroups();

std::unique_ptr<engine::Engine::UnstreamGuard> sg;
bool streamedForPart{false};
findOrSet(v, "streamedForPart", false, streamedForPart);
if (streamedForPart)
{
uint64_t partStreamingVersion{0};
findIf(v, "streamingVersion", partStreamingVersion);
SCLOG("Unstreaming part state. Stream version : "
<< scxt::humanReadableVersion(partStreamingVersion));

scxt::sample::SampleManager::sampleAddressesAndIds_t samples;
findIf(v, "samplesUsedByPart", samples);
to.parentPatch->parentEngine->getSampleManager()->restoreFromSampleAddressesAndIDs(
samples);

sg = std::make_unique<engine::Engine::UnstreamGuard>(partStreamingVersion);
}

if (SC_UNSTREAMING_FROM_PRIOR_TO(0x2024'08'18))
{
findIf(v, "channel", part.configuration.channel);
Expand Down
2 changes: 2 additions & 0 deletions src/json/scxt_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ void addUnlessDefault(V &v, const std::string &key, const R &defVal, const R &va
#define SC_STREAMING_FOR_DAW (engine::Engine::streamReason == engine::Engine::StreamReason::FOR_DAW)
#define SC_STREAMING_FOR_MULTI \
(engine::Engine::streamReason == engine::Engine::StreamReason::FOR_MULTI)
#define SC_STREAMING_FOR_PART \
(engine::Engine::streamReason == engine::Engine::StreamReason::FOR_PART)

#define SC_STREAMING_FOR_DAW_OR_MULTI (SC_STREAMING_FOR_DAW || SC_STREAMING_FOR_MULTI)

Expand Down
32 changes: 32 additions & 0 deletions src/json/stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,36 @@ void unstreamEngineState(engine::Engine &e, const std::string &data, bool msgPac

e.sendFullRefreshToClient();
}

void unstreamPartState(engine::Engine &e, int part, const std::string &data, bool msgPack)
{
e.clearAll();
if (msgPack)
{
tao::json::events::transformer<tao::json::events::to_basic_value<scxt_traits>> consumer;
tao::json::msgpack::events::from_string(consumer, data);
auto jv = std::move(consumer.value);
jv.to(*(e.getPatch()->getPart(part)));
}
else
{
tao::json::events::transformer<tao::json::events::to_basic_value<scxt_traits>> consumer;
tao::json::events::from_string(consumer, data);
auto jv = std::move(consumer.value);
jv.to(*(e.getPatch()->getPart(part)));
}

if (!e.getSampleManager()->missingList.empty())
{
std::ostringstream oss;
oss << "On load, sample manager could not locate the following files:\n";
for (const auto &p : e.getSampleManager()->missingList)
{
oss << " " << p.u8string() << "\n";
}
e.getMessageController()->reportErrorToClient("Missing Samples", oss.str());
}

e.sendFullRefreshToClient();
}
} // namespace scxt::json
2 changes: 2 additions & 0 deletions src/json/stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace scxt::json
std::string streamPatch(const engine::Patch &p, bool pretty = false);
std::string streamEngineState(const engine::Engine &e, bool pretty = false);
void unstreamEngineState(engine::Engine &e, const std::string &jsonData, bool msgPack = false);
void unstreamPartState(engine::Engine &e, int part, const std::string &jsonData,
bool msgPack = false);
} // namespace scxt::json

#endif // SHORTCIRCUIT_STREAM_H
2 changes: 1 addition & 1 deletion src/messaging/client/browser_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ inline void doAddBrowserDeviceLocation(const fs::path &p, const engine::Engine &
serializationSendToClient(s2c_refresh_browser, true, cont);
}
CLIENT_TO_SERIAL(AddBrowserDeviceLocation, c2s_add_browser_device_location, std::string,
doAddBrowserDeviceLocation(fs::path{payload}, engine, cont));
doAddBrowserDeviceLocation(fs::path(fs::u8path(payload)), engine, cont));

SERIAL_TO_CLIENT(RefreshBrowser, s2c_refresh_browser, bool, onBrowserRefresh)

Expand Down
25 changes: 22 additions & 3 deletions src/messaging/client/patch_io_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,33 @@ namespace scxt::messaging::client
{
inline void doSaveMulti(const std::string &s, engine::Engine &engine, MessageController &cont)
{
patch_io::saveMulti(fs::path{s}, engine);
patch_io::saveMulti(fs::path(fs::u8path(s)), engine);

SCLOG("Remember to update the browser also");
// engine.getBrowser()->doSomething;
}
CLIENT_TO_SERIAL(SaveMulti, c2s_save_multi, std::string, doSaveMulti(payload, engine, cont));

CLIENT_TO_SERIAL(LoadMulti, c2s_load_multi, std::string,
patch_io::loadMulti(fs::path{payload}, engine));
patch_io::loadMulti(fs::path(fs::u8path(payload)), engine));

inline void doSaveSelectedPart(const std::string &s, engine::Engine &engine,
MessageController &cont)
{
SCLOG("Saving part to " << s);
patch_io::savePart(fs::path(fs::u8path(s)), engine, engine.getSelectionManager()->selectedPart);
// engine.getBrowser()->doSomething;
}
CLIENT_TO_SERIAL(SaveSelectedPart, c2s_save_selected_part, std::string,
doSaveSelectedPart(payload, engine, cont));

using loadPartIntoPayload_t = std::tuple<std::string, int16_t>;
inline void doLoadPartInto(const loadPartIntoPayload_t &payload, engine::Engine &engine,
MessageController &cont)
{
patch_io::loadPartInto(fs::path(fs::u8path(std::get<0>(payload))), engine,
std::get<1>(payload));
}
CLIENT_TO_SERIAL(LoadPartInto, c2s_load_part_into, loadPartIntoPayload_t,
doLoadPartInto(payload, engine, cont));
} // namespace scxt::messaging::client
#endif // SHORTCIRCUITXT_PATCH_IO_MESSAGES_H
4 changes: 2 additions & 2 deletions src/messaging/client/structure_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ CLIENT_TO_SERIAL(RegisterClient, c2s_register_client, bool, doRegisterClient(eng
inline void addSample(const std::string &payload, engine::Engine &engine, MessageController &cont)
{
assert(cont.threadingChecker.isSerialThread());
auto p = fs::path{payload};
auto p = fs::path(fs::u8path(payload));
engine.loadSampleIntoSelectedPartAndGroup(p);
}
CLIENT_TO_SERIAL(AddSample, c2s_add_sample, std::string, addSample(payload, engine, cont);)
Expand All @@ -106,7 +106,7 @@ inline void addSampleInZone(const addSampleInZone_t &payload, engine::Engine &en
MessageController &cont)
{
assert(cont.threadingChecker.isSerialThread());
auto path = fs::path{std::get<0>(payload)};
auto path = fs::path(fs::u8path(std::get<0>(payload)));
auto part{std::get<1>(payload)};
auto group{std::get<2>(payload)};
auto zone{std::get<3>(payload)};
Expand Down
78 changes: 77 additions & 1 deletion src/patch_io/patch_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ std::string readSCDataChunk(const std::unique_ptr<RIFF::File> &f)

bool saveMulti(const fs::path &p, const scxt::engine::Engine &e)
{
SCLOG("Made it to the patch code " << p.u8string());
SCLOG("Saving Multi to " << p.u8string());

try
{
Expand All @@ -112,6 +112,32 @@ bool saveMulti(const fs::path &p, const scxt::engine::Engine &e)
return true;
}

bool savePart(const fs::path &p, const scxt::engine::Engine &e, int part)
{
SCLOG("Saving part " << part << " to " << p.u8string());

try
{
auto sg = scxt::engine::Engine::StreamGuard(engine::Engine::FOR_PART);
// auto msg =
// tao::json::msgpack::to_string(json::scxt_value(*(e.getPatch()->getPart(part))));
auto msg = tao::json::to_string(json::scxt_value(*(e.getPatch()->getPart(part))));

auto f = std::make_unique<RIFF::File>('SCXT');
f->SetByteOrder(RIFF::endian_little);
addSCManifest(f, "part");
addSCDataChunk(f, msg);

// TODO: If embeeding samples, add a list here with them
f->Save(p.u8string());
}
catch (const RIFF::Exception &e)
{
SCLOG(e.Message);
}
return true;
}

bool initFromResourceBundle(scxt::engine::Engine &engine)
{
SCLOG("Init From Resource Bundle");
Expand Down Expand Up @@ -210,4 +236,54 @@ bool loadMulti(const fs::path &p, scxt::engine::Engine &engine)
}
return true;
}

bool loadPartInto(const fs::path &p, scxt::engine::Engine &engine, int part)
{
SCLOG("loadPart " << p.u8string() << " " << part);

std::string payload;
try
{
auto f = std::make_unique<RIFF::File>(p.u8string());
auto manifest = readSCManifest(f);
payload = readSCDataChunk(f);
}
catch (const RIFF::Exception &e)
{
SCLOG("RIFF::Exception " << e.Message);
return false;
}

auto &cont = engine.getMessageController();
if (cont->isAudioRunning)
{
cont->stopAudioThreadThenRunOnSerial([payload, part, &nonconste = engine](auto &e) {
try
{
nonconste.stopAllSounds();
scxt::json::unstreamPartState(nonconste, part, payload, false);
auto &cont = *e.getMessageController();
cont.restartAudioThreadFromSerial();
}
catch (std::exception &err)
{
SCLOG("Unable to load [" << err.what() << "]");
}
});
}
else
{
try
{
engine.stopAllSounds();
scxt::json::unstreamPartState(engine, part, payload, false);
}
catch (std::exception &err)
{
SCLOG("Unable to load [" << err.what() << "]");
}
}

return true;
}
} // namespace scxt::patch_io
4 changes: 2 additions & 2 deletions src/patch_io/patch_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ namespace scxt::patch_io
{
bool saveMulti(const fs::path &toFile, const scxt::engine::Engine &);
bool loadMulti(const fs::path &fromFile, scxt::engine::Engine &);
bool streamPart(const fs::path &toFile, const scxt::engine::Part &);
bool unstreamPart(const fs::path &fromFile, scxt::engine::Part &);
bool savePart(const fs::path &toFile, const scxt::engine::Engine &, int part);
bool loadPartInto(const fs::path &fromFile, scxt::engine::Engine &, int part);

bool initFromResourceBundle(scxt::engine::Engine &e);
} // namespace scxt::patch_io
Expand Down
Loading
Loading