diff options
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/VstPresets.cpp | 307 |
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 |