commit 63f3923b8553e8e1831cefb0ee18d590dcaa1df8
parent 5f852def9906f9cdf8fbb91adbf87c0eb6b2fb77
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Mon, 20 May 2024 11:40:17 +0200
support drag & drop of patches to external applications, will export midi files
Diffstat:
7 files changed, 101 insertions(+), 18 deletions(-)
diff --git a/source/jucePluginEditorLib/patchmanager/savepatchdesc.cpp b/source/jucePluginEditorLib/patchmanager/savepatchdesc.cpp
@@ -18,6 +18,35 @@ namespace jucePluginEditorLib::patchManager
return m_patches;
}
+ bool SavePatchDesc::writePatchesToFile(const juce::File& _file) const
+ {
+ const auto& patches = getPatches();
+
+ if(patches.empty())
+ return false;
+
+ std::vector<std::vector<uint8_t>> patchesData;
+
+ for (auto& patch : patches)
+ {
+ auto data = m_patchManager.prepareSave(patch.second);
+ if(data.empty())
+ return false;
+ patchesData.emplace_back(std::move(data));
+ }
+
+ std::stringstream ss;
+
+ synthLib::SysexToMidi::write(ss, patchesData);
+
+ const auto size = ss.tellp();
+ std::vector<char> buffer;
+ buffer.resize(size);
+ ss.read(buffer.data(), size);
+
+ return _file.replaceWithData(buffer.data(), size);
+ }
+
std::vector<pluginLib::patchDB::PatchPtr> SavePatchDesc::getPatchesFromDragSource(const juce::DragAndDropTarget::SourceDetails& _dragSourceDetails)
{
const auto* savePatchDesc = fromDragSource(_dragSourceDetails);
diff --git a/source/jucePluginEditorLib/patchmanager/savepatchdesc.h b/source/jucePluginEditorLib/patchmanager/savepatchdesc.h
@@ -29,6 +29,8 @@ namespace jucePluginEditorLib::patchManager
bool isPartValid() const { return m_part != InvalidPart; }
bool hasPatches() const { return !getPatches().empty(); }
+ bool writePatchesToFile(const juce::File& _file) const;
+
static const SavePatchDesc* fromDragSource(const juce::DragAndDropTarget::SourceDetails& _source)
{
return dynamic_cast<const SavePatchDesc*>(_source.description.getObject());
diff --git a/source/jucePluginEditorLib/pluginEditor.cpp b/source/jucePluginEditorLib/pluginEditor.cpp
@@ -8,6 +8,7 @@
#include "../synthLib/sysexToMidi.h"
#include "patchmanager/patchmanager.h"
+#include "patchmanager/savepatchdesc.h"
namespace jucePluginEditorLib
{
@@ -20,7 +21,11 @@ namespace jucePluginEditorLib
showDisclaimer();
}
- Editor::~Editor() = default;
+ Editor::~Editor()
+ {
+ for (const auto& file : m_dragAndDropFiles)
+ file.deleteFile();
+ }
void Editor::loadPreset(const std::function<void(const juce::File&)>& _callback)
{
@@ -189,6 +194,47 @@ namespace jucePluginEditorLib
}
}
+ bool Editor::shouldDropFilesWhenDraggedExternally(const juce::DragAndDropTarget::SourceDetails& sourceDetails, juce::StringArray& files, bool& canMoveFiles)
+ {
+ const auto* savePatchDesc = patchManager::SavePatchDesc::fromDragSource(sourceDetails);
+
+ if(!savePatchDesc || !savePatchDesc->hasPatches())
+ return false;
+
+ // try to create human-readable filename first
+ const auto patch = savePatchDesc->getPatches().begin()->second;
+
+ auto patchFileName = patch->getName();
+
+ while(!patchFileName.empty() && patchFileName.back() == ' ')
+ patchFileName.pop_back();
+
+ patchFileName = m_processor.getProperties().name + "_" + patchManager::PatchManager::createValidFilename(patchFileName) + ".mid";
+
+ const auto pathName = juce::File::getSpecialLocation(juce::File::tempDirectory).getFullPathName().toStdString() + "/" + patchFileName;
+
+ auto file = juce::File(pathName);
+
+ if(file.hasWriteAccess())
+ {
+ m_dragAndDropFiles.emplace_back(file);
+ }
+ else
+ {
+ // failed, create temp file
+ const auto& tempFile = m_dragAndDropTempFiles.emplace_back(std::make_shared<juce::TemporaryFile>(patchFileName));
+ file = tempFile->getFile();
+ }
+
+ if(!savePatchDesc->writePatchesToFile(file))
+ return false;
+
+ files.add(file.getFullPathName());
+
+ canMoveFiles = true;
+ return true;
+ }
+
void Editor::onDisclaimerFinished() const
{
if(!synthLib::isRunningUnderRosetta())
diff --git a/source/jucePluginEditorLib/pluginEditor.h b/source/jucePluginEditorLib/pluginEditor.h
@@ -60,6 +60,8 @@ namespace jucePluginEditorLib
void showDisclaimer() const;
+ bool shouldDropFilesWhenDraggedExternally(const juce::DragAndDropTarget::SourceDetails& sourceDetails, juce::StringArray& files, bool& canMoveFiles) override;
+
private:
void onDisclaimerFinished() const;
@@ -80,5 +82,7 @@ namespace jucePluginEditorLib
std::unique_ptr<juce::FileChooser> m_fileChooser;
std::unique_ptr<patchManager::PatchManager> m_patchManager;
std::vector<uint8_t> m_instanceConfig;
+ std::vector<std::shared_ptr<juce::TemporaryFile>> m_dragAndDropTempFiles;
+ std::vector<juce::File> m_dragAndDropFiles;
};
}
diff --git a/source/jucePluginLib/patchdb/db.cpp b/source/jucePluginLib/patchdb/db.cpp
@@ -17,23 +17,6 @@ namespace pluginLib::patchDB
{
static constexpr bool g_cacheEnabled = true;
- namespace
- {
- std::string createValidFilename(const std::string& _name)
- {
- std::string result;
- result.reserve(_name.size());
-
- for (const char c : _name)
- {
- if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
- result += c;
- else
- result += '_';
- }
- return result;
- }
- }
DB::DB(juce::File _dir)
: m_settingsDir(std::move(_dir))
, m_jsonFileName(m_settingsDir.getChildFile("patchmanagerdb.json"))
@@ -91,6 +74,21 @@ namespace pluginLib::patchDB
_mods->updateCache();
}
+ std::string DB::createValidFilename(const std::string& _name)
+ {
+ std::string result;
+ result.reserve(_name.size());
+
+ for (const char c : _name)
+ {
+ if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+ result += c;
+ else
+ result += '_';
+ }
+ return result;
+ }
+
DataSourceNodePtr DB::addDataSource(const DataSource& _ds, const bool _save, const DataSourceLoadedCallback& _callback)
{
const auto needsSave = _save && _ds.origin == DataSourceOrigin::Manual && _ds.type != SourceType::Rom;
diff --git a/source/jucePluginLib/patchdb/db.h b/source/jucePluginLib/patchdb/db.h
@@ -82,6 +82,8 @@ namespace pluginLib::patchDB
static void assign(const PatchPtr& _patch, const PatchModificationsPtr& _mods);
+ static std::string createValidFilename(const std::string& _name);
+
protected:
DataSourceNodePtr addDataSource(const DataSource& _ds, bool _save, const DataSourceLoadedCallback& = [](bool , std::shared_ptr<DataSourceNode>) {});
diff --git a/source/synthLib/sysexToMidi.cpp b/source/synthLib/sysexToMidi.cpp
@@ -48,8 +48,10 @@ namespace synthLib
writeBuf(_dst, {0xff,0x2f,0x00}); // end of track
+ const auto end = _dst.tellp();
_dst.seekp(trackChunkBegin);
writeUInt32(_dst, static_cast<uint32_t>(trackChunkLength));
+ _dst.seekp(end);
}
void SysexToMidi::writeBuf(std::ostream& _dst, const std::vector<uint8_t>& _data)