aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp')
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp307
1 files changed, 307 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp
new file mode 100644
index 00000000..44e94e6e
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp
@@ -0,0 +1,307 @@
+/*
+ * VstPresets.cpp
+ * --------------
+ * Purpose: Plugin preset / bank handling
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#include "stdafx.h"
+
+#ifndef NO_PLUGINS
+#include "../soundlib/Sndfile.h"
+#include "../soundlib/plugins/PlugInterface.h"
+#ifdef MPT_WITH_VST
+#include "Vstplug.h"
+#endif // MPT_WITH_VST
+#include "VstPresets.h"
+#include "../common/FileReader.h"
+#include <ostream>
+#include "mpt/io/base.hpp"
+#include "mpt/io/io.hpp"
+#include "mpt/io/io_stdstream.hpp"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+// This part of the header is identical for both presets and banks.
+struct ChunkHeader
+{
+ char chunkMagic[4]; // 'CcnK'
+ int32be byteSize; // Size of this chunk, excluding magic + byteSize
+
+ char fxMagic[4]; // 'FxBk' (regular) or 'FBCh' (opaque chunk)
+ int32be version; // Format version (1 or 2)
+ int32be fxID; // Plugin unique ID
+ int32be fxVersion; // Plugin version
+};
+
+MPT_BINARY_STRUCT(ChunkHeader, 24)
+
+
+VSTPresets::ErrorCode VSTPresets::LoadFile(FileReader &file, IMixPlugin &plugin)
+{
+ const bool firstChunk = file.GetPosition() == 0;
+ ChunkHeader header;
+ if(!file.ReadStruct(header) || memcmp(header.chunkMagic, "CcnK", 4))
+ {
+ return invalidFile;
+ }
+ if(header.fxID != plugin.GetUID())
+ {
+ return wrongPlugin;
+ }
+#ifdef MPT_WITH_VST
+ CVstPlugin *vstPlug = dynamic_cast<CVstPlugin *>(&plugin);
+#endif // MPT_WITH_VST
+
+ if(!memcmp(header.fxMagic, "FxCk", 4) || !memcmp(header.fxMagic, "FPCh", 4))
+ {
+ // Program
+ PlugParamIndex numParams = file.ReadUint32BE();
+
+#ifdef MPT_WITH_VST
+ if(vstPlug != nullptr)
+ {
+ Vst::VstPatchChunkInfo info;
+ info.version = 1;
+ info.pluginUniqueID = header.fxID;
+ info.pluginVersion = header.fxVersion;
+ info.numElements = numParams;
+ MemsetZero(info.reserved);
+ vstPlug->Dispatch(Vst::effBeginLoadProgram, 0, 0, &info, 0.0f);
+ }
+#endif // MPT_WITH_VST
+ plugin.BeginSetProgram();
+
+ std::string prgName;
+ file.ReadString<mpt::String::maybeNullTerminated>(prgName, 28);
+ plugin.SetCurrentProgramName(mpt::ToCString(mpt::Charset::Locale, prgName));
+
+ if(!memcmp(header.fxMagic, "FxCk", 4))
+ {
+ if(plugin.GetNumParameters() != numParams)
+ {
+ return wrongParameters;
+ }
+ for(PlugParamIndex p = 0; p < numParams; p++)
+ {
+ const auto value = file.ReadFloatBE();
+ plugin.SetParameter(p, std::isfinite(value) ? value : 0.0f);
+ }
+ } else
+ {
+ uint32 chunkSize = file.ReadUint32BE();
+ // Some nasty plugins (e.g. SmartElectronix Ambience) write to our memory block.
+ // Directly writing to a memory-mapped file block results in a crash...
+ std::byte *chunkData = new (std::nothrow) std::byte[chunkSize];
+ if(chunkData)
+ {
+ file.ReadRaw(mpt::span(chunkData, chunkSize));
+ plugin.SetChunk(mpt::as_span(chunkData, chunkSize), false);
+ delete[] chunkData;
+ } else
+ {
+ return outOfMemory;
+ }
+ }
+ plugin.EndSetProgram();
+ } else if((!memcmp(header.fxMagic, "FxBk", 4) || !memcmp(header.fxMagic, "FBCh", 4)) && firstChunk)
+ {
+ // Bank - only read if it's the first chunk in the file, not if it's a sub chunk.
+ uint32 numProgs = file.ReadUint32BE();
+ uint32 currentProgram = file.ReadUint32BE();
+ file.Skip(124);
+
+#ifdef MPT_WITH_VST
+ if(vstPlug != nullptr)
+ {
+ Vst::VstPatchChunkInfo info;
+ info.version = 1;
+ info.pluginUniqueID = header.fxID;
+ info.pluginVersion = header.fxVersion;
+ info.numElements = numProgs;
+ MemsetZero(info.reserved);
+ vstPlug->Dispatch(Vst::effBeginLoadBank, 0, 0, &info, 0.0f);
+ }
+#endif // MPT_WITH_VST
+
+ if(!memcmp(header.fxMagic, "FxBk", 4))
+ {
+ int32 oldCurrentProgram = plugin.GetCurrentProgram();
+ for(uint32 p = 0; p < numProgs; p++)
+ {
+ plugin.BeginSetProgram(p);
+ ErrorCode retVal = LoadFile(file, plugin);
+ if(retVal != noError)
+ {
+ return retVal;
+ }
+ plugin.EndSetProgram();
+ }
+ plugin.SetCurrentProgram(oldCurrentProgram);
+ } else
+ {
+ uint32 chunkSize = file.ReadUint32BE();
+ // Some nasty plugins (e.g. SmartElectronix Ambience) write to our memory block.
+ // Directly writing to a memory-mapped file block results in a crash...
+ std::byte *chunkData = new (std::nothrow) std::byte[chunkSize];
+ if(chunkData)
+ {
+ file.ReadRaw(mpt::span(chunkData, chunkSize));
+ plugin.SetChunk(mpt::as_span(chunkData, chunkSize), true);
+ delete[] chunkData;
+ } else
+ {
+ return outOfMemory;
+ }
+ }
+ if(header.version >= 2)
+ {
+ plugin.SetCurrentProgram(currentProgram);
+ }
+ }
+
+ return noError;
+}
+
+
+bool VSTPresets::SaveFile(std::ostream &f, IMixPlugin &plugin, bool bank)
+{
+ if(!bank)
+ {
+ SaveProgram(f, plugin);
+ } else
+ {
+ bool writeChunk = plugin.ProgramsAreChunks();
+ ChunkHeader header;
+ memcpy(header.chunkMagic, "CcnK", 4);
+ header.byteSize = 0; // will be corrected later
+ header.version = 2;
+ header.fxID = plugin.GetUID();
+ header.fxVersion = plugin.GetVersion();
+
+ // Write unfinished header... We need to update the size once we're done writing.
+ mpt::IO::Write(f, header);
+
+ uint32 numProgs = std::max(plugin.GetNumPrograms(), int32(1)), curProg = plugin.GetCurrentProgram();
+ mpt::IO::WriteIntBE(f, numProgs);
+ mpt::IO::WriteIntBE(f, curProg);
+ uint8 reserved[124];
+ MemsetZero(reserved);
+ mpt::IO::WriteRaw(f, reserved, sizeof(reserved));
+
+ if(writeChunk)
+ {
+ auto chunk = plugin.GetChunk(true);
+ uint32 chunkSize = mpt::saturate_cast<uint32>(chunk.size());
+ if(chunkSize)
+ {
+ mpt::IO::WriteIntBE(f, chunkSize);
+ mpt::IO::WriteRaw(f, chunk.data(), chunkSize);
+ } else
+ {
+ // The plugin returned no chunk! Gracefully go back and save parameters instead...
+ writeChunk = false;
+ }
+ }
+ if(!writeChunk)
+ {
+ for(uint32 p = 0; p < numProgs; p++)
+ {
+ plugin.SetCurrentProgram(p);
+ SaveProgram(f, plugin);
+ }
+ plugin.SetCurrentProgram(curProg);
+ }
+
+ // Now we know the correct chunk size.
+ std::streamoff end = f.tellp();
+ header.byteSize = static_cast<int32>(end - 8);
+ memcpy(header.fxMagic, writeChunk ? "FBCh" : "FxBk", 4);
+ mpt::IO::SeekBegin(f);
+ mpt::IO::Write(f, header);
+ }
+
+ return true;
+}
+
+
+void VSTPresets::SaveProgram(std::ostream &f, IMixPlugin &plugin)
+{
+ bool writeChunk = plugin.ProgramsAreChunks();
+ ChunkHeader header;
+ memcpy(header.chunkMagic, "CcnK", 4);
+ header.byteSize = 0; // will be corrected later
+ header.version = 1;
+ header.fxID = plugin.GetUID();
+ header.fxVersion = plugin.GetVersion();
+
+ // Write unfinished header... We need to update the size once we're done writing.
+ mpt::IO::Offset start = mpt::IO::TellWrite(f);
+ mpt::IO::Write(f, header);
+
+ const uint32 numParams = plugin.GetNumParameters();
+ mpt::IO::WriteIntBE(f, numParams);
+
+ char name[28];
+ mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = mpt::ToCharset(mpt::Charset::Locale, plugin.GetCurrentProgramName());
+ mpt::IO::WriteRaw(f, name, 28);
+
+ if(writeChunk)
+ {
+ auto chunk = plugin.GetChunk(false);
+ uint32 chunkSize = mpt::saturate_cast<uint32>(chunk.size());
+ if(chunkSize)
+ {
+ mpt::IO::WriteIntBE(f, chunkSize);
+ mpt::IO::WriteRaw(f, chunk.data(), chunkSize);
+ } else
+ {
+ // The plugin returned no chunk! Gracefully go back and save parameters instead...
+ writeChunk = false;
+ }
+ }
+ if(!writeChunk)
+ {
+ plugin.BeginGetProgram();
+ for(uint32 p = 0; p < numParams; p++)
+ {
+ mpt::IO::Write(f, IEEE754binary32BE(plugin.GetParameter(p)));
+ }
+ plugin.EndGetProgram();
+ }
+
+ // Now we know the correct chunk size.
+ mpt::IO::Offset end = mpt::IO::TellWrite(f);
+ header.byteSize = static_cast<int32>(end - start - 8);
+ memcpy(header.fxMagic, writeChunk ? "FPCh" : "FxCk", 4);
+ mpt::IO::SeekAbsolute(f, start);
+ mpt::IO::Write(f, header);
+ mpt::IO::SeekAbsolute(f, end);
+}
+
+
+// Translate error code to string. Returns nullptr if there was no error.
+const char *VSTPresets::GetErrorMessage(ErrorCode code)
+{
+ switch(code)
+ {
+ case VSTPresets::invalidFile:
+ return "This does not appear to be a valid preset file.";
+ case VSTPresets::wrongPlugin:
+ return "This file appears to be for a different plugin.";
+ case VSTPresets::wrongParameters:
+ return "The number of parameters in this file is incompatible with the current plugin.";
+ case VSTPresets::outOfMemory:
+ return "Not enough memory to load preset data.";
+ }
+ return nullptr;
+}
+
+#endif // NO_PLUGINS
+
+
+OPENMPT_NAMESPACE_END