diff --git a/cmake/wrap_standalone.cmake b/cmake/wrap_standalone.cmake index 81f2bc6e..22ee612d 100644 --- a/cmake/wrap_standalone.cmake +++ b/cmake/wrap_standalone.cmake @@ -68,6 +68,7 @@ function(target_add_standalone_wrapper) target_sources(${SA_TARGET} PRIVATE "${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/wrapasstandalone.mm" ${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/macos/AppDelegate.mm + ${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/macos/StandaloneFunctions.mm ${GEN_XIB} ) diff --git a/src/detail/standalone/entry.cpp b/src/detail/standalone/entry.cpp index f14c439d..6ee9c965 100644 --- a/src/detail/standalone/entry.cpp +++ b/src/detail/standalone/entry.cpp @@ -52,6 +52,24 @@ std::shared_ptr mainCreatePlugin(const clap_plugin_entry *ee, cons plugin->initialize(); + auto pt = getStandaloneSettingsPath(); + if (pt.has_value()) + { + auto loadPath = *pt / plugin->_plugin->desc->id; + try + { + if (fs::exists(loadPath / "settings.clapwrapper")) + { + LOG << "Trying to load from clap wrapper settings" << std::endl; + standaloneHost->tryLoadStandaloneAndPluginSettings(loadPath, "settings.clapwrapper"); + } + } + catch (const fs::filesystem_error &e) + { + // Oh well - whatcha gonna do? + } + } + plugin->setSampleRate(48000); plugin->setBlockSizes(32, 1024); plugin->activate(); @@ -96,6 +114,26 @@ int mainFinish() standaloneHost->stopAudioThread(); standaloneHost->stopMIDIThread(); + auto pt = getStandaloneSettingsPath(); + if (pt.has_value()) + { + auto savePath = *pt / plugin->_plugin->desc->id; + LOG << "Saving settings to '" << savePath << "'" << std::endl; + try + { + fs::create_directories(savePath); + standaloneHost->saveStandaloneAndPluginSettings(savePath, "settings.clapwrapper"); + } + catch (const fs::filesystem_error &e) + { + // Oh well - whatcha gonna do? + } + } + else + { + LOG << "No Standalone Settings Path; not streaming" << std::endl; + } + plugin->deactivate(); } plugin.reset(); diff --git a/src/detail/standalone/macos/StandaloneFunctions.mm b/src/detail/standalone/macos/StandaloneFunctions.mm new file mode 100644 index 00000000..a5b17362 --- /dev/null +++ b/src/detail/standalone/macos/StandaloneFunctions.mm @@ -0,0 +1,20 @@ + + +#include +#include "detail/standalone/standalone_host.h" + +namespace freeaudio::clap_wrapper::standalone +{ +std::optional getStandaloneSettingsPath() +{ + auto *fileManager = [NSFileManager defaultManager]; + auto *resultURLs = [fileManager URLsForDirectory:NSApplicationSupportDirectory + inDomains:NSUserDomainMask]; + if (resultURLs) + { + auto *u = [resultURLs objectAtIndex:0]; + return fs::path{[u fileSystemRepresentation]} / "clap-wrapper-standalone"; + } + return std::nullopt; +} +} // namespace freeaudio::clap_wrapper::standalone diff --git a/src/detail/standalone/standalone_host.cpp b/src/detail/standalone/standalone_host.cpp index e88e61d4..0dc648bf 100644 --- a/src/detail/standalone/standalone_host.cpp +++ b/src/detail/standalone/standalone_host.cpp @@ -1,6 +1,7 @@ #include #include "standalone_host.h" +#include #if LIN #if CLAP_WRAPPER_HAS_GTK3 @@ -10,6 +11,15 @@ namespace freeaudio::clap_wrapper::standalone { + +#if !MAC +std::optional getStandaloneSettingsPath() +{ + TRACE; + return std::nullopt; +} +#endif + StandaloneHost::~StandaloneHost() { } @@ -234,4 +244,74 @@ bool StandaloneHost::unregister_timer(clap_id timer_id) } #endif +static int64_t clapwrite(const clap_ostream *s, const void *buffer, uint64_t size) +{ + auto ofs = static_cast(s->ctx); + ofs->write((const char *)buffer, size); + return size; +} + +static int64_t clapread(const struct clap_istream *s, void *buffer, uint64_t size) +{ + auto ifs = static_cast(s->ctx); + + // Oh this API is so terrible. I think this is right? + ifs->read(static_cast(buffer), size); + if (ifs->rdstate() == std::ios::goodbit || ifs->rdstate() == std::ios::eofbit) return ifs->gcount(); + + if (ifs->rdstate() & std::ios::eofbit) return ifs->gcount(); + + return -1; +} + +bool StandaloneHost::saveStandaloneAndPluginSettings(const fs::path &intoDir, const fs::path &withName) +{ + // This should obviously be a more robust file format. What we + // want is an envelope containing the standalone settings and then + // the streamed plugin data. What we have here is just the streamed + // plugin data with no settings space for audio port selection etc... + + std::ofstream ofs(intoDir / withName, std::ios::out | std::ios::binary); + if (!ofs.is_open()) + { + LOG << "Unable to open for writing " << (intoDir / withName).u8string() << std::endl; + return false; + } + if (!clapPlugin || !clapPlugin->_ext._state) + { + return false; + } + clap_ostream cos{}; + cos.ctx = &ofs; + cos.write = clapwrite; + clapPlugin->_ext._state->save(clapPlugin->_plugin, &cos); + ofs.close(); + + return true; +} + +bool StandaloneHost::tryLoadStandaloneAndPluginSettings(const fs::path &fromDir, + const fs::path &withName) +{ + // see comment above on this file format being not just the + // raw stream in the future + auto fsp = fromDir / withName; + std::ifstream ifs(fsp, std::ios::in | std::ios::binary); + if (!ifs.is_open()) + { + LOG << "Unable to open for reading " << fsp.u8string() << std::endl; + return false; + } + if (!clapPlugin || !clapPlugin->_ext._state) + { + return false; + } + clap_istream cis{}; + cis.ctx = &ifs; + cis.read = clapread; + clapPlugin->_ext._state->load(clapPlugin->_plugin, &cis); + ifs.close(); + return true; +} + } // namespace freeaudio::clap_wrapper::standalone diff --git a/src/detail/standalone/standalone_host.h b/src/detail/standalone/standalone_host.h index 5dbae69d..512466f2 100644 --- a/src/detail/standalone/standalone_host.h +++ b/src/detail/standalone/standalone_host.h @@ -5,9 +5,12 @@ #include #include #include +#include #include "standalone_details.h" +#include "detail/clap/fsutil.h" + #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wall" // other peoples errors are outside my scope @@ -43,6 +46,8 @@ struct Win32Gui; #endif #endif +std::optional getStandaloneSettingsPath(); + struct StandaloneHost : Clap::IHost { StandaloneHost() @@ -129,6 +134,9 @@ struct StandaloneHost : Clap::IHost TRACE; } + bool saveStandaloneAndPluginSettings(const fs::path &intoDir, const fs::path &withName); + bool tryLoadStandaloneAndPluginSettings(const fs::path &fromDir, const fs::path &withName); + uint32_t numAudioInputs{0}, numAudioOutputs{0}; std::vector inputChannelByBus; std::vector outputChannelByBus;