1
0

InstrumentExtensions.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. /*
  2. * InstrumentExtensions.cpp
  3. * ------------------------
  4. * Purpose: Instrument properties I/O
  5. * Notes : Welcome to the absolutely horrible abominations that are the "extended instrument properties"
  6. * which are some of the earliest additions OpenMPT did to the IT / XM format. They are ugly,
  7. * and the way they work even differs between IT/XM and ITI/XI/ITP.
  8. * Yes, the world would be a better place without this stuff.
  9. * Authors: OpenMPT Devs
  10. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  11. */
  12. #include "stdafx.h"
  13. #include "Loaders.h"
  14. #ifndef MODPLUG_NO_FILESAVE
  15. #include "mpt/io/base.hpp"
  16. #include "mpt/io/io.hpp"
  17. #include "mpt/io/io_stdstream.hpp"
  18. #endif
  19. OPENMPT_NAMESPACE_BEGIN
  20. /*---------------------------------------------------------------------------------------------
  21. -----------------------------------------------------------------------------------------------
  22. MODULAR (in/out) ModInstrument :
  23. -----------------------------------------------------------------------------------------------
  24. * to update:
  25. ------------
  26. - both following functions need to be updated when adding a new member in ModInstrument :
  27. void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, int16 fixedsize);
  28. bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, int16 fsize, FileReader &file);
  29. - see below for body declaration.
  30. * members:
  31. ----------
  32. - 32bit identification CODE tag (must be unique)
  33. - 16bit content SIZE in byte(s)
  34. - member field
  35. * CODE tag naming convention:
  36. -----------------------------
  37. - have a look below in current tag dictionnary
  38. - take the initial ones of the field name
  39. - 4 caracters code (not more, not less)
  40. - must be filled with '.' caracters if code has less than 4 caracters
  41. - for arrays, must include a '[' caracter following significant caracters ('.' not significant!!!)
  42. - use only caracters used in full member name, ordered as they appear in it
  43. - match caracter attribute (small,capital)
  44. Example with "PanEnv.nLoopEnd" , "PitchEnv.nLoopEnd" & "VolEnv.Values[MAX_ENVPOINTS]" members :
  45. - use 'PLE.' for PanEnv.nLoopEnd
  46. - use 'PiLE' for PitchEnv.nLoopEnd
  47. - use 'VE[.' for VolEnv.Values[MAX_ENVPOINTS]
  48. * In use CODE tag dictionary (alphabetical order):
  49. --------------------------------------------------
  50. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  51. !!! SECTION TO BE UPDATED !!!
  52. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  53. [EXT] means external (not related) to ModInstrument content
  54. AUTH [EXT] Song artist
  55. C... [EXT] nChannels
  56. ChnS [EXT] IT/MPTM: Channel settings for channels 65-127 if needed (doesn't fit to IT header).
  57. CS.. nCutSwing
  58. CUES [EXT] Sample cue points
  59. CWV. [EXT] dwCreatedWithVersion
  60. DCT. nDCT;
  61. dF.. dwFlags;
  62. DGV. [EXT] nDefaultGlobalVolume
  63. DT.. [EXT] nDefaultTempo;
  64. DTFR [EXT] Fractional part of default tempo
  65. DNA. nDNA;
  66. EBIH [EXT] embeded instrument header tag (ITP file format)
  67. FM.. filterMode;
  68. fn[. filename[12];
  69. FO.. nFadeOut;
  70. GV.. nGlobalVol;
  71. IFC. nIFC;
  72. IFR. nIFR;
  73. K[. Keyboard[128];
  74. LSWV [EXT] Last Saved With Version
  75. MB.. wMidiBank;
  76. MC.. nMidiChannel;
  77. MDK. nMidiDrumKey;
  78. MIMA [EXT] MIdi MApping directives
  79. MiP. nMixPlug;
  80. MP.. nMidiProgram;
  81. MPTS [EXT] Extra song info tag
  82. MPTX [EXT] EXTRA INFO tag
  83. MSF. [EXT] Mod(Specific)Flags
  84. n[.. name[32];
  85. NNA. nNNA;
  86. NM[. NoteMap[128];
  87. P... nPan;
  88. PE.. PanEnv.nNodes;
  89. PE[. PanEnv.Values[MAX_ENVPOINTS];
  90. PiE. PitchEnv.nNodes;
  91. PiE[ PitchEnv.Values[MAX_ENVPOINTS];
  92. PiLE PitchEnv.nLoopEnd;
  93. PiLS PitchEnv.nLoopStart;
  94. PiP[ PitchEnv.Ticks[MAX_ENVPOINTS];
  95. PiSB PitchEnv.nSustainStart;
  96. PiSE PitchEnv.nSustainEnd;
  97. PLE. PanEnv.nLoopEnd;
  98. PLS. PanEnv.nLoopStart;
  99. PMM. [EXT] nPlugMixMode;
  100. PP[. PanEnv.Ticks[MAX_ENVPOINTS];
  101. PPC. nPPC;
  102. PPS. nPPS;
  103. PS.. nPanSwing;
  104. PSB. PanEnv.nSustainStart;
  105. PSE. PanEnv.nSustainEnd;
  106. PTTL pitchToTempoLock;
  107. PTTF pitchToTempoLock (fractional part);
  108. PVEH pluginVelocityHandling;
  109. PVOH pluginVolumeHandling;
  110. R... resampling;
  111. RP.. [EXT] nRestartPos;
  112. RPB. [EXT] nRowsPerBeat;
  113. RPM. [EXT] nRowsPerMeasure;
  114. RS.. nResSwing;
  115. RSMP [EXT] Global resampling
  116. SEP@ [EXT] chunk SEPARATOR tag
  117. SPA. [EXT] m_nSamplePreAmp;
  118. TM.. [EXT] nTempoMode;
  119. VE.. VolEnv.nNodes;
  120. VE[. VolEnv.Values[MAX_ENVPOINTS];
  121. VLE. VolEnv.nLoopEnd;
  122. VLS. VolEnv.nLoopStart;
  123. VP[. VolEnv.Ticks[MAX_ENVPOINTS];
  124. VR.. nVolRampUp;
  125. VS.. nVolSwing;
  126. VSB. VolEnv.nSustainStart;
  127. VSE. VolEnv.nSustainEnd;
  128. VSTV [EXT] nVSTiVolume;
  129. PERN PitchEnv.nReleaseNode
  130. AERN PanEnv.nReleaseNode
  131. VERN VolEnv.nReleaseNode
  132. PFLG PitchEnv.dwFlag
  133. AFLG PanEnv.dwFlags
  134. VFLG VolEnv.dwFlags
  135. MPWD MIDI Pitch Wheel Depth
  136. -----------------------------------------------------------------------------------------------
  137. ---------------------------------------------------------------------------------------------*/
  138. #ifndef MODPLUG_NO_FILESAVE
  139. template<typename T, bool is_signed> struct IsNegativeFunctor { bool operator()(T val) const { return val < 0; } };
  140. template<typename T> struct IsNegativeFunctor<T, true> { bool operator()(T val) const { return val < 0; } };
  141. template<typename T> struct IsNegativeFunctor<T, false> { bool operator()(T /*val*/) const { return false; } };
  142. template<typename T>
  143. bool IsNegative(const T &val)
  144. {
  145. return IsNegativeFunctor<T, std::numeric_limits<T>::is_signed>()(val);
  146. }
  147. // ------------------------------------------------------------------------------------------
  148. // Convenient macro to help WRITE_HEADER declaration for single type members ONLY (non-array)
  149. // ------------------------------------------------------------------------------------------
  150. #define WRITE_MPTHEADER_sized_member(name,type,code) \
  151. static_assert(sizeof(input->name) == sizeof(type), "Instrument property does match specified type!");\
  152. fcode = code;\
  153. fsize = sizeof( type );\
  154. if(writeAll) \
  155. { \
  156. mpt::IO::WriteIntLE<uint32>(file, fcode); \
  157. mpt::IO::WriteIntLE<uint16>(file, fsize); \
  158. } else if(only_this_code == fcode)\
  159. { \
  160. MPT_ASSERT(fixedsize == fsize); \
  161. } \
  162. if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \
  163. { \
  164. type tmp = (type)(input-> name ); \
  165. mpt::IO::WriteIntLE(file, tmp); \
  166. } \
  167. /**/
  168. // -----------------------------------------------------------------------------------------------------
  169. // Convenient macro to help WRITE_HEADER declaration for single type members which are written truncated
  170. // -----------------------------------------------------------------------------------------------------
  171. #define WRITE_MPTHEADER_trunc_member(name,type,code) \
  172. static_assert(sizeof(input->name) > sizeof(type), "Instrument property would not be truncated, use WRITE_MPTHEADER_sized_member instead!");\
  173. fcode = code;\
  174. fsize = sizeof( type );\
  175. if(writeAll) \
  176. { \
  177. mpt::IO::WriteIntLE<uint32>(file, fcode); \
  178. mpt::IO::WriteIntLE<uint16>(file, fsize); \
  179. type tmp = (type)(input-> name ); \
  180. mpt::IO::WriteIntLE(file, tmp); \
  181. } else if(only_this_code == fcode)\
  182. { \
  183. /* hackish workaround to resolve mismatched size values: */ \
  184. /* nResampling was a long time declared as uint32 but these macro tables used uint16 and UINT. */ \
  185. /* This worked fine on little-endian, on big-endian not so much. Thus support writing size-mismatched fields. */ \
  186. MPT_ASSERT(fixedsize >= fsize); \
  187. type tmp = (type)(input-> name ); \
  188. mpt::IO::WriteIntLE(file, tmp); \
  189. if(fixedsize > fsize) \
  190. { \
  191. for(int16 i = 0; i < fixedsize - fsize; ++i) \
  192. { \
  193. uint8 fillbyte = !IsNegative(tmp) ? 0 : 0xff; /* sign extend */ \
  194. mpt::IO::WriteIntLE(file, fillbyte); \
  195. } \
  196. } \
  197. } \
  198. /**/
  199. // ------------------------------------------------------------------------
  200. // Convenient macro to help WRITE_HEADER declaration for array members ONLY
  201. // ------------------------------------------------------------------------
  202. #define WRITE_MPTHEADER_array_member(name,type,code,arraysize) \
  203. static_assert(sizeof(type) == sizeof(input-> name [0])); \
  204. MPT_ASSERT(sizeof(input->name) >= sizeof(type) * arraysize);\
  205. fcode = code;\
  206. fsize = sizeof( type ) * arraysize;\
  207. if(writeAll) \
  208. { \
  209. mpt::IO::WriteIntLE<uint32>(file, fcode); \
  210. mpt::IO::WriteIntLE<uint16>(file, fsize); \
  211. } else if(only_this_code == fcode)\
  212. { \
  213. /* MPT_ASSERT(fixedsize <= fsize); */ \
  214. fsize = fixedsize; /* just trust the size we got passed */ \
  215. } \
  216. if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \
  217. { \
  218. for(std::size_t i = 0; i < fsize/sizeof(type); ++i) \
  219. { \
  220. type tmp; \
  221. tmp = input-> name [i]; \
  222. mpt::IO::WriteIntLE(file, tmp); \
  223. } \
  224. } \
  225. /**/
  226. // ------------------------------------------------------------------------
  227. // Convenient macro to help WRITE_HEADER declaration for envelope members ONLY
  228. // ------------------------------------------------------------------------
  229. #define WRITE_MPTHEADER_envelope_member(envType,envField,type,code) \
  230. {\
  231. const InstrumentEnvelope &env = input->GetEnvelope(envType); \
  232. static_assert(sizeof(type) == sizeof(env[0]. envField)); \
  233. fcode = code;\
  234. fsize = mpt::saturate_cast<int16>(sizeof( type ) * env.size());\
  235. MPT_ASSERT(size_t(fsize) == sizeof( type ) * env.size()); \
  236. \
  237. if(writeAll) \
  238. { \
  239. mpt::IO::WriteIntLE<uint32>(file, fcode); \
  240. mpt::IO::WriteIntLE<uint16>(file, fsize); \
  241. } else if(only_this_code == fcode)\
  242. { \
  243. fsize = fixedsize; /* just trust the size we got passed */ \
  244. } \
  245. if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \
  246. { \
  247. uint32 maxNodes = std::min(static_cast<uint32>(fsize/sizeof(type)), static_cast<uint32>(env.size())); \
  248. for(uint32 i = 0; i < maxNodes; ++i) \
  249. { \
  250. type tmp; \
  251. tmp = env[i]. envField ; \
  252. mpt::IO::WriteIntLE(file, tmp); \
  253. } \
  254. /* Not every instrument's envelope will be the same length. fill up with zeros. */ \
  255. for(uint32 i = maxNodes; i < fsize/sizeof(type); ++i) \
  256. { \
  257. type tmp = 0; \
  258. mpt::IO::WriteIntLE(file, tmp); \
  259. } \
  260. } \
  261. }\
  262. /**/
  263. // Write (in 'file') 'input' ModInstrument with 'code' & 'size' extra field infos for each member
  264. void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, uint16 fixedsize)
  265. {
  266. uint32 fcode;
  267. uint16 fsize;
  268. // If true, all extension are written to the file; otherwise only the specified extension is written.
  269. // writeAll is true iff we are saving an instrument (or, hypothetically, the legacy ITP format)
  270. const bool writeAll = only_this_code == Util::MaxValueOfType(only_this_code);
  271. if(!writeAll)
  272. {
  273. MPT_ASSERT(fixedsize > 0);
  274. }
  275. // clang-format off
  276. WRITE_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") )
  277. WRITE_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") )
  278. WRITE_MPTHEADER_sized_member( VolEnv.size() , uint32 , MagicBE("VE..") )
  279. WRITE_MPTHEADER_sized_member( PanEnv.size() , uint32 , MagicBE("PE..") )
  280. WRITE_MPTHEADER_sized_member( PitchEnv.size() , uint32 , MagicBE("PiE.") )
  281. WRITE_MPTHEADER_sized_member( wMidiBank , uint16 , MagicBE("MB..") )
  282. WRITE_MPTHEADER_sized_member( nMidiProgram , uint8 , MagicBE("MP..") )
  283. WRITE_MPTHEADER_sized_member( nMidiChannel , uint8 , MagicBE("MC..") )
  284. WRITE_MPTHEADER_envelope_member( ENV_VOLUME , tick , uint16 , MagicBE("VP[.") )
  285. WRITE_MPTHEADER_envelope_member( ENV_PANNING , tick , uint16 , MagicBE("PP[.") )
  286. WRITE_MPTHEADER_envelope_member( ENV_PITCH , tick , uint16 , MagicBE("PiP[") )
  287. WRITE_MPTHEADER_envelope_member( ENV_VOLUME , value , uint8 , MagicBE("VE[.") )
  288. WRITE_MPTHEADER_envelope_member( ENV_PANNING , value , uint8 , MagicBE("PE[.") )
  289. WRITE_MPTHEADER_envelope_member( ENV_PITCH , value , uint8 , MagicBE("PiE[") )
  290. WRITE_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") )
  291. WRITE_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") )
  292. WRITE_MPTHEADER_sized_member( resampling , uint8 , MagicBE("R...") )
  293. WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") )
  294. WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") )
  295. WRITE_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") )
  296. WRITE_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") )
  297. WRITE_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") )
  298. WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MagicBE("PTTL") )
  299. WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetFract() , uint16 , MagicLE("PTTF") )
  300. WRITE_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") )
  301. WRITE_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") )
  302. WRITE_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") )
  303. WRITE_MPTHEADER_sized_member( PitchEnv.dwFlags , uint8 , MagicBE("PFLG") )
  304. WRITE_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") )
  305. WRITE_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") )
  306. WRITE_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") )
  307. // clang-format on
  308. }
  309. template<typename TIns, typename PropType>
  310. static bool IsPropertyNeeded(const TIns &Instruments, PropType ModInstrument::*Prop)
  311. {
  312. const ModInstrument defaultIns;
  313. for(const auto &ins : Instruments)
  314. {
  315. if(ins != nullptr && defaultIns.*Prop != ins->*Prop)
  316. return true;
  317. }
  318. return false;
  319. }
  320. template<typename PropType>
  321. static void WritePropertyIfNeeded(const CSoundFile &sndFile, PropType ModInstrument::*Prop, uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX numInstruments)
  322. {
  323. if(IsPropertyNeeded(sndFile.Instruments, Prop))
  324. {
  325. sndFile.WriteInstrumentPropertyForAllInstruments(code, size, f, numInstruments);
  326. }
  327. }
  328. // Used only when saving IT, XM and MPTM.
  329. // ITI, ITP saves using Ericus' macros etc...
  330. // The reason is that ITs and XMs save [code][size][ins1.Value][ins2.Value]...
  331. // whereas ITP saves [code][size][ins1.Value][code][size][ins2.Value]...
  332. // too late to turn back....
  333. void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX numInstruments, std::ostream &f) const
  334. {
  335. uint32 code = MagicBE("MPTX"); // write extension header code
  336. mpt::IO::WriteIntLE<uint32>(f, code);
  337. if (numInstruments == 0)
  338. return;
  339. WritePropertyIfNeeded(*this, &ModInstrument::nVolRampUp, MagicBE("VR.."), sizeof(ModInstrument::nVolRampUp), f, numInstruments);
  340. WritePropertyIfNeeded(*this, &ModInstrument::nMixPlug, MagicBE("MiP."), sizeof(ModInstrument::nMixPlug), f, numInstruments);
  341. WritePropertyIfNeeded(*this, &ModInstrument::nMidiChannel, MagicBE("MC.."), sizeof(ModInstrument::nMidiChannel), f, numInstruments);
  342. WritePropertyIfNeeded(*this, &ModInstrument::nMidiProgram, MagicBE("MP.."), sizeof(ModInstrument::nMidiProgram), f, numInstruments);
  343. WritePropertyIfNeeded(*this, &ModInstrument::wMidiBank, MagicBE("MB.."), sizeof(ModInstrument::wMidiBank), f, numInstruments);
  344. WritePropertyIfNeeded(*this, &ModInstrument::resampling, MagicBE("R..."), sizeof(ModInstrument::resampling), f, numInstruments);
  345. WritePropertyIfNeeded(*this, &ModInstrument::pluginVelocityHandling, MagicBE("PVEH"), sizeof(ModInstrument::pluginVelocityHandling), f, numInstruments);
  346. WritePropertyIfNeeded(*this, &ModInstrument::pluginVolumeHandling, MagicBE("PVOH"), sizeof(ModInstrument::pluginVolumeHandling), f, numInstruments);
  347. if(!(GetType() & MOD_TYPE_XM))
  348. {
  349. // XM instrument headers already stores full-precision fade-out
  350. WritePropertyIfNeeded(*this, &ModInstrument::nFadeOut, MagicBE("FO.."), sizeof(ModInstrument::nFadeOut), f, numInstruments);
  351. // XM instrument headers already have support for this
  352. WritePropertyIfNeeded(*this, &ModInstrument::midiPWD, MagicBE("MPWD"), sizeof(ModInstrument::midiPWD), f, numInstruments);
  353. // We never supported these as hacks in XM (luckily!)
  354. WritePropertyIfNeeded(*this, &ModInstrument::nPan, MagicBE("P..."), sizeof(ModInstrument::nPan), f, numInstruments);
  355. WritePropertyIfNeeded(*this, &ModInstrument::nCutSwing, MagicBE("CS.."), sizeof(ModInstrument::nCutSwing), f, numInstruments);
  356. WritePropertyIfNeeded(*this, &ModInstrument::nResSwing, MagicBE("RS.."), sizeof(ModInstrument::nResSwing), f, numInstruments);
  357. WritePropertyIfNeeded(*this, &ModInstrument::filterMode, MagicBE("FM.."), sizeof(ModInstrument::filterMode), f, numInstruments);
  358. if(IsPropertyNeeded(Instruments, &ModInstrument::pitchToTempoLock))
  359. {
  360. WriteInstrumentPropertyForAllInstruments(MagicBE("PTTL"), sizeof(uint16), f, numInstruments);
  361. WriteInstrumentPropertyForAllInstruments(MagicLE("PTTF"), sizeof(uint16), f, numInstruments);
  362. }
  363. }
  364. if(GetType() & MOD_TYPE_MPT)
  365. {
  366. uint32 maxNodes[3] = { 0, 0, 0 };
  367. bool hasReleaseNode[3] = { false, false, false };
  368. for(INSTRUMENTINDEX i = 1; i <= numInstruments; i++) if(Instruments[i] != nullptr)
  369. {
  370. maxNodes[0] = std::max(maxNodes[0], Instruments[i]->VolEnv.size());
  371. maxNodes[1] = std::max(maxNodes[1], Instruments[i]->PanEnv.size());
  372. maxNodes[2] = std::max(maxNodes[2], Instruments[i]->PitchEnv.size());
  373. hasReleaseNode[0] |= (Instruments[i]->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET);
  374. hasReleaseNode[1] |= (Instruments[i]->PanEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET);
  375. hasReleaseNode[2] |= (Instruments[i]->PitchEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET);
  376. }
  377. // write full envelope information for MPTM files (more env points)
  378. if(maxNodes[0] > 25)
  379. {
  380. WriteInstrumentPropertyForAllInstruments(MagicBE("VE.."), sizeof(ModInstrument::VolEnv.size()), f, numInstruments);
  381. WriteInstrumentPropertyForAllInstruments(MagicBE("VP[."), static_cast<uint16>(maxNodes[0] * sizeof(EnvelopeNode::tick)), f, numInstruments);
  382. WriteInstrumentPropertyForAllInstruments(MagicBE("VE[."), static_cast<uint16>(maxNodes[0] * sizeof(EnvelopeNode::value)), f, numInstruments);
  383. }
  384. if(maxNodes[1] > 25)
  385. {
  386. WriteInstrumentPropertyForAllInstruments(MagicBE("PE.."), sizeof(ModInstrument::PanEnv.size()), f, numInstruments);
  387. WriteInstrumentPropertyForAllInstruments(MagicBE("PP[."), static_cast<uint16>(maxNodes[1] * sizeof(EnvelopeNode::tick)), f, numInstruments);
  388. WriteInstrumentPropertyForAllInstruments(MagicBE("PE[."), static_cast<uint16>(maxNodes[1] * sizeof(EnvelopeNode::value)), f, numInstruments);
  389. }
  390. if(maxNodes[2] > 25)
  391. {
  392. WriteInstrumentPropertyForAllInstruments(MagicBE("PiE."), sizeof(ModInstrument::PitchEnv.size()), f, numInstruments);
  393. WriteInstrumentPropertyForAllInstruments(MagicBE("PiP["), static_cast<uint16>(maxNodes[2] * sizeof(EnvelopeNode::tick)), f, numInstruments);
  394. WriteInstrumentPropertyForAllInstruments(MagicBE("PiE["), static_cast<uint16>(maxNodes[2] * sizeof(EnvelopeNode::value)), f, numInstruments);
  395. }
  396. if(hasReleaseNode[0])
  397. WriteInstrumentPropertyForAllInstruments(MagicBE("VERN"), sizeof(ModInstrument::VolEnv.nReleaseNode), f, numInstruments);
  398. if(hasReleaseNode[1])
  399. WriteInstrumentPropertyForAllInstruments(MagicBE("AERN"), sizeof(ModInstrument::PanEnv.nReleaseNode), f, numInstruments);
  400. if(hasReleaseNode[2])
  401. WriteInstrumentPropertyForAllInstruments(MagicBE("PERN"), sizeof(ModInstrument::PitchEnv.nReleaseNode), f, numInstruments);
  402. }
  403. }
  404. void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 size, std::ostream &f, INSTRUMENTINDEX nInstruments) const
  405. {
  406. mpt::IO::WriteIntLE<uint32>(f, code); //write code
  407. mpt::IO::WriteIntLE<uint16>(f, size); //write size
  408. for(INSTRUMENTINDEX i = 1; i <= nInstruments; i++) //for all instruments...
  409. {
  410. if (Instruments[i])
  411. {
  412. WriteInstrumentHeaderStructOrField(Instruments[i], f, code, size);
  413. } else
  414. {
  415. ModInstrument emptyInstrument;
  416. WriteInstrumentHeaderStructOrField(&emptyInstrument, f, code, size);
  417. }
  418. }
  419. }
  420. #endif // !MODPLUG_NO_FILESAVE
  421. // --------------------------------------------------------------------------------------------
  422. // Convenient macro to help GET_HEADER declaration for single type members ONLY (non-array)
  423. // --------------------------------------------------------------------------------------------
  424. #define GET_MPTHEADER_sized_member(name,type,code) \
  425. case code: \
  426. {\
  427. if( fsize <= sizeof( type ) ) \
  428. { \
  429. /* hackish workaround to resolve mismatched size values: */ \
  430. /* nResampling was a long time declared as uint32 but these macro tables used uint16 and UINT. */ \
  431. /* This worked fine on little-endian, on big-endian not so much. Thus support reading size-mismatched fields. */ \
  432. if(file.CanRead(fsize)) \
  433. { \
  434. type tmp; \
  435. tmp = file.ReadTruncatedIntLE<type>(fsize); \
  436. static_assert(sizeof(tmp) == sizeof(input-> name )); \
  437. input-> name = decltype(input-> name )(tmp); \
  438. result = true; \
  439. } \
  440. } \
  441. } break;
  442. // --------------------------------------------------------------------------------------------
  443. // Convenient macro to help GET_HEADER declaration for array members ONLY
  444. // --------------------------------------------------------------------------------------------
  445. #define GET_MPTHEADER_array_member(name,type,code) \
  446. case code: \
  447. {\
  448. if( fsize <= sizeof( type ) * std::size(input-> name) ) \
  449. { \
  450. FileReader arrayChunk = file.ReadChunk(fsize); \
  451. for(std::size_t i = 0; i < std::size(input-> name); ++i) \
  452. { \
  453. input-> name [i] = arrayChunk.ReadIntLE<type>(); \
  454. } \
  455. result = true; \
  456. } \
  457. } break;
  458. // --------------------------------------------------------------------------------------------
  459. // Convenient macro to help GET_HEADER declaration for character buffer members ONLY
  460. // --------------------------------------------------------------------------------------------
  461. #define GET_MPTHEADER_charbuf_member(name,type,code) \
  462. case code: \
  463. {\
  464. if( fsize <= sizeof( type ) * input-> name .static_length() ) \
  465. { \
  466. FileReader arrayChunk = file.ReadChunk(fsize); \
  467. std::string tmp; \
  468. for(std::size_t i = 0; i < fsize; ++i) \
  469. { \
  470. tmp += arrayChunk.ReadChar(); \
  471. } \
  472. input-> name = tmp; \
  473. result = true; \
  474. } \
  475. } break;
  476. // --------------------------------------------------------------------------------------------
  477. // Convenient macro to help GET_HEADER declaration for envelope tick/value members
  478. // --------------------------------------------------------------------------------------------
  479. #define GET_MPTHEADER_envelope_member(envType,envField,type,code) \
  480. case code: \
  481. {\
  482. FileReader arrayChunk = file.ReadChunk(fsize); \
  483. InstrumentEnvelope &env = input->GetEnvelope(envType); \
  484. for(uint32 i = 0; i < env.size(); i++) \
  485. { \
  486. env[i]. envField = arrayChunk.ReadIntLE<type>(); \
  487. } \
  488. result = true; \
  489. } break;
  490. // Return a pointer on the wanted field in 'input' ModInstrument given field code & size
  491. bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, FileReader &file)
  492. {
  493. if(input == nullptr) return false;
  494. bool result = false;
  495. // Members which can be found in this table but not in the write table are only required in the legacy ITP format.
  496. switch(fcode)
  497. {
  498. // clang-format off
  499. GET_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") )
  500. GET_MPTHEADER_sized_member( dwFlags , uint8 , MagicBE("dF..") )
  501. GET_MPTHEADER_sized_member( nGlobalVol , uint32 , MagicBE("GV..") )
  502. GET_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") )
  503. GET_MPTHEADER_sized_member( VolEnv.nLoopStart , uint8 , MagicBE("VLS.") )
  504. GET_MPTHEADER_sized_member( VolEnv.nLoopEnd , uint8 , MagicBE("VLE.") )
  505. GET_MPTHEADER_sized_member( VolEnv.nSustainStart , uint8 , MagicBE("VSB.") )
  506. GET_MPTHEADER_sized_member( VolEnv.nSustainEnd , uint8 , MagicBE("VSE.") )
  507. GET_MPTHEADER_sized_member( PanEnv.nLoopStart , uint8 , MagicBE("PLS.") )
  508. GET_MPTHEADER_sized_member( PanEnv.nLoopEnd , uint8 , MagicBE("PLE.") )
  509. GET_MPTHEADER_sized_member( PanEnv.nSustainStart , uint8 , MagicBE("PSB.") )
  510. GET_MPTHEADER_sized_member( PanEnv.nSustainEnd , uint8 , MagicBE("PSE.") )
  511. GET_MPTHEADER_sized_member( PitchEnv.nLoopStart , uint8 , MagicBE("PiLS") )
  512. GET_MPTHEADER_sized_member( PitchEnv.nLoopEnd , uint8 , MagicBE("PiLE") )
  513. GET_MPTHEADER_sized_member( PitchEnv.nSustainStart , uint8 , MagicBE("PiSB") )
  514. GET_MPTHEADER_sized_member( PitchEnv.nSustainEnd , uint8 , MagicBE("PiSE") )
  515. GET_MPTHEADER_sized_member( nNNA , uint8 , MagicBE("NNA.") )
  516. GET_MPTHEADER_sized_member( nDCT , uint8 , MagicBE("DCT.") )
  517. GET_MPTHEADER_sized_member( nDNA , uint8 , MagicBE("DNA.") )
  518. GET_MPTHEADER_sized_member( nPanSwing , uint8 , MagicBE("PS..") )
  519. GET_MPTHEADER_sized_member( nVolSwing , uint8 , MagicBE("VS..") )
  520. GET_MPTHEADER_sized_member( nIFC , uint8 , MagicBE("IFC.") )
  521. GET_MPTHEADER_sized_member( nIFR , uint8 , MagicBE("IFR.") )
  522. GET_MPTHEADER_sized_member( wMidiBank , uint16 , MagicBE("MB..") )
  523. GET_MPTHEADER_sized_member( nMidiProgram , uint8 , MagicBE("MP..") )
  524. GET_MPTHEADER_sized_member( nMidiChannel , uint8 , MagicBE("MC..") )
  525. GET_MPTHEADER_sized_member( nPPS , int8 , MagicBE("PPS.") )
  526. GET_MPTHEADER_sized_member( nPPC , uint8 , MagicBE("PPC.") )
  527. GET_MPTHEADER_envelope_member(ENV_VOLUME , tick , uint16 , MagicBE("VP[.") )
  528. GET_MPTHEADER_envelope_member(ENV_PANNING , tick , uint16 , MagicBE("PP[.") )
  529. GET_MPTHEADER_envelope_member(ENV_PITCH , tick , uint16 , MagicBE("PiP[") )
  530. GET_MPTHEADER_envelope_member(ENV_VOLUME , value , uint8 , MagicBE("VE[.") )
  531. GET_MPTHEADER_envelope_member(ENV_PANNING , value , uint8 , MagicBE("PE[.") )
  532. GET_MPTHEADER_envelope_member(ENV_PITCH , value , uint8 , MagicBE("PiE[") )
  533. GET_MPTHEADER_array_member( NoteMap , uint8 , MagicBE("NM[.") )
  534. GET_MPTHEADER_array_member( Keyboard , uint16 , MagicBE("K[..") )
  535. GET_MPTHEADER_charbuf_member( name , char , MagicBE("n[..") )
  536. GET_MPTHEADER_charbuf_member( filename , char , MagicBE("fn[.") )
  537. GET_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") )
  538. GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") )
  539. GET_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") )
  540. GET_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") )
  541. GET_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") )
  542. GET_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") )
  543. GET_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") )
  544. GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") )
  545. GET_MPTHEADER_sized_member( PanEnv.nReleaseNode , uint8 , MagicBE("AERN") )
  546. GET_MPTHEADER_sized_member( VolEnv.nReleaseNode , uint8 , MagicBE("VERN") )
  547. GET_MPTHEADER_sized_member( PitchEnv.dwFlags , uint8 , MagicBE("PFLG") )
  548. GET_MPTHEADER_sized_member( PanEnv.dwFlags , uint8 , MagicBE("AFLG") )
  549. GET_MPTHEADER_sized_member( VolEnv.dwFlags , uint8 , MagicBE("VFLG") )
  550. GET_MPTHEADER_sized_member( midiPWD , int8 , MagicBE("MPWD") )
  551. // clang-format on
  552. case MagicBE("R..."):
  553. {
  554. // Resampling has been written as various sizes including uint16 and uint32 in the past
  555. uint32 tmp = file.ReadSizedIntLE<uint32>(fsize);
  556. if(Resampling::IsKnownMode(tmp))
  557. input->resampling = static_cast<ResamplingMode>(tmp);
  558. result = true;
  559. } break;
  560. case MagicBE("PTTL"):
  561. {
  562. // Integer part of pitch/tempo lock
  563. uint16 tmp = file.ReadSizedIntLE<uint16>(fsize);
  564. input->pitchToTempoLock.Set(tmp, input->pitchToTempoLock.GetFract());
  565. result = true;
  566. } break;
  567. case MagicLE("PTTF"):
  568. {
  569. // Fractional part of pitch/tempo lock
  570. uint16 tmp = file.ReadSizedIntLE<uint16>(fsize);
  571. input->pitchToTempoLock.Set(input->pitchToTempoLock.GetInt(), tmp);
  572. result = true;
  573. } break;
  574. case MagicBE("VE.."):
  575. input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE<uint32>(fsize)));
  576. result = true;
  577. break;
  578. case MagicBE("PE.."):
  579. input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE<uint32>(fsize)));
  580. result = true;
  581. break;
  582. case MagicBE("PiE."):
  583. input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadSizedIntLE<uint32>(fsize)));
  584. result = true;
  585. break;
  586. }
  587. return result;
  588. }
  589. // Convert instrument flags which were read from 'dF..' extension to proper internal representation.
  590. static void ConvertReadExtendedFlags(ModInstrument *pIns)
  591. {
  592. // Flags of 'dF..' datafield in extended instrument properties.
  593. enum
  594. {
  595. dFdd_VOLUME = 0x0001,
  596. dFdd_VOLSUSTAIN = 0x0002,
  597. dFdd_VOLLOOP = 0x0004,
  598. dFdd_PANNING = 0x0008,
  599. dFdd_PANSUSTAIN = 0x0010,
  600. dFdd_PANLOOP = 0x0020,
  601. dFdd_PITCH = 0x0040,
  602. dFdd_PITCHSUSTAIN = 0x0080,
  603. dFdd_PITCHLOOP = 0x0100,
  604. dFdd_SETPANNING = 0x0200,
  605. dFdd_FILTER = 0x0400,
  606. dFdd_VOLCARRY = 0x0800,
  607. dFdd_PANCARRY = 0x1000,
  608. dFdd_PITCHCARRY = 0x2000,
  609. dFdd_MUTE = 0x4000,
  610. };
  611. const uint32 dwOldFlags = pIns->dwFlags.GetRaw();
  612. pIns->VolEnv.dwFlags.set(ENV_ENABLED, (dwOldFlags & dFdd_VOLUME) != 0);
  613. pIns->VolEnv.dwFlags.set(ENV_SUSTAIN, (dwOldFlags & dFdd_VOLSUSTAIN) != 0);
  614. pIns->VolEnv.dwFlags.set(ENV_LOOP, (dwOldFlags & dFdd_VOLLOOP) != 0);
  615. pIns->VolEnv.dwFlags.set(ENV_CARRY, (dwOldFlags & dFdd_VOLCARRY) != 0);
  616. pIns->PanEnv.dwFlags.set(ENV_ENABLED, (dwOldFlags & dFdd_PANNING) != 0);
  617. pIns->PanEnv.dwFlags.set(ENV_SUSTAIN, (dwOldFlags & dFdd_PANSUSTAIN) != 0);
  618. pIns->PanEnv.dwFlags.set(ENV_LOOP, (dwOldFlags & dFdd_PANLOOP) != 0);
  619. pIns->PanEnv.dwFlags.set(ENV_CARRY, (dwOldFlags & dFdd_PANCARRY) != 0);
  620. pIns->PitchEnv.dwFlags.set(ENV_ENABLED, (dwOldFlags & dFdd_PITCH) != 0);
  621. pIns->PitchEnv.dwFlags.set(ENV_SUSTAIN, (dwOldFlags & dFdd_PITCHSUSTAIN) != 0);
  622. pIns->PitchEnv.dwFlags.set(ENV_LOOP, (dwOldFlags & dFdd_PITCHLOOP) != 0);
  623. pIns->PitchEnv.dwFlags.set(ENV_CARRY, (dwOldFlags & dFdd_PITCHCARRY) != 0);
  624. pIns->PitchEnv.dwFlags.set(ENV_FILTER, (dwOldFlags & dFdd_FILTER) != 0);
  625. pIns->dwFlags.reset();
  626. pIns->dwFlags.set(INS_SETPANNING, (dwOldFlags & dFdd_SETPANNING) != 0);
  627. pIns->dwFlags.set(INS_MUTE, (dwOldFlags & dFdd_MUTE) != 0);
  628. }
  629. void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const uint16 size, FileReader &file)
  630. {
  631. if(code == MagicBE("K[.."))
  632. {
  633. // skip keyboard mapping
  634. file.Skip(size);
  635. return;
  636. }
  637. bool success = ReadInstrumentHeaderField(pIns, code, size, file);
  638. if(!success)
  639. {
  640. file.Skip(size);
  641. return;
  642. }
  643. if(code == MagicBE("dF..")) // 'dF..' field requires additional processing.
  644. ConvertReadExtendedFlags(pIns);
  645. }
  646. void ReadExtendedInstrumentProperty(ModInstrument* pIns, const uint32 code, FileReader &file)
  647. {
  648. uint16 size = file.ReadUint16LE();
  649. if(!file.CanRead(size))
  650. {
  651. return;
  652. }
  653. ReadInstrumentExtensionField(pIns, code, size, file);
  654. }
  655. void ReadExtendedInstrumentProperties(ModInstrument* pIns, FileReader &file)
  656. {
  657. if(!file.ReadMagic("XTPM")) // 'MPTX'
  658. {
  659. return;
  660. }
  661. while(file.CanRead(7))
  662. {
  663. ReadExtendedInstrumentProperty(pIns, file.ReadUint32LE(), file);
  664. }
  665. }
  666. bool CSoundFile::LoadExtendedInstrumentProperties(FileReader &file)
  667. {
  668. if(!file.ReadMagic("XTPM")) // 'MPTX'
  669. {
  670. return false;
  671. }
  672. while(file.CanRead(6))
  673. {
  674. uint32 code = file.ReadUint32LE();
  675. if(code == MagicBE("MPTS") // Reached song extensions, break out of this loop
  676. || code == MagicLE("228\x04") // Reached MPTM extensions (in case there are no song extensions)
  677. || (code & 0x80808080) || !(code & 0x60606060)) // Non-ASCII chunk ID
  678. {
  679. file.SkipBack(4);
  680. break;
  681. }
  682. // Read size of this property for *one* instrument
  683. const uint16 size = file.ReadUint16LE();
  684. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
  685. {
  686. if(Instruments[i])
  687. {
  688. ReadInstrumentExtensionField(Instruments[i], code, size, file);
  689. }
  690. }
  691. }
  692. return true;
  693. }
  694. OPENMPT_NAMESPACE_END