1
0

EffectInfo.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  1. /*
  2. * EffectInfo.cpp
  3. * --------------
  4. * Purpose: Provide information about effect names, parameter interpretation to the tracker interface.
  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. #include "EffectInfo.h"
  11. #include "Mptrack.h" // for szHexChar
  12. #include "../soundlib/Sndfile.h"
  13. #include "../soundlib/mod_specifications.h"
  14. #include "../soundlib/Tables.h"
  15. OPENMPT_NAMESPACE_BEGIN
  16. ///////////////////////////////////////////////////////////////////////////
  17. // Effects description
  18. struct MPTEffectInfo
  19. {
  20. EffectCommand effect; // CMD_XXXX
  21. ModCommand::PARAM paramMask; // 0 = default
  22. ModCommand::PARAM paramValue; // 0 = default
  23. ModCommand::PARAM paramLimit; // Parameter Editor limit
  24. FlagSet<MODTYPE> supportedFormats; // MOD_TYPE_XXX combo
  25. const TCHAR *name; // e.g. "Tone Portamento"
  26. };
  27. static constexpr FlagSet<MODTYPE> MOD_TYPE_MODXM = MOD_TYPE_MOD | MOD_TYPE_XM;
  28. static constexpr FlagSet<MODTYPE> MOD_TYPE_S3MIT = MOD_TYPE_S3M | MOD_TYPE_IT;
  29. static constexpr FlagSet<MODTYPE> MOD_TYPE_S3MITMPT = MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT;
  30. static constexpr FlagSet<MODTYPE> MOD_TYPE_NOMOD = MOD_TYPE_S3M | MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT;
  31. static constexpr FlagSet<MODTYPE> MOD_TYPE_XMIT = MOD_TYPE_XM | MOD_TYPE_IT;
  32. static constexpr FlagSet<MODTYPE> MOD_TYPE_XMITMPT = MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT;
  33. static constexpr FlagSet<MODTYPE> MOD_TYPE_ITMPT = MOD_TYPE_IT | MOD_TYPE_MPT;
  34. static constexpr FlagSet<MODTYPE> MOD_TYPE_ALL = MODTYPE(~0);
  35. static constexpr MPTEffectInfo gFXInfo[] =
  36. {
  37. {CMD_ARPEGGIO, 0,0, 0, MOD_TYPE_ALL, _T("Arpeggio")},
  38. {CMD_PORTAMENTOUP, 0,0, 0, MOD_TYPE_ALL, _T("Portamento Up")},
  39. {CMD_PORTAMENTODOWN,0,0, 0, MOD_TYPE_ALL, _T("Portamento Down")},
  40. {CMD_TONEPORTAMENTO,0,0, 0, MOD_TYPE_ALL, _T("Tone Portamento")},
  41. {CMD_VIBRATO, 0,0, 0, MOD_TYPE_ALL, _T("Vibrato")},
  42. {CMD_TONEPORTAVOL, 0,0, 0, MOD_TYPE_ALL, _T("Volslide+Toneporta")},
  43. {CMD_VIBRATOVOL, 0,0, 0, MOD_TYPE_ALL, _T("VolSlide+Vibrato")},
  44. {CMD_TREMOLO, 0,0, 0, MOD_TYPE_ALL, _T("Tremolo")},
  45. {CMD_PANNING8, 0,0, 0, MOD_TYPE_ALL, _T("Set Panning")},
  46. {CMD_OFFSET, 0,0, 0, MOD_TYPE_ALL, _T("Set Offset")},
  47. {CMD_VOLUMESLIDE, 0,0, 0, MOD_TYPE_ALL, _T("Volume Slide")},
  48. {CMD_POSITIONJUMP, 0,0, 0, MOD_TYPE_ALL, _T("Position Jump")},
  49. {CMD_VOLUME, 0,0, 0, MOD_TYPE_MODXM, _T("Set Volume")},
  50. {CMD_PATTERNBREAK, 0,0, 0, MOD_TYPE_ALL, _T("Pattern Break")},
  51. {CMD_RETRIG, 0,0, 0, MOD_TYPE_NOMOD, _T("Retrigger Note")},
  52. {CMD_SPEED, 0,0, 0, MOD_TYPE_ALL, _T("Set Speed")},
  53. {CMD_TEMPO, 0,0, 0, MOD_TYPE_ALL, _T("Set Tempo")},
  54. {CMD_TREMOR, 0,0, 0, MOD_TYPE_NOMOD, _T("Tremor")},
  55. {CMD_CHANNELVOLUME, 0,0, 0, MOD_TYPE_S3MITMPT, _T("Set Channel Volume")},
  56. {CMD_CHANNELVOLSLIDE,0,0, 0, MOD_TYPE_S3MITMPT, _T("Channel Volume Slide")},
  57. {CMD_GLOBALVOLUME, 0,0, 0, MOD_TYPE_NOMOD, _T("Set Global Volume")},
  58. {CMD_GLOBALVOLSLIDE,0,0, 0, MOD_TYPE_NOMOD, _T("Global Volume Slide")},
  59. {CMD_KEYOFF, 0,0, 0, MOD_TYPE_XM, _T("Key Off")},
  60. {CMD_FINEVIBRATO, 0,0, 0, MOD_TYPE_S3MITMPT, _T("Fine Vibrato")},
  61. {CMD_PANBRELLO, 0,0, 0, MOD_TYPE_NOMOD, _T("Panbrello")},
  62. {CMD_PANNINGSLIDE, 0,0, 0, MOD_TYPE_NOMOD, _T("Panning Slide")},
  63. {CMD_SETENVPOSITION,0,0, 0, MOD_TYPE_XM, _T("Envelope position")},
  64. {CMD_MIDI, 0,0, 0x7F, MOD_TYPE_NOMOD, _T("MIDI Macro")},
  65. {CMD_SMOOTHMIDI, 0,0, 0x7F, MOD_TYPE_XMITMPT, _T("Smooth MIDI Macro")},
  66. // Extended MOD/XM effects
  67. {CMD_MODCMDEX, 0xF0,0x00, 0, MOD_TYPE_MOD, _T("Set Filter")},
  68. {CMD_MODCMDEX, 0xF0,0x10, 0, MOD_TYPE_MODXM, _T("Fine Porta Up")},
  69. {CMD_MODCMDEX, 0xF0,0x20, 0, MOD_TYPE_MODXM, _T("Fine Porta Down")},
  70. {CMD_MODCMDEX, 0xF0,0x30, 0, MOD_TYPE_MODXM, _T("Glissando Control")},
  71. {CMD_MODCMDEX, 0xF0,0x40, 0, MOD_TYPE_MODXM, _T("Vibrato Waveform")},
  72. {CMD_MODCMDEX, 0xF0,0x50, 0, MOD_TYPE_MODXM, _T("Set Finetune")},
  73. {CMD_MODCMDEX, 0xF0,0x60, 0, MOD_TYPE_MODXM, _T("Pattern Loop")},
  74. {CMD_MODCMDEX, 0xF0,0x70, 0, MOD_TYPE_MODXM, _T("Tremolo Waveform")},
  75. {CMD_MODCMDEX, 0xF0,0x80, 0, MOD_TYPE_MODXM, _T("Set Panning")},
  76. {CMD_MODCMDEX, 0xF0,0x90, 0, MOD_TYPE_MODXM, _T("Retrigger Note")},
  77. {CMD_MODCMDEX, 0xF0,0xA0, 0, MOD_TYPE_MODXM, _T("Fine Volslide Up")},
  78. {CMD_MODCMDEX, 0xF0,0xB0, 0, MOD_TYPE_MODXM, _T("Fine Volslide Down")},
  79. {CMD_MODCMDEX, 0xF0,0xC0, 0, MOD_TYPE_MODXM, _T("Note Cut")},
  80. {CMD_MODCMDEX, 0xF0,0xD0, 0, MOD_TYPE_MODXM, _T("Note Delay")},
  81. {CMD_MODCMDEX, 0xF0,0xE0, 0, MOD_TYPE_MODXM, _T("Pattern Delay")},
  82. {CMD_MODCMDEX, 0xF0,0xF0, 0, MOD_TYPE_XM, _T("Set Active Macro")},
  83. {CMD_MODCMDEX, 0xF0,0xF0, 0, MOD_TYPE_MOD, _T("Invert Loop")},
  84. // Extended S3M/IT effects
  85. {CMD_S3MCMDEX, 0xF0,0x10, 0, MOD_TYPE_S3MITMPT, _T("Glissando Control")},
  86. {CMD_S3MCMDEX, 0xF0,0x20, 0, MOD_TYPE_S3M, _T("Set Finetune")},
  87. {CMD_S3MCMDEX, 0xF0,0x30, 0, MOD_TYPE_S3MITMPT, _T("Vibrato Waveform")},
  88. {CMD_S3MCMDEX, 0xF0,0x40, 0, MOD_TYPE_S3MITMPT, _T("Tremolo Waveform")},
  89. {CMD_S3MCMDEX, 0xF0,0x50, 0, MOD_TYPE_S3MITMPT, _T("Panbrello Waveform")},
  90. {CMD_S3MCMDEX, 0xF0,0x60, 0, MOD_TYPE_S3MITMPT, _T("Fine Pattern Delay")},
  91. {CMD_S3MCMDEX, 0xF0,0x80, 0, MOD_TYPE_S3MITMPT, _T("Set Panning")},
  92. {CMD_S3MCMDEX, 0xF0,0xA0, 0, MOD_TYPE_ITMPT, _T("Set High Offset")},
  93. {CMD_S3MCMDEX, 0xF0,0xB0, 0, MOD_TYPE_S3MITMPT, _T("Pattern Loop")},
  94. {CMD_S3MCMDEX, 0xF0,0xC0, 0, MOD_TYPE_S3MITMPT, _T("Note Cut")},
  95. {CMD_S3MCMDEX, 0xF0,0xD0, 0, MOD_TYPE_S3MITMPT, _T("Note Delay")},
  96. {CMD_S3MCMDEX, 0xF0,0xE0, 0, MOD_TYPE_S3MITMPT, _T("Pattern Delay")},
  97. {CMD_S3MCMDEX, 0xF0,0xF0, 0, MOD_TYPE_ITMPT, _T("Set Active Macro")},
  98. // MPT XM extensions and special effects
  99. {CMD_XFINEPORTAUPDOWN,0xF0,0x10,0, MOD_TYPE_XM, _T("Extra Fine Porta Up")},
  100. {CMD_XFINEPORTAUPDOWN,0xF0,0x20,0, MOD_TYPE_XM, _T("Extra Fine Porta Down")},
  101. {CMD_XFINEPORTAUPDOWN,0xF0,0x50,0, MOD_TYPE_XM, _T("Panbrello Waveform")},
  102. {CMD_XFINEPORTAUPDOWN,0xF0,0x60,0, MOD_TYPE_XM, _T("Fine Pattern Delay")},
  103. {CMD_XFINEPORTAUPDOWN,0xF0,0x90,0, MOD_TYPE_XM, _T("Sound Control")},
  104. {CMD_XFINEPORTAUPDOWN,0xF0,0xA0,0, MOD_TYPE_XM, _T("Set High Offset")},
  105. // MPT IT extensions and special effects
  106. {CMD_S3MCMDEX, 0xF0,0x90, 0, MOD_TYPE_S3MITMPT, _T("Sound Control")},
  107. {CMD_S3MCMDEX, 0xF0,0x70, 0, MOD_TYPE_ITMPT, _T("Instr. Control")},
  108. {CMD_DELAYCUT, 0x00,0x00, 0, MOD_TYPE_MPT, _T("Note Delay and Cut")},
  109. {CMD_XPARAM, 0,0, 0, MOD_TYPE_XMITMPT, _T("Parameter Extension")},
  110. {CMD_NOTESLIDEUP, 0,0, 0, MOD_TYPE_IMF | MOD_TYPE_PTM, _T("Note Slide Up")}, // IMF / PTM effect
  111. {CMD_NOTESLIDEDOWN, 0,0, 0, MOD_TYPE_IMF | MOD_TYPE_PTM, _T("Note Slide Down")}, // IMF / PTM effect
  112. {CMD_NOTESLIDEUPRETRIG, 0,0, 0, MOD_TYPE_PTM, _T("Note Slide Up + Retrigger Note")}, // PTM effect
  113. {CMD_NOTESLIDEDOWNRETRIG,0,0, 0, MOD_TYPE_PTM, _T("Note Slide Down + Retrigger Note")}, // PTM effect
  114. {CMD_REVERSEOFFSET, 0,0, 0, MOD_TYPE_PTM, _T("Revert Sample + Offset")}, // PTM effect
  115. {CMD_DBMECHO, 0,0, 0, MOD_TYPE_DBM, _T("Echo Enable")}, // DBM effect
  116. {CMD_OFFSETPERCENTAGE, 0,0, 0, MOD_TYPE_PLM, _T("Offset (Percentage)")}, // PLM effect
  117. {CMD_FINETUNE, 0,0, 0, MOD_TYPE_MPT, _T("Finetune")},
  118. {CMD_FINETUNE_SMOOTH, 0,0, 0, MOD_TYPE_MPT, _T("Finetune (Smooth)")},
  119. {CMD_DUMMY, 0,0, 0, MOD_TYPE_NONE, _T("Empty") },
  120. {CMD_DIGIREVERSESAMPLE, 0, 0, 0, MOD_TYPE_NONE, _T("Reverse Sample")}, // DIGI effect
  121. };
  122. UINT EffectInfo::GetNumEffects() const
  123. {
  124. return static_cast<UINT>(std::size(gFXInfo));
  125. }
  126. bool EffectInfo::IsExtendedEffect(UINT ndx) const
  127. {
  128. return ((ndx < std::size(gFXInfo)) && (gFXInfo[ndx].paramMask));
  129. }
  130. bool EffectInfo::GetEffectName(CString &pszDescription, ModCommand::COMMAND command, UINT param, bool bXX) const
  131. {
  132. bool bSupported;
  133. UINT fxndx = static_cast<UINT>(std::size(gFXInfo));
  134. pszDescription.Empty();
  135. for (UINT i = 0; i < std::size(gFXInfo); i++)
  136. {
  137. if ((command == gFXInfo[i].effect) // Effect
  138. && ((param & gFXInfo[i].paramMask) == gFXInfo[i].paramValue)) // Value
  139. {
  140. fxndx = i;
  141. // if format is compatible, everything is fine. if not, let's still search
  142. // for another command. this fixes searching for the EFx command, which
  143. // does different things in MOD format.
  144. if((sndFile.GetType() & gFXInfo[i].supportedFormats))
  145. break;
  146. }
  147. }
  148. if (fxndx == std::size(gFXInfo)) return false;
  149. bSupported = ((sndFile.GetType() & gFXInfo[fxndx].supportedFormats));
  150. if (gFXInfo[fxndx].name)
  151. {
  152. if ((bXX) && (bSupported))
  153. {
  154. pszDescription.Format(_T("%c%c%c: ")
  155. , sndFile.GetModSpecifications().GetEffectLetter(command)
  156. , ((gFXInfo[fxndx].paramMask & 0xF0) == 0xF0) ? szHexChar[gFXInfo[fxndx].paramValue >> 4] : 'x'
  157. , ((gFXInfo[fxndx].paramMask & 0x0F) == 0x0F) ? szHexChar[gFXInfo[fxndx].paramValue & 0x0F] : 'x'
  158. );
  159. }
  160. pszDescription += gFXInfo[fxndx].name;
  161. }
  162. return bSupported;
  163. }
  164. LONG EffectInfo::GetIndexFromEffect(ModCommand::COMMAND command, ModCommand::PARAM param) const
  165. {
  166. UINT ndx = static_cast<UINT>(std::size(gFXInfo));
  167. for (UINT i = 0; i < std::size(gFXInfo); i++)
  168. {
  169. if ((command == gFXInfo[i].effect) // Effect
  170. && ((param & gFXInfo[i].paramMask) == gFXInfo[i].paramValue)) // Value
  171. {
  172. ndx = i;
  173. if((sndFile.GetType() & gFXInfo[i].supportedFormats))
  174. break; // found fitting format; this is correct for sure
  175. }
  176. }
  177. return ndx;
  178. }
  179. //Returns command and corrects parameter refParam if necessary
  180. EffectCommand EffectInfo::GetEffectFromIndex(UINT ndx, ModCommand::PARAM &refParam) const
  181. {
  182. if (ndx >= std::size(gFXInfo))
  183. {
  184. refParam = 0;
  185. return CMD_NONE;
  186. }
  187. // Cap parameter to match FX if necessary.
  188. if (gFXInfo[ndx].paramMask)
  189. {
  190. if (refParam < gFXInfo[ndx].paramValue)
  191. {
  192. refParam = gFXInfo[ndx].paramValue; // for example: delay with param < D0 becomes SD0
  193. } else if (refParam > gFXInfo[ndx].paramValue + 15)
  194. {
  195. refParam = gFXInfo[ndx].paramValue + 15; // for example: delay with param > DF becomes SDF
  196. }
  197. }
  198. if (gFXInfo[ndx].paramLimit)
  199. {
  200. // used for Zxx macro control in parameter editor: limit to 7F max.
  201. LimitMax(refParam, gFXInfo[ndx].paramLimit);
  202. }
  203. return gFXInfo[ndx].effect;
  204. }
  205. EffectCommand EffectInfo::GetEffectFromIndex(UINT ndx) const
  206. {
  207. if (ndx >= std::size(gFXInfo))
  208. {
  209. return CMD_NONE;
  210. }
  211. return gFXInfo[ndx].effect;
  212. }
  213. UINT EffectInfo::GetEffectMaskFromIndex(UINT ndx) const
  214. {
  215. if (ndx >= std::size(gFXInfo))
  216. {
  217. return 0;
  218. }
  219. return gFXInfo[ndx].paramValue;
  220. }
  221. bool EffectInfo::GetEffectInfo(UINT ndx, CString *s, bool bXX, ModCommand::PARAM *prangeMin, ModCommand::PARAM *prangeMax) const
  222. {
  223. if (s) s->Empty();
  224. if (prangeMin) *prangeMin = 0;
  225. if (prangeMax) *prangeMax = 0;
  226. if ((ndx >= std::size(gFXInfo)) || (!(sndFile.GetType() & gFXInfo[ndx].supportedFormats))) return FALSE;
  227. if (s) GetEffectName(*s, gFXInfo[ndx].effect, gFXInfo[ndx].paramValue, bXX);
  228. if ((prangeMin) && (prangeMax))
  229. {
  230. ModCommand::PARAM nmin = 0, nmax = 0xFF;
  231. if (gFXInfo[ndx].paramMask == 0xF0)
  232. {
  233. nmin = gFXInfo[ndx].paramValue;
  234. nmax = nmin | 0x0F;
  235. }
  236. switch(gFXInfo[ndx].effect)
  237. {
  238. case CMD_ARPEGGIO:
  239. if (sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM)) nmin = 1;
  240. break;
  241. case CMD_VOLUME:
  242. case CMD_CHANNELVOLUME:
  243. nmax = 0x40;
  244. break;
  245. case CMD_SPEED:
  246. nmin = 1;
  247. if (sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) nmax = 0x1F;
  248. else nmax = 0xFF;
  249. break;
  250. case CMD_TEMPO:
  251. if (sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) nmin = 0x20;
  252. else nmin = 0;
  253. break;
  254. case CMD_VOLUMESLIDE:
  255. case CMD_TONEPORTAVOL:
  256. case CMD_VIBRATOVOL:
  257. case CMD_GLOBALVOLSLIDE:
  258. case CMD_CHANNELVOLSLIDE:
  259. case CMD_PANNINGSLIDE:
  260. nmax = (sndFile.GetType() & MOD_TYPE_S3MITMPT) ? 59 : 30;
  261. break;
  262. case CMD_PANNING8:
  263. if (sndFile.GetType() & (MOD_TYPE_S3M)) nmax = 0x81;
  264. else nmax = 0xFF;
  265. break;
  266. case CMD_GLOBALVOLUME:
  267. nmax = (sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) ? 128 : 64;
  268. break;
  269. case CMD_MODCMDEX:
  270. // adjust waveform types for XM/MOD
  271. if(gFXInfo[ndx].paramValue == 0x40 || gFXInfo[ndx].paramValue == 0x70) nmax = gFXInfo[ndx].paramValue | 0x07;
  272. if(gFXInfo[ndx].paramValue == 0x00) nmax = 1;
  273. break;
  274. case CMD_S3MCMDEX:
  275. // adjust waveform types for IT/S3M
  276. if(gFXInfo[ndx].paramValue >= 0x30 && gFXInfo[ndx].paramValue <= 0x50) nmax = gFXInfo[ndx].paramValue | ((sndFile.m_playBehaviour[kITVibratoTremoloPanbrello] || sndFile.GetType() == MOD_TYPE_S3M) ? 0x03 : 0x07);
  277. break;
  278. case CMD_PATTERNBREAK:
  279. // no big patterns in MOD/S3M files, and FT2 disallows breaking to rows > 63
  280. if(sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M | MOD_TYPE_XM))
  281. nmax = 63;
  282. break;
  283. }
  284. *prangeMin = nmin;
  285. *prangeMax = nmax;
  286. }
  287. return TRUE;
  288. }
  289. UINT EffectInfo::MapValueToPos(UINT ndx, UINT param) const
  290. {
  291. UINT pos;
  292. if (ndx >= std::size(gFXInfo)) return 0;
  293. pos = param;
  294. if (gFXInfo[ndx].paramMask == 0xF0)
  295. {
  296. pos &= 0x0F;
  297. pos |= gFXInfo[ndx].paramValue;
  298. }
  299. switch(gFXInfo[ndx].effect)
  300. {
  301. case CMD_VOLUMESLIDE:
  302. case CMD_TONEPORTAVOL:
  303. case CMD_VIBRATOVOL:
  304. case CMD_GLOBALVOLSLIDE:
  305. case CMD_CHANNELVOLSLIDE:
  306. case CMD_PANNINGSLIDE:
  307. if (sndFile.GetType() & MOD_TYPE_S3MITMPT)
  308. {
  309. if (!param)
  310. pos = 29;
  311. else if (((param & 0x0F) == 0x0F) && (param & 0xF0))
  312. pos = 29 + (param >> 4); // Fine Up
  313. else if (((param & 0xF0) == 0xF0) && (param & 0x0F))
  314. pos = 29 - (param & 0x0F); // Fine Down
  315. else if (param & 0x0F)
  316. pos = 15 - (param & 0x0F); // Down
  317. else
  318. pos = (param >> 4) + 44; // Up
  319. } else
  320. {
  321. if (param & 0x0F)
  322. pos = 15 - (param & 0x0F);
  323. else
  324. pos = (param >> 4) + 15;
  325. }
  326. break;
  327. case CMD_PANNING8:
  328. if(sndFile.GetType() == MOD_TYPE_S3M)
  329. {
  330. pos = Clamp(param, 0u, 0x80u);
  331. if(param == 0xA4)
  332. pos = 0x81;
  333. }
  334. break;
  335. }
  336. return pos;
  337. }
  338. UINT EffectInfo::MapPosToValue(UINT ndx, UINT pos) const
  339. {
  340. UINT param;
  341. if (ndx >= std::size(gFXInfo)) return 0;
  342. param = pos;
  343. if (gFXInfo[ndx].paramMask == 0xF0) param |= gFXInfo[ndx].paramValue;
  344. switch(gFXInfo[ndx].effect)
  345. {
  346. case CMD_VOLUMESLIDE:
  347. case CMD_TONEPORTAVOL:
  348. case CMD_VIBRATOVOL:
  349. case CMD_GLOBALVOLSLIDE:
  350. case CMD_CHANNELVOLSLIDE:
  351. case CMD_PANNINGSLIDE:
  352. if (sndFile.GetType() & MOD_TYPE_S3MITMPT)
  353. {
  354. if (pos < 15)
  355. param = 15 - pos;
  356. else if (pos < 29)
  357. param = (29 - pos) | 0xF0;
  358. else if (pos == 29)
  359. param = 0;
  360. else if (pos <= 44)
  361. param = ((pos - 29) << 4) | 0x0F;
  362. else
  363. if (pos <= 59) param = (pos - 44) << 4;
  364. } else
  365. {
  366. if (pos < 15)
  367. param = 15 - pos;
  368. else
  369. param = (pos - 15) << 4;
  370. }
  371. break;
  372. case CMD_PANNING8:
  373. if(sndFile.GetType() == MOD_TYPE_S3M)
  374. param = (pos <= 0x80) ? pos : 0xA4;
  375. break;
  376. }
  377. return param;
  378. }
  379. bool EffectInfo::GetEffectNameEx(CString &pszName, const ModCommand &m, uint32 param, CHANNELINDEX chn) const
  380. {
  381. CString s;
  382. const TCHAR *continueOrIgnore;
  383. auto ndx = GetIndexFromEffect(m.command, static_cast<ModCommand::PARAM>(param));
  384. if(ndx < 0 || static_cast<std::size_t>(ndx) >= std::size(gFXInfo) || !gFXInfo[ndx].name)
  385. return false;
  386. pszName = CString{gFXInfo[ndx].name} + _T(": ");
  387. // for effects that don't have effect memory in MOD format.
  388. if(sndFile.GetType() == MOD_TYPE_MOD)
  389. continueOrIgnore = _T("ignore");
  390. else
  391. continueOrIgnore = _T("continue");
  392. const TCHAR *plusChar = _T("+"), *minusChar = _T("-");
  393. switch(gFXInfo[ndx].effect)
  394. {
  395. case CMD_ARPEGGIO:
  396. if(sndFile.GetType() == MOD_TYPE_XM) // XM also ignores this!
  397. continueOrIgnore = _T("ignore");
  398. if(param)
  399. s.Format(_T("note+%d note+%d"), param >> 4, param & 0x0F);
  400. else
  401. s = continueOrIgnore;
  402. break;
  403. case CMD_PORTAMENTOUP:
  404. case CMD_PORTAMENTODOWN:
  405. if(param)
  406. {
  407. TCHAR sign = (gFXInfo[ndx].effect == CMD_PORTAMENTOUP) ? _T('+') : _T('-');
  408. if((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0xF0) == 0xF0))
  409. s.Format(_T("fine %c%d"), sign, (param & 0x0F));
  410. else if((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0xF0) == 0xE0))
  411. s.Format(_T("extra fine %c%d"), sign, (param & 0x0F));
  412. else
  413. s.Format(_T("%c%d"), sign, param);
  414. } else
  415. {
  416. s = continueOrIgnore;
  417. }
  418. break;
  419. case CMD_TONEPORTAMENTO:
  420. if (param)
  421. s.Format(_T("speed %d"), param);
  422. else
  423. s = _T("continue");
  424. break;
  425. case CMD_VIBRATO:
  426. case CMD_TREMOLO:
  427. case CMD_PANBRELLO:
  428. case CMD_FINEVIBRATO:
  429. if (param)
  430. s.Format(_T("speed=%d depth=%d"), param >> 4, param & 0x0F);
  431. else
  432. s = _T("continue");
  433. break;
  434. case CMD_SPEED:
  435. s.Format(_T("%d ticks/row"), param);
  436. break;
  437. case CMD_TEMPO:
  438. if (param == 0)
  439. s = _T("continue");
  440. else if (param < 0x10)
  441. s.Format(_T("-%d bpm (slower)"), param & 0x0F);
  442. else if (param < 0x20)
  443. s.Format(_T("+%d bpm (faster)"), param & 0x0F);
  444. else
  445. s.Format(_T("%d bpm"), param);
  446. break;
  447. case CMD_PANNING8:
  448. if(sndFile.GetType() == MOD_TYPE_S3M && param == 0xA4)
  449. s = _T("Surround");
  450. else
  451. s.Format(_T("%d"), param);
  452. break;
  453. case CMD_RETRIG:
  454. switch(param >> 4)
  455. {
  456. case 0:
  457. if(sndFile.GetType() & MOD_TYPE_XM)
  458. s = _T("continue");
  459. else
  460. s = _T("vol *1");
  461. break;
  462. case 1: s = _T("vol -1"); break;
  463. case 2: s = _T("vol -2"); break;
  464. case 3: s = _T("vol -4"); break;
  465. case 4: s = _T("vol -8"); break;
  466. case 5: s = _T("vol -16"); break;
  467. case 6: s = _T("vol *0.66"); break;
  468. case 7: s = _T("vol *0.5"); break;
  469. case 8: s = _T("vol *1"); break;
  470. case 9: s = _T("vol +1"); break;
  471. case 10: s = _T("vol +2"); break;
  472. case 11: s = _T("vol +4"); break;
  473. case 12: s = _T("vol +8"); break;
  474. case 13: s = _T("vol +16"); break;
  475. case 14: s = _T("vol *1.5"); break;
  476. case 15: s = _T("vol *2"); break;
  477. }
  478. s.AppendFormat(_T(" speed %d"), param & 0x0F);
  479. break;
  480. case CMD_VOLUMESLIDE:
  481. if(sndFile.GetType() == MOD_TYPE_MOD && !param)
  482. {
  483. s = continueOrIgnore;
  484. break;
  485. }
  486. [[fallthrough]];
  487. case CMD_TONEPORTAVOL:
  488. case CMD_VIBRATOVOL:
  489. case CMD_GLOBALVOLSLIDE:
  490. case CMD_CHANNELVOLSLIDE:
  491. case CMD_PANNINGSLIDE:
  492. if(gFXInfo[ndx].effect == CMD_PANNINGSLIDE)
  493. {
  494. if(sndFile.GetType() == MOD_TYPE_XM)
  495. {
  496. plusChar = _T("-> ");
  497. minusChar = _T("<- ");
  498. } else
  499. {
  500. plusChar = _T("<- ");
  501. minusChar = _T("-> ");
  502. }
  503. }
  504. if (!param)
  505. {
  506. s.Format(_T("continue"));
  507. } else if ((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0x0F) == 0x0F) && (param & 0xF0))
  508. {
  509. s.Format(_T("fine %s%d"), plusChar, param >> 4);
  510. } else if ((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0xF0) == 0xF0) && (param & 0x0F))
  511. {
  512. s.Format(_T("fine %s%d"), minusChar, param & 0x0F);
  513. } else if ((param & 0x0F) != param && (param & 0xF0) != param) // both nibbles are set.
  514. {
  515. s = _T("undefined");
  516. } else if (param & 0x0F)
  517. {
  518. s.Format(_T("%s%d"), minusChar, param & 0x0F);
  519. } else
  520. {
  521. s.Format(_T("%s%d"), plusChar, param >> 4);
  522. }
  523. break;
  524. case CMD_PATTERNBREAK:
  525. pszName.Format(_T("Break to row %u"), param);
  526. break;
  527. case CMD_POSITIONJUMP:
  528. pszName.Format(_T("Jump to position %u"), param);
  529. break;
  530. case CMD_OFFSET:
  531. if (param)
  532. pszName.Format(_T("Set Offset to %s"), mpt::cfmt::dec(3, ',', param).GetString());
  533. else
  534. s = _T("continue");
  535. break;
  536. case CMD_CHANNELVOLUME:
  537. case CMD_GLOBALVOLUME:
  538. {
  539. ModCommand::PARAM minVal = 0, maxVal = 128;
  540. GetEffectInfo(ndx, nullptr, false, &minVal, &maxVal);
  541. if((sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M)) && param > maxVal)
  542. s = _T("undefined");
  543. else
  544. s.Format(_T("%u"), std::min(static_cast<uint32>(param), static_cast<uint32>(maxVal)));
  545. }
  546. break;
  547. case CMD_TREMOR:
  548. if(param)
  549. {
  550. uint8 ontime = (uint8)(param >> 4), offtime = (uint8)(param & 0x0F);
  551. if(sndFile.m_SongFlags[SONG_ITOLDEFFECTS] || (sndFile.GetType() & MOD_TYPE_XM))
  552. {
  553. ontime++;
  554. offtime++;
  555. } else
  556. {
  557. if(ontime == 0) ontime = 1;
  558. if(offtime == 0) offtime = 1;
  559. }
  560. s.Format(_T("ontime %u, offtime %u"), ontime, offtime);
  561. } else
  562. {
  563. s = _T("continue");
  564. }
  565. break;
  566. case CMD_SETENVPOSITION:
  567. s.Format(_T("Tick %u"), param);
  568. break;
  569. case CMD_MIDI:
  570. case CMD_SMOOTHMIDI:
  571. if (param < 0x80)
  572. {
  573. if(chn != CHANNELINDEX_INVALID)
  574. {
  575. const uint8 macroIndex = sndFile.m_PlayState.Chn[chn].nActiveMacro;
  576. const PLUGINDEX plugin = sndFile.GetBestPlugin(sndFile.m_PlayState, chn, PrioritiseChannel, EvenIfMuted) - 1;
  577. IMixPlugin *pPlugin = (plugin < MAX_MIXPLUGINS ? sndFile.m_MixPlugins[plugin].pMixPlugin : nullptr);
  578. pszName.Format(_T("SFx MIDI Macro z=%d (SF%X: %s)"), param, macroIndex, sndFile.m_MidiCfg.GetParameteredMacroName(macroIndex, pPlugin).GetString());
  579. } else
  580. {
  581. pszName.Format(_T("SFx MIDI Macro z=%02X (%d)"), param, param);
  582. }
  583. } else
  584. {
  585. pszName.Format(_T("Fixed Macro Z%02X"), param);
  586. }
  587. break;
  588. case CMD_DELAYCUT:
  589. pszName.Format(_T("Note delay: %d, cut after %d ticks"), (param >> 4), (param & 0x0F));
  590. break;
  591. case CMD_FINETUNE:
  592. case CMD_FINETUNE_SMOOTH:
  593. {
  594. int8 pwd = 1;
  595. const TCHAR *unit = _T(" cents");
  596. if(m.instr > 0 && m.instr <= sndFile.GetNumInstruments() && sndFile.Instruments[m.instr] != nullptr)
  597. pwd = sndFile.Instruments[m.instr]->midiPWD;
  598. else if(chn != CHANNELINDEX_INVALID && sndFile.m_PlayState.Chn[chn].pModInstrument != nullptr)
  599. pwd = sndFile.m_PlayState.Chn[chn].pModInstrument->midiPWD;
  600. else if(sndFile.GetNumInstruments())
  601. unit = _T("");
  602. pszName = MPT_CFORMAT("Finetune{}: {}{}{}")(
  603. CString(gFXInfo[ndx].effect == CMD_FINETUNE ? _T("") : _T(" (Smooth)")),
  604. CString(param >= 0x8000 ? _T("+") : _T("")),
  605. mpt::cfmt::val((static_cast<int32>(param) - 0x8000) * pwd / 327.68),
  606. CString(unit));
  607. }
  608. break;
  609. default:
  610. if (gFXInfo[ndx].paramMask == 0xF0)
  611. {
  612. // Sound control names
  613. if (((gFXInfo[ndx].effect == CMD_XFINEPORTAUPDOWN) || (gFXInfo[ndx].effect == CMD_S3MCMDEX))
  614. && ((gFXInfo[ndx].paramValue & 0xF0) == 0x90) && ((param & 0xF0) == 0x90))
  615. {
  616. switch(param & 0x0F)
  617. {
  618. case 0x00: s = _T("90: Surround Off"); break;
  619. case 0x01: s = _T("91: Surround On"); break;
  620. case 0x08: s = _T("98: Reverb Off"); break;
  621. case 0x09: s = _T("99: Reverb On"); break;
  622. case 0x0A: s = _T("9A: Center surround"); break;
  623. case 0x0B: s = _T("9B: Quad surround"); break;
  624. case 0x0C: s = _T("9C: Global filters"); break;
  625. case 0x0D: s = _T("9D: Local filters"); break;
  626. case 0x0E: s = _T("9E: Play Forward"); break;
  627. case 0x0F: s = _T("9F: Play Backward"); break;
  628. default: s.Format(_T("%02X: undefined"), param);
  629. }
  630. } else
  631. if (((gFXInfo[ndx].effect == CMD_XFINEPORTAUPDOWN) || (gFXInfo[ndx].effect == CMD_S3MCMDEX))
  632. && ((gFXInfo[ndx].paramValue & 0xF0) == 0x70) && ((param & 0xF0) == 0x70))
  633. {
  634. switch(param & 0x0F)
  635. {
  636. case 0x00: s = _T("70: Past note cut"); break;
  637. case 0x01: s = _T("71: Past note off"); break;
  638. case 0x02: s = _T("72: Past note fade"); break;
  639. case 0x03: s = _T("73: NNA note cut"); break;
  640. case 0x04: s = _T("74: NNA continue"); break;
  641. case 0x05: s = _T("75: NNA note off"); break;
  642. case 0x06: s = _T("76: NNA note fade"); break;
  643. case 0x07: s = _T("77: Volume Env Off"); break;
  644. case 0x08: s = _T("78: Volume Env On"); break;
  645. case 0x09: s = _T("79: Pan Env Off"); break;
  646. case 0x0A: s = _T("7A: Pan Env On"); break;
  647. case 0x0B: s = _T("7B: Pitch Env Off"); break;
  648. case 0x0C: s = _T("7C: Pitch Env On"); break;
  649. case 0x0D: if(sndFile.GetType() == MOD_TYPE_MPT) { s = _T("7D: Force Pitch Env"); break; }
  650. [[fallthrough]];
  651. case 0x0E: if(sndFile.GetType() == MOD_TYPE_MPT) { s = _T("7E: Force Filter Env"); break; }
  652. [[fallthrough]];
  653. default: s.Format(_T("%02X: undefined"), param); break;
  654. }
  655. } else
  656. {
  657. s.Format(_T("%d"), param & 0x0F);
  658. if(gFXInfo[ndx].effect == CMD_S3MCMDEX)
  659. {
  660. switch(param & 0xF0)
  661. {
  662. case 0x10: // glissando control
  663. if((param & 0x0F) == 0)
  664. s = _T("smooth");
  665. else
  666. s = _T("semitones");
  667. break;
  668. case 0x20: // set finetune
  669. s.Format(_T("%dHz"), S3MFineTuneTable[param & 0x0F]);
  670. break;
  671. case 0x30: // vibrato waveform
  672. case 0x40: // tremolo waveform
  673. case 0x50: // panbrello waveform
  674. if(((param & 0x0F) > 0x03) && sndFile.m_playBehaviour[kITVibratoTremoloPanbrello])
  675. {
  676. s = _T("ignore");
  677. break;
  678. }
  679. switch(param & 0x0F)
  680. {
  681. case 0x00: s = _T("sine wave"); break;
  682. case 0x01: s = _T("ramp down"); break;
  683. case 0x02: s = _T("square wave"); break;
  684. case 0x03: s = _T("random"); break;
  685. case 0x04: s = _T("sine wave (cont.)"); break;
  686. case 0x05: s = _T("ramp down (cont.)"); break;
  687. case 0x06: s = _T("square wave (cont.)"); break;
  688. case 0x07: s = _T("random (cont.)"); break;
  689. default: s = _T("ignore"); break;
  690. }
  691. break;
  692. case 0x60: // fine pattern delay (ticks)
  693. s += _T(" ticks");
  694. break;
  695. case 0xA0: // high offset
  696. s.Format(_T("+ %u samples"), (param & 0x0F) * 0x10000);
  697. break;
  698. case 0xB0: // pattern loop
  699. if((param & 0x0F) == 0x00)
  700. s = _T("loop start");
  701. else
  702. s += _T(" times");
  703. break;
  704. case 0xC0: // note cut
  705. case 0xD0: // note delay
  706. //IT compatibility 22. SD0 == SD1, SC0 == SC1
  707. if(((param & 0x0F) == 1) || ((param & 0x0F) == 0 && (sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))))
  708. s = _T("1 tick");
  709. else
  710. s += _T(" ticks");
  711. break;
  712. case 0xE0: // pattern delay (rows)
  713. s += _T(" rows");
  714. break;
  715. case 0xF0: // macro
  716. s = sndFile.m_MidiCfg.GetParameteredMacroName(param & 0x0F);
  717. break;
  718. default:
  719. break;
  720. }
  721. }
  722. if(gFXInfo[ndx].effect == CMD_MODCMDEX)
  723. {
  724. switch(param & 0xF0)
  725. {
  726. case 0x00:
  727. // Filter
  728. if(param & 1)
  729. s = _T("LED Filter Off");
  730. else
  731. s = _T("LED Filter On");
  732. break;
  733. case 0x30: // glissando control
  734. if((param & 0x0F) == 0)
  735. s = _T("smooth");
  736. else
  737. s = _T("semitones");
  738. break;
  739. case 0x40: // vibrato waveform
  740. case 0x70: // tremolo waveform
  741. switch(param & 0x0F)
  742. {
  743. case 0x00: case 0x08: s = _T("sine wave"); break;
  744. case 0x01: case 0x09: s = _T("ramp down"); break;
  745. case 0x02: case 0x0A: s = _T("square wave"); break;
  746. case 0x03: case 0x0B: s = _T("square wave"); break;
  747. case 0x04: case 0x0C: s = _T("sine wave (cont.)"); break;
  748. case 0x05: case 0x0D: s = _T("ramp down (cont.)"); break;
  749. case 0x06: case 0x0E: s = _T("square wave (cont.)"); break;
  750. case 0x07: case 0x0F: s = _T("square wave (cont.)"); break;
  751. }
  752. break;
  753. case 0x50: // set finetune
  754. {
  755. int8 nFinetune = (param & 0x0F);
  756. if(sndFile.GetType() & MOD_TYPE_XM)
  757. {
  758. // XM finetune
  759. nFinetune = (nFinetune - 8) * 16;
  760. } else
  761. {
  762. // MOD finetune
  763. if(nFinetune > 7) nFinetune -= 16;
  764. }
  765. s.Format(_T("%d"), nFinetune);
  766. }
  767. break;
  768. case 0x60: // pattern loop
  769. if((param & 0x0F) == 0x00)
  770. s = _T("loop start");
  771. else
  772. s += _T(" times");
  773. break;
  774. case 0x90: // retrigger
  775. s.Format(_T("speed %d"), param & 0x0F);
  776. break;
  777. case 0xC0: // note cut
  778. case 0xD0: // note delay
  779. s += _T(" ticks");
  780. break;
  781. case 0xE0: // pattern delay (rows)
  782. s += _T(" rows");
  783. break;
  784. case 0xF0:
  785. if(sndFile.GetType() == MOD_TYPE_MOD)
  786. {
  787. // invert loop
  788. if((param & 0x0F) == 0)
  789. s = _T("Stop");
  790. else
  791. s.Format(_T("Speed %d"), param & 0x0F);
  792. } else
  793. {
  794. // macro
  795. s = sndFile.m_MidiCfg.GetParameteredMacroName(param & 0x0F);
  796. }
  797. break;
  798. default:
  799. break;
  800. }
  801. }
  802. }
  803. } else
  804. {
  805. s.Format(_T("%u"), param);
  806. }
  807. }
  808. pszName += s;
  809. return true;
  810. }
  811. ////////////////////////////////////////////////////////////////////////////////////////
  812. // Volume column effects description
  813. struct MPTVolCmdInfo
  814. {
  815. VolumeCommand volCmd; // VOLCMD_XXXX
  816. FlagSet<MODTYPE> supportedFormats; // MOD_TYPE_XXX combo
  817. const TCHAR *name; // e.g. "Set Volume"
  818. };
  819. static constexpr MPTVolCmdInfo gVolCmdInfo[] =
  820. {
  821. {VOLCMD_VOLUME, MOD_TYPE_NOMOD, _T("Set Volume")},
  822. {VOLCMD_PANNING, MOD_TYPE_NOMOD, _T("Set Panning")},
  823. {VOLCMD_VOLSLIDEUP, MOD_TYPE_XMITMPT, _T("Volume slide up")},
  824. {VOLCMD_VOLSLIDEDOWN, MOD_TYPE_XMITMPT, _T("Volume slide down")},
  825. {VOLCMD_FINEVOLUP, MOD_TYPE_XMITMPT, _T("Fine volume up")},
  826. {VOLCMD_FINEVOLDOWN, MOD_TYPE_XMITMPT, _T("Fine volume down")},
  827. {VOLCMD_VIBRATOSPEED, MOD_TYPE_XM, _T("Vibrato speed")},
  828. {VOLCMD_VIBRATODEPTH, MOD_TYPE_XMITMPT, _T("Vibrato depth")},
  829. {VOLCMD_PANSLIDELEFT, MOD_TYPE_XM, _T("Pan slide left")},
  830. {VOLCMD_PANSLIDERIGHT, MOD_TYPE_XM, _T("Pan slide right")},
  831. {VOLCMD_TONEPORTAMENTO, MOD_TYPE_XMITMPT, _T("Tone portamento")},
  832. {VOLCMD_PORTAUP, MOD_TYPE_ITMPT, _T("Portamento up")},
  833. {VOLCMD_PORTADOWN, MOD_TYPE_ITMPT, _T("Portamento down")},
  834. {VOLCMD_PLAYCONTROL, MOD_TYPE_NONE, _T("Play Control")},
  835. {VOLCMD_OFFSET, MOD_TYPE_MPT, _T("Sample Cue")},
  836. };
  837. static_assert(mpt::array_size<decltype(gVolCmdInfo)>::size == (MAX_VOLCMDS - 1));
  838. UINT EffectInfo::GetNumVolCmds() const
  839. {
  840. return static_cast<UINT>(std::size(gVolCmdInfo));
  841. }
  842. LONG EffectInfo::GetIndexFromVolCmd(ModCommand::VOLCMD volcmd) const
  843. {
  844. for (UINT i = 0; i < std::size(gVolCmdInfo); i++)
  845. {
  846. if (gVolCmdInfo[i].volCmd == volcmd) return i;
  847. }
  848. return -1;
  849. }
  850. VolumeCommand EffectInfo::GetVolCmdFromIndex(UINT ndx) const
  851. {
  852. return (ndx < std::size(gVolCmdInfo)) ? gVolCmdInfo[ndx].volCmd : VOLCMD_NONE;
  853. }
  854. bool EffectInfo::GetVolCmdInfo(UINT ndx, CString *s, ModCommand::VOL *prangeMin, ModCommand::VOL *prangeMax) const
  855. {
  856. if (s) s->Empty();
  857. if (prangeMin) *prangeMin = 0;
  858. if (prangeMax) *prangeMax = 0;
  859. if (ndx >= std::size(gVolCmdInfo)) return false;
  860. if (s)
  861. {
  862. s->Format(_T("%c: %s"), sndFile.GetModSpecifications().GetVolEffectLetter(GetVolCmdFromIndex(ndx)), gVolCmdInfo[ndx].name);
  863. }
  864. if ((prangeMin) && (prangeMax))
  865. {
  866. switch(gVolCmdInfo[ndx].volCmd)
  867. {
  868. case VOLCMD_VOLUME:
  869. case VOLCMD_PANNING:
  870. *prangeMax = 64;
  871. break;
  872. default:
  873. *prangeMax = (sndFile.GetType() & MOD_TYPE_XM) ? 15 : 9;
  874. }
  875. }
  876. return (sndFile.GetType() & gVolCmdInfo[ndx].supportedFormats);
  877. }
  878. bool EffectInfo::GetVolCmdParamInfo(const ModCommand &m, CString *s) const
  879. {
  880. if(s == nullptr) return false;
  881. s->Empty();
  882. switch(m.volcmd)
  883. {
  884. case VOLCMD_VOLSLIDEUP:
  885. case VOLCMD_VOLSLIDEDOWN:
  886. case VOLCMD_FINEVOLUP:
  887. case VOLCMD_FINEVOLDOWN:
  888. if(m.vol > 0 || sndFile.GetType() == MOD_TYPE_XM)
  889. {
  890. s->Format(_T("%c%u"),
  891. (m.volcmd == VOLCMD_VOLSLIDEUP || m.volcmd == VOLCMD_FINEVOLUP) ? _T('+') : _T('-'),
  892. m.vol);
  893. } else
  894. {
  895. *s = _T("continue");
  896. }
  897. break;
  898. case VOLCMD_PORTAUP:
  899. case VOLCMD_PORTADOWN:
  900. case VOLCMD_TONEPORTAMENTO:
  901. if(m.vol > 0)
  902. {
  903. ModCommand::PARAM param = m.vol << 2;
  904. ModCommand::COMMAND cmd = CMD_PORTAMENTOUP;
  905. if(m.volcmd == VOLCMD_PORTADOWN)
  906. {
  907. cmd = CMD_PORTAMENTODOWN;
  908. } else if(m.volcmd == VOLCMD_TONEPORTAMENTO)
  909. {
  910. cmd = CMD_TONEPORTAMENTO;
  911. if(sndFile.GetType() != MOD_TYPE_XM) param = ImpulseTrackerPortaVolCmd[m.vol & 0x0F];
  912. else param = m.vol << 4;
  913. }
  914. s->Format(_T("%u (%c%02X)"),
  915. m.vol,
  916. sndFile.GetModSpecifications().GetEffectLetter(cmd),
  917. param);
  918. } else
  919. {
  920. *s = _T("continue");
  921. }
  922. break;
  923. case VOLCMD_OFFSET:
  924. if(m.vol)
  925. {
  926. SAMPLEINDEX smp = m.instr;
  927. if(smp > 0 && smp <= sndFile.GetNumInstruments() && m.IsNote() && sndFile.Instruments[smp] != nullptr)
  928. {
  929. smp = sndFile.Instruments[smp]->Keyboard[m.note - NOTE_MIN];
  930. }
  931. s->Format(_T("Cue %u: "), m.vol);
  932. if(smp > 0 && smp <= sndFile.GetNumSamples() && m.vol > 0 && m.vol <= std::size(sndFile.GetSample(smp).cues))
  933. {
  934. auto cue = sndFile.GetSample(smp).cues[m.vol - 1];
  935. if(cue < sndFile.GetSample(smp).nLength)
  936. s->Append(mpt::cfmt::dec(3, _T(','), sndFile.GetSample(smp).cues[m.vol - 1]));
  937. else
  938. s->Append(_T("unused"));
  939. } else
  940. s->Append(_T("unknown"));
  941. } else
  942. {
  943. *s = _T("continue");
  944. }
  945. break;
  946. case VOLCMD_PLAYCONTROL:
  947. if(m.vol == 0)
  948. *s = _T("Pause Playback");
  949. else if(m.vol == 1)
  950. *s = _T("Continue Playback");
  951. break;
  952. default:
  953. s->Format(_T("%u"), m.vol);
  954. break;
  955. }
  956. return true;
  957. }
  958. OPENMPT_NAMESPACE_END