VstPresets.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * VstPresets.cpp
  3. * --------------
  4. * Purpose: Plugin preset / bank handling
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #ifndef NO_PLUGINS
  11. #include "../soundlib/Sndfile.h"
  12. #include "../soundlib/plugins/PlugInterface.h"
  13. #ifdef MPT_WITH_VST
  14. #include "Vstplug.h"
  15. #endif // MPT_WITH_VST
  16. #include "VstPresets.h"
  17. #include "../common/FileReader.h"
  18. #include <ostream>
  19. #include "mpt/io/base.hpp"
  20. #include "mpt/io/io.hpp"
  21. #include "mpt/io/io_stdstream.hpp"
  22. OPENMPT_NAMESPACE_BEGIN
  23. // This part of the header is identical for both presets and banks.
  24. struct ChunkHeader
  25. {
  26. char chunkMagic[4]; // 'CcnK'
  27. int32be byteSize; // Size of this chunk, excluding magic + byteSize
  28. char fxMagic[4]; // 'FxBk' (regular) or 'FBCh' (opaque chunk)
  29. int32be version; // Format version (1 or 2)
  30. int32be fxID; // Plugin unique ID
  31. int32be fxVersion; // Plugin version
  32. };
  33. MPT_BINARY_STRUCT(ChunkHeader, 24)
  34. VSTPresets::ErrorCode VSTPresets::LoadFile(FileReader &file, IMixPlugin &plugin)
  35. {
  36. const bool firstChunk = file.GetPosition() == 0;
  37. ChunkHeader header;
  38. if(!file.ReadStruct(header) || memcmp(header.chunkMagic, "CcnK", 4))
  39. {
  40. return invalidFile;
  41. }
  42. if(header.fxID != plugin.GetUID())
  43. {
  44. return wrongPlugin;
  45. }
  46. #ifdef MPT_WITH_VST
  47. CVstPlugin *vstPlug = dynamic_cast<CVstPlugin *>(&plugin);
  48. #endif // MPT_WITH_VST
  49. if(!memcmp(header.fxMagic, "FxCk", 4) || !memcmp(header.fxMagic, "FPCh", 4))
  50. {
  51. // Program
  52. PlugParamIndex numParams = file.ReadUint32BE();
  53. #ifdef MPT_WITH_VST
  54. if(vstPlug != nullptr)
  55. {
  56. Vst::VstPatchChunkInfo info;
  57. info.version = 1;
  58. info.pluginUniqueID = header.fxID;
  59. info.pluginVersion = header.fxVersion;
  60. info.numElements = numParams;
  61. MemsetZero(info.reserved);
  62. vstPlug->Dispatch(Vst::effBeginLoadProgram, 0, 0, &info, 0.0f);
  63. }
  64. #endif // MPT_WITH_VST
  65. plugin.BeginSetProgram();
  66. std::string prgName;
  67. file.ReadString<mpt::String::maybeNullTerminated>(prgName, 28);
  68. plugin.SetCurrentProgramName(mpt::ToCString(mpt::Charset::Locale, prgName));
  69. if(!memcmp(header.fxMagic, "FxCk", 4))
  70. {
  71. if(plugin.GetNumParameters() != numParams)
  72. {
  73. return wrongParameters;
  74. }
  75. for(PlugParamIndex p = 0; p < numParams; p++)
  76. {
  77. const auto value = file.ReadFloatBE();
  78. plugin.SetParameter(p, std::isfinite(value) ? value : 0.0f);
  79. }
  80. } else
  81. {
  82. uint32 chunkSize = file.ReadUint32BE();
  83. // Some nasty plugins (e.g. SmartElectronix Ambience) write to our memory block.
  84. // Directly writing to a memory-mapped file block results in a crash...
  85. std::byte *chunkData = new (std::nothrow) std::byte[chunkSize];
  86. if(chunkData)
  87. {
  88. file.ReadRaw(mpt::span(chunkData, chunkSize));
  89. plugin.SetChunk(mpt::as_span(chunkData, chunkSize), false);
  90. delete[] chunkData;
  91. } else
  92. {
  93. return outOfMemory;
  94. }
  95. }
  96. plugin.EndSetProgram();
  97. } else if((!memcmp(header.fxMagic, "FxBk", 4) || !memcmp(header.fxMagic, "FBCh", 4)) && firstChunk)
  98. {
  99. // Bank - only read if it's the first chunk in the file, not if it's a sub chunk.
  100. uint32 numProgs = file.ReadUint32BE();
  101. uint32 currentProgram = file.ReadUint32BE();
  102. file.Skip(124);
  103. #ifdef MPT_WITH_VST
  104. if(vstPlug != nullptr)
  105. {
  106. Vst::VstPatchChunkInfo info;
  107. info.version = 1;
  108. info.pluginUniqueID = header.fxID;
  109. info.pluginVersion = header.fxVersion;
  110. info.numElements = numProgs;
  111. MemsetZero(info.reserved);
  112. vstPlug->Dispatch(Vst::effBeginLoadBank, 0, 0, &info, 0.0f);
  113. }
  114. #endif // MPT_WITH_VST
  115. if(!memcmp(header.fxMagic, "FxBk", 4))
  116. {
  117. int32 oldCurrentProgram = plugin.GetCurrentProgram();
  118. for(uint32 p = 0; p < numProgs; p++)
  119. {
  120. plugin.BeginSetProgram(p);
  121. ErrorCode retVal = LoadFile(file, plugin);
  122. if(retVal != noError)
  123. {
  124. return retVal;
  125. }
  126. plugin.EndSetProgram();
  127. }
  128. plugin.SetCurrentProgram(oldCurrentProgram);
  129. } else
  130. {
  131. uint32 chunkSize = file.ReadUint32BE();
  132. // Some nasty plugins (e.g. SmartElectronix Ambience) write to our memory block.
  133. // Directly writing to a memory-mapped file block results in a crash...
  134. std::byte *chunkData = new (std::nothrow) std::byte[chunkSize];
  135. if(chunkData)
  136. {
  137. file.ReadRaw(mpt::span(chunkData, chunkSize));
  138. plugin.SetChunk(mpt::as_span(chunkData, chunkSize), true);
  139. delete[] chunkData;
  140. } else
  141. {
  142. return outOfMemory;
  143. }
  144. }
  145. if(header.version >= 2)
  146. {
  147. plugin.SetCurrentProgram(currentProgram);
  148. }
  149. }
  150. return noError;
  151. }
  152. bool VSTPresets::SaveFile(std::ostream &f, IMixPlugin &plugin, bool bank)
  153. {
  154. if(!bank)
  155. {
  156. SaveProgram(f, plugin);
  157. } else
  158. {
  159. bool writeChunk = plugin.ProgramsAreChunks();
  160. ChunkHeader header;
  161. memcpy(header.chunkMagic, "CcnK", 4);
  162. header.byteSize = 0; // will be corrected later
  163. header.version = 2;
  164. header.fxID = plugin.GetUID();
  165. header.fxVersion = plugin.GetVersion();
  166. // Write unfinished header... We need to update the size once we're done writing.
  167. mpt::IO::Write(f, header);
  168. uint32 numProgs = std::max(plugin.GetNumPrograms(), int32(1)), curProg = plugin.GetCurrentProgram();
  169. mpt::IO::WriteIntBE(f, numProgs);
  170. mpt::IO::WriteIntBE(f, curProg);
  171. uint8 reserved[124];
  172. MemsetZero(reserved);
  173. mpt::IO::WriteRaw(f, reserved, sizeof(reserved));
  174. if(writeChunk)
  175. {
  176. auto chunk = plugin.GetChunk(true);
  177. uint32 chunkSize = mpt::saturate_cast<uint32>(chunk.size());
  178. if(chunkSize)
  179. {
  180. mpt::IO::WriteIntBE(f, chunkSize);
  181. mpt::IO::WriteRaw(f, chunk.data(), chunkSize);
  182. } else
  183. {
  184. // The plugin returned no chunk! Gracefully go back and save parameters instead...
  185. writeChunk = false;
  186. }
  187. }
  188. if(!writeChunk)
  189. {
  190. for(uint32 p = 0; p < numProgs; p++)
  191. {
  192. plugin.SetCurrentProgram(p);
  193. SaveProgram(f, plugin);
  194. }
  195. plugin.SetCurrentProgram(curProg);
  196. }
  197. // Now we know the correct chunk size.
  198. std::streamoff end = f.tellp();
  199. header.byteSize = static_cast<int32>(end - 8);
  200. memcpy(header.fxMagic, writeChunk ? "FBCh" : "FxBk", 4);
  201. mpt::IO::SeekBegin(f);
  202. mpt::IO::Write(f, header);
  203. }
  204. return true;
  205. }
  206. void VSTPresets::SaveProgram(std::ostream &f, IMixPlugin &plugin)
  207. {
  208. bool writeChunk = plugin.ProgramsAreChunks();
  209. ChunkHeader header;
  210. memcpy(header.chunkMagic, "CcnK", 4);
  211. header.byteSize = 0; // will be corrected later
  212. header.version = 1;
  213. header.fxID = plugin.GetUID();
  214. header.fxVersion = plugin.GetVersion();
  215. // Write unfinished header... We need to update the size once we're done writing.
  216. mpt::IO::Offset start = mpt::IO::TellWrite(f);
  217. mpt::IO::Write(f, header);
  218. const uint32 numParams = plugin.GetNumParameters();
  219. mpt::IO::WriteIntBE(f, numParams);
  220. char name[28];
  221. mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = mpt::ToCharset(mpt::Charset::Locale, plugin.GetCurrentProgramName());
  222. mpt::IO::WriteRaw(f, name, 28);
  223. if(writeChunk)
  224. {
  225. auto chunk = plugin.GetChunk(false);
  226. uint32 chunkSize = mpt::saturate_cast<uint32>(chunk.size());
  227. if(chunkSize)
  228. {
  229. mpt::IO::WriteIntBE(f, chunkSize);
  230. mpt::IO::WriteRaw(f, chunk.data(), chunkSize);
  231. } else
  232. {
  233. // The plugin returned no chunk! Gracefully go back and save parameters instead...
  234. writeChunk = false;
  235. }
  236. }
  237. if(!writeChunk)
  238. {
  239. plugin.BeginGetProgram();
  240. for(uint32 p = 0; p < numParams; p++)
  241. {
  242. mpt::IO::Write(f, IEEE754binary32BE(plugin.GetParameter(p)));
  243. }
  244. plugin.EndGetProgram();
  245. }
  246. // Now we know the correct chunk size.
  247. mpt::IO::Offset end = mpt::IO::TellWrite(f);
  248. header.byteSize = static_cast<int32>(end - start - 8);
  249. memcpy(header.fxMagic, writeChunk ? "FPCh" : "FxCk", 4);
  250. mpt::IO::SeekAbsolute(f, start);
  251. mpt::IO::Write(f, header);
  252. mpt::IO::SeekAbsolute(f, end);
  253. }
  254. // Translate error code to string. Returns nullptr if there was no error.
  255. const char *VSTPresets::GetErrorMessage(ErrorCode code)
  256. {
  257. switch(code)
  258. {
  259. case VSTPresets::invalidFile:
  260. return "This does not appear to be a valid preset file.";
  261. case VSTPresets::wrongPlugin:
  262. return "This file appears to be for a different plugin.";
  263. case VSTPresets::wrongParameters:
  264. return "The number of parameters in this file is incompatible with the current plugin.";
  265. case VSTPresets::outOfMemory:
  266. return "Not enough memory to load preset data.";
  267. }
  268. return nullptr;
  269. }
  270. #endif // NO_PLUGINS
  271. OPENMPT_NAMESPACE_END