diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/EffectInfo.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/EffectInfo.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/EffectInfo.cpp | 1052 |
1 files changed, 1052 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/EffectInfo.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/EffectInfo.cpp new file mode 100644 index 00000000..294e6908 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/EffectInfo.cpp @@ -0,0 +1,1052 @@ +/* + * EffectInfo.cpp + * -------------- + * Purpose: Provide information about effect names, parameter interpretation to the tracker interface. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "EffectInfo.h" +#include "Mptrack.h" // for szHexChar +#include "../soundlib/Sndfile.h" +#include "../soundlib/mod_specifications.h" +#include "../soundlib/Tables.h" + + +OPENMPT_NAMESPACE_BEGIN + + +/////////////////////////////////////////////////////////////////////////// +// Effects description + +struct MPTEffectInfo +{ + EffectCommand effect; // CMD_XXXX + ModCommand::PARAM paramMask; // 0 = default + ModCommand::PARAM paramValue; // 0 = default + ModCommand::PARAM paramLimit; // Parameter Editor limit + FlagSet<MODTYPE> supportedFormats; // MOD_TYPE_XXX combo + const TCHAR *name; // e.g. "Tone Portamento" +}; + +static constexpr FlagSet<MODTYPE> MOD_TYPE_MODXM = MOD_TYPE_MOD | MOD_TYPE_XM; +static constexpr FlagSet<MODTYPE> MOD_TYPE_S3MIT = MOD_TYPE_S3M | MOD_TYPE_IT; +static constexpr FlagSet<MODTYPE> MOD_TYPE_S3MITMPT = MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT; +static constexpr FlagSet<MODTYPE> MOD_TYPE_NOMOD = MOD_TYPE_S3M | MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT; +static constexpr FlagSet<MODTYPE> MOD_TYPE_XMIT = MOD_TYPE_XM | MOD_TYPE_IT; +static constexpr FlagSet<MODTYPE> MOD_TYPE_XMITMPT = MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT; +static constexpr FlagSet<MODTYPE> MOD_TYPE_ITMPT = MOD_TYPE_IT | MOD_TYPE_MPT; +static constexpr FlagSet<MODTYPE> MOD_TYPE_ALL = MODTYPE(~0); + +static constexpr MPTEffectInfo gFXInfo[] = +{ + {CMD_ARPEGGIO, 0,0, 0, MOD_TYPE_ALL, _T("Arpeggio")}, + {CMD_PORTAMENTOUP, 0,0, 0, MOD_TYPE_ALL, _T("Portamento Up")}, + {CMD_PORTAMENTODOWN,0,0, 0, MOD_TYPE_ALL, _T("Portamento Down")}, + {CMD_TONEPORTAMENTO,0,0, 0, MOD_TYPE_ALL, _T("Tone Portamento")}, + {CMD_VIBRATO, 0,0, 0, MOD_TYPE_ALL, _T("Vibrato")}, + {CMD_TONEPORTAVOL, 0,0, 0, MOD_TYPE_ALL, _T("Volslide+Toneporta")}, + {CMD_VIBRATOVOL, 0,0, 0, MOD_TYPE_ALL, _T("VolSlide+Vibrato")}, + {CMD_TREMOLO, 0,0, 0, MOD_TYPE_ALL, _T("Tremolo")}, + {CMD_PANNING8, 0,0, 0, MOD_TYPE_ALL, _T("Set Panning")}, + {CMD_OFFSET, 0,0, 0, MOD_TYPE_ALL, _T("Set Offset")}, + {CMD_VOLUMESLIDE, 0,0, 0, MOD_TYPE_ALL, _T("Volume Slide")}, + {CMD_POSITIONJUMP, 0,0, 0, MOD_TYPE_ALL, _T("Position Jump")}, + {CMD_VOLUME, 0,0, 0, MOD_TYPE_MODXM, _T("Set Volume")}, + {CMD_PATTERNBREAK, 0,0, 0, MOD_TYPE_ALL, _T("Pattern Break")}, + {CMD_RETRIG, 0,0, 0, MOD_TYPE_NOMOD, _T("Retrigger Note")}, + {CMD_SPEED, 0,0, 0, MOD_TYPE_ALL, _T("Set Speed")}, + {CMD_TEMPO, 0,0, 0, MOD_TYPE_ALL, _T("Set Tempo")}, + {CMD_TREMOR, 0,0, 0, MOD_TYPE_NOMOD, _T("Tremor")}, + {CMD_CHANNELVOLUME, 0,0, 0, MOD_TYPE_S3MITMPT, _T("Set Channel Volume")}, + {CMD_CHANNELVOLSLIDE,0,0, 0, MOD_TYPE_S3MITMPT, _T("Channel Volume Slide")}, + {CMD_GLOBALVOLUME, 0,0, 0, MOD_TYPE_NOMOD, _T("Set Global Volume")}, + {CMD_GLOBALVOLSLIDE,0,0, 0, MOD_TYPE_NOMOD, _T("Global Volume Slide")}, + {CMD_KEYOFF, 0,0, 0, MOD_TYPE_XM, _T("Key Off")}, + {CMD_FINEVIBRATO, 0,0, 0, MOD_TYPE_S3MITMPT, _T("Fine Vibrato")}, + {CMD_PANBRELLO, 0,0, 0, MOD_TYPE_NOMOD, _T("Panbrello")}, + {CMD_PANNINGSLIDE, 0,0, 0, MOD_TYPE_NOMOD, _T("Panning Slide")}, + {CMD_SETENVPOSITION,0,0, 0, MOD_TYPE_XM, _T("Envelope position")}, + {CMD_MIDI, 0,0, 0x7F, MOD_TYPE_NOMOD, _T("MIDI Macro")}, + {CMD_SMOOTHMIDI, 0,0, 0x7F, MOD_TYPE_XMITMPT, _T("Smooth MIDI Macro")}, + // Extended MOD/XM effects + {CMD_MODCMDEX, 0xF0,0x00, 0, MOD_TYPE_MOD, _T("Set Filter")}, + {CMD_MODCMDEX, 0xF0,0x10, 0, MOD_TYPE_MODXM, _T("Fine Porta Up")}, + {CMD_MODCMDEX, 0xF0,0x20, 0, MOD_TYPE_MODXM, _T("Fine Porta Down")}, + {CMD_MODCMDEX, 0xF0,0x30, 0, MOD_TYPE_MODXM, _T("Glissando Control")}, + {CMD_MODCMDEX, 0xF0,0x40, 0, MOD_TYPE_MODXM, _T("Vibrato Waveform")}, + {CMD_MODCMDEX, 0xF0,0x50, 0, MOD_TYPE_MODXM, _T("Set Finetune")}, + {CMD_MODCMDEX, 0xF0,0x60, 0, MOD_TYPE_MODXM, _T("Pattern Loop")}, + {CMD_MODCMDEX, 0xF0,0x70, 0, MOD_TYPE_MODXM, _T("Tremolo Waveform")}, + {CMD_MODCMDEX, 0xF0,0x80, 0, MOD_TYPE_MODXM, _T("Set Panning")}, + {CMD_MODCMDEX, 0xF0,0x90, 0, MOD_TYPE_MODXM, _T("Retrigger Note")}, + {CMD_MODCMDEX, 0xF0,0xA0, 0, MOD_TYPE_MODXM, _T("Fine Volslide Up")}, + {CMD_MODCMDEX, 0xF0,0xB0, 0, MOD_TYPE_MODXM, _T("Fine Volslide Down")}, + {CMD_MODCMDEX, 0xF0,0xC0, 0, MOD_TYPE_MODXM, _T("Note Cut")}, + {CMD_MODCMDEX, 0xF0,0xD0, 0, MOD_TYPE_MODXM, _T("Note Delay")}, + {CMD_MODCMDEX, 0xF0,0xE0, 0, MOD_TYPE_MODXM, _T("Pattern Delay")}, + {CMD_MODCMDEX, 0xF0,0xF0, 0, MOD_TYPE_XM, _T("Set Active Macro")}, + {CMD_MODCMDEX, 0xF0,0xF0, 0, MOD_TYPE_MOD, _T("Invert Loop")}, + // Extended S3M/IT effects + {CMD_S3MCMDEX, 0xF0,0x10, 0, MOD_TYPE_S3MITMPT, _T("Glissando Control")}, + {CMD_S3MCMDEX, 0xF0,0x20, 0, MOD_TYPE_S3M, _T("Set Finetune")}, + {CMD_S3MCMDEX, 0xF0,0x30, 0, MOD_TYPE_S3MITMPT, _T("Vibrato Waveform")}, + {CMD_S3MCMDEX, 0xF0,0x40, 0, MOD_TYPE_S3MITMPT, _T("Tremolo Waveform")}, + {CMD_S3MCMDEX, 0xF0,0x50, 0, MOD_TYPE_S3MITMPT, _T("Panbrello Waveform")}, + {CMD_S3MCMDEX, 0xF0,0x60, 0, MOD_TYPE_S3MITMPT, _T("Fine Pattern Delay")}, + {CMD_S3MCMDEX, 0xF0,0x80, 0, MOD_TYPE_S3MITMPT, _T("Set Panning")}, + {CMD_S3MCMDEX, 0xF0,0xA0, 0, MOD_TYPE_ITMPT, _T("Set High Offset")}, + {CMD_S3MCMDEX, 0xF0,0xB0, 0, MOD_TYPE_S3MITMPT, _T("Pattern Loop")}, + {CMD_S3MCMDEX, 0xF0,0xC0, 0, MOD_TYPE_S3MITMPT, _T("Note Cut")}, + {CMD_S3MCMDEX, 0xF0,0xD0, 0, MOD_TYPE_S3MITMPT, _T("Note Delay")}, + {CMD_S3MCMDEX, 0xF0,0xE0, 0, MOD_TYPE_S3MITMPT, _T("Pattern Delay")}, + {CMD_S3MCMDEX, 0xF0,0xF0, 0, MOD_TYPE_ITMPT, _T("Set Active Macro")}, + // MPT XM extensions and special effects + {CMD_XFINEPORTAUPDOWN,0xF0,0x10,0, MOD_TYPE_XM, _T("Extra Fine Porta Up")}, + {CMD_XFINEPORTAUPDOWN,0xF0,0x20,0, MOD_TYPE_XM, _T("Extra Fine Porta Down")}, + {CMD_XFINEPORTAUPDOWN,0xF0,0x50,0, MOD_TYPE_XM, _T("Panbrello Waveform")}, + {CMD_XFINEPORTAUPDOWN,0xF0,0x60,0, MOD_TYPE_XM, _T("Fine Pattern Delay")}, + {CMD_XFINEPORTAUPDOWN,0xF0,0x90,0, MOD_TYPE_XM, _T("Sound Control")}, + {CMD_XFINEPORTAUPDOWN,0xF0,0xA0,0, MOD_TYPE_XM, _T("Set High Offset")}, + // MPT IT extensions and special effects + {CMD_S3MCMDEX, 0xF0,0x90, 0, MOD_TYPE_S3MITMPT, _T("Sound Control")}, + {CMD_S3MCMDEX, 0xF0,0x70, 0, MOD_TYPE_ITMPT, _T("Instr. Control")}, + {CMD_DELAYCUT, 0x00,0x00, 0, MOD_TYPE_MPT, _T("Note Delay and Cut")}, + {CMD_XPARAM, 0,0, 0, MOD_TYPE_XMITMPT, _T("Parameter Extension")}, + {CMD_NOTESLIDEUP, 0,0, 0, MOD_TYPE_IMF | MOD_TYPE_PTM, _T("Note Slide Up")}, // IMF / PTM effect + {CMD_NOTESLIDEDOWN, 0,0, 0, MOD_TYPE_IMF | MOD_TYPE_PTM, _T("Note Slide Down")}, // IMF / PTM effect + {CMD_NOTESLIDEUPRETRIG, 0,0, 0, MOD_TYPE_PTM, _T("Note Slide Up + Retrigger Note")}, // PTM effect + {CMD_NOTESLIDEDOWNRETRIG,0,0, 0, MOD_TYPE_PTM, _T("Note Slide Down + Retrigger Note")}, // PTM effect + {CMD_REVERSEOFFSET, 0,0, 0, MOD_TYPE_PTM, _T("Revert Sample + Offset")}, // PTM effect + {CMD_DBMECHO, 0,0, 0, MOD_TYPE_DBM, _T("Echo Enable")}, // DBM effect + {CMD_OFFSETPERCENTAGE, 0,0, 0, MOD_TYPE_PLM, _T("Offset (Percentage)")}, // PLM effect + {CMD_FINETUNE, 0,0, 0, MOD_TYPE_MPT, _T("Finetune")}, + {CMD_FINETUNE_SMOOTH, 0,0, 0, MOD_TYPE_MPT, _T("Finetune (Smooth)")}, + {CMD_DUMMY, 0,0, 0, MOD_TYPE_NONE, _T("Empty") }, + {CMD_DIGIREVERSESAMPLE, 0, 0, 0, MOD_TYPE_NONE, _T("Reverse Sample")}, // DIGI effect +}; + + +UINT EffectInfo::GetNumEffects() const +{ + return static_cast<UINT>(std::size(gFXInfo)); +} + + +bool EffectInfo::IsExtendedEffect(UINT ndx) const +{ + return ((ndx < std::size(gFXInfo)) && (gFXInfo[ndx].paramMask)); +} + + +bool EffectInfo::GetEffectName(CString &pszDescription, ModCommand::COMMAND command, UINT param, bool bXX) const +{ + bool bSupported; + UINT fxndx = static_cast<UINT>(std::size(gFXInfo)); + pszDescription.Empty(); + for (UINT i = 0; i < std::size(gFXInfo); i++) + { + if ((command == gFXInfo[i].effect) // Effect + && ((param & gFXInfo[i].paramMask) == gFXInfo[i].paramValue)) // Value + { + fxndx = i; + // if format is compatible, everything is fine. if not, let's still search + // for another command. this fixes searching for the EFx command, which + // does different things in MOD format. + if((sndFile.GetType() & gFXInfo[i].supportedFormats)) + break; + } + } + if (fxndx == std::size(gFXInfo)) return false; + bSupported = ((sndFile.GetType() & gFXInfo[fxndx].supportedFormats)); + if (gFXInfo[fxndx].name) + { + if ((bXX) && (bSupported)) + { + pszDescription.Format(_T("%c%c%c: ") + , sndFile.GetModSpecifications().GetEffectLetter(command) + , ((gFXInfo[fxndx].paramMask & 0xF0) == 0xF0) ? szHexChar[gFXInfo[fxndx].paramValue >> 4] : 'x' + , ((gFXInfo[fxndx].paramMask & 0x0F) == 0x0F) ? szHexChar[gFXInfo[fxndx].paramValue & 0x0F] : 'x' + ); + } + pszDescription += gFXInfo[fxndx].name; + } + return bSupported; +} + + +LONG EffectInfo::GetIndexFromEffect(ModCommand::COMMAND command, ModCommand::PARAM param) const +{ + UINT ndx = static_cast<UINT>(std::size(gFXInfo)); + for (UINT i = 0; i < std::size(gFXInfo); i++) + { + if ((command == gFXInfo[i].effect) // Effect + && ((param & gFXInfo[i].paramMask) == gFXInfo[i].paramValue)) // Value + { + ndx = i; + if((sndFile.GetType() & gFXInfo[i].supportedFormats)) + break; // found fitting format; this is correct for sure + } + } + return ndx; +} + + +//Returns command and corrects parameter refParam if necessary +EffectCommand EffectInfo::GetEffectFromIndex(UINT ndx, ModCommand::PARAM &refParam) const +{ + if (ndx >= std::size(gFXInfo)) + { + refParam = 0; + return CMD_NONE; + } + + // Cap parameter to match FX if necessary. + if (gFXInfo[ndx].paramMask) + { + if (refParam < gFXInfo[ndx].paramValue) + { + refParam = gFXInfo[ndx].paramValue; // for example: delay with param < D0 becomes SD0 + } else if (refParam > gFXInfo[ndx].paramValue + 15) + { + refParam = gFXInfo[ndx].paramValue + 15; // for example: delay with param > DF becomes SDF + } + } + if (gFXInfo[ndx].paramLimit) + { + // used for Zxx macro control in parameter editor: limit to 7F max. + LimitMax(refParam, gFXInfo[ndx].paramLimit); + } + + return gFXInfo[ndx].effect; +} + + +EffectCommand EffectInfo::GetEffectFromIndex(UINT ndx) const +{ + if (ndx >= std::size(gFXInfo)) + { + return CMD_NONE; + } + + return gFXInfo[ndx].effect; +} + +UINT EffectInfo::GetEffectMaskFromIndex(UINT ndx) const +{ + if (ndx >= std::size(gFXInfo)) + { + return 0; + } + + return gFXInfo[ndx].paramValue; + +} + +bool EffectInfo::GetEffectInfo(UINT ndx, CString *s, bool bXX, ModCommand::PARAM *prangeMin, ModCommand::PARAM *prangeMax) const +{ + + if (s) s->Empty(); + if (prangeMin) *prangeMin = 0; + if (prangeMax) *prangeMax = 0; + if ((ndx >= std::size(gFXInfo)) || (!(sndFile.GetType() & gFXInfo[ndx].supportedFormats))) return FALSE; + if (s) GetEffectName(*s, gFXInfo[ndx].effect, gFXInfo[ndx].paramValue, bXX); + if ((prangeMin) && (prangeMax)) + { + ModCommand::PARAM nmin = 0, nmax = 0xFF; + if (gFXInfo[ndx].paramMask == 0xF0) + { + nmin = gFXInfo[ndx].paramValue; + nmax = nmin | 0x0F; + } + switch(gFXInfo[ndx].effect) + { + case CMD_ARPEGGIO: + if (sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM)) nmin = 1; + break; + case CMD_VOLUME: + case CMD_CHANNELVOLUME: + nmax = 0x40; + break; + case CMD_SPEED: + nmin = 1; + if (sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) nmax = 0x1F; + else nmax = 0xFF; + break; + case CMD_TEMPO: + if (sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) nmin = 0x20; + else nmin = 0; + break; + case CMD_VOLUMESLIDE: + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + case CMD_GLOBALVOLSLIDE: + case CMD_CHANNELVOLSLIDE: + case CMD_PANNINGSLIDE: + nmax = (sndFile.GetType() & MOD_TYPE_S3MITMPT) ? 59 : 30; + break; + case CMD_PANNING8: + if (sndFile.GetType() & (MOD_TYPE_S3M)) nmax = 0x81; + else nmax = 0xFF; + break; + case CMD_GLOBALVOLUME: + nmax = (sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) ? 128 : 64; + break; + + case CMD_MODCMDEX: + // adjust waveform types for XM/MOD + if(gFXInfo[ndx].paramValue == 0x40 || gFXInfo[ndx].paramValue == 0x70) nmax = gFXInfo[ndx].paramValue | 0x07; + if(gFXInfo[ndx].paramValue == 0x00) nmax = 1; + break; + case CMD_S3MCMDEX: + // adjust waveform types for IT/S3M + if(gFXInfo[ndx].paramValue >= 0x30 && gFXInfo[ndx].paramValue <= 0x50) nmax = gFXInfo[ndx].paramValue | ((sndFile.m_playBehaviour[kITVibratoTremoloPanbrello] || sndFile.GetType() == MOD_TYPE_S3M) ? 0x03 : 0x07); + break; + case CMD_PATTERNBREAK: + // no big patterns in MOD/S3M files, and FT2 disallows breaking to rows > 63 + if(sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M | MOD_TYPE_XM)) + nmax = 63; + break; + } + *prangeMin = nmin; + *prangeMax = nmax; + } + return TRUE; +} + + +UINT EffectInfo::MapValueToPos(UINT ndx, UINT param) const +{ + UINT pos; + + if (ndx >= std::size(gFXInfo)) return 0; + pos = param; + if (gFXInfo[ndx].paramMask == 0xF0) + { + pos &= 0x0F; + pos |= gFXInfo[ndx].paramValue; + } + switch(gFXInfo[ndx].effect) + { + case CMD_VOLUMESLIDE: + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + case CMD_GLOBALVOLSLIDE: + case CMD_CHANNELVOLSLIDE: + case CMD_PANNINGSLIDE: + if (sndFile.GetType() & MOD_TYPE_S3MITMPT) + { + if (!param) + pos = 29; + else if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + pos = 29 + (param >> 4); // Fine Up + else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + pos = 29 - (param & 0x0F); // Fine Down + else if (param & 0x0F) + pos = 15 - (param & 0x0F); // Down + else + pos = (param >> 4) + 44; // Up + } else + { + if (param & 0x0F) + pos = 15 - (param & 0x0F); + else + pos = (param >> 4) + 15; + } + break; + case CMD_PANNING8: + if(sndFile.GetType() == MOD_TYPE_S3M) + { + pos = Clamp(param, 0u, 0x80u); + if(param == 0xA4) + pos = 0x81; + } + break; + } + return pos; +} + + +UINT EffectInfo::MapPosToValue(UINT ndx, UINT pos) const +{ + UINT param; + + if (ndx >= std::size(gFXInfo)) return 0; + param = pos; + if (gFXInfo[ndx].paramMask == 0xF0) param |= gFXInfo[ndx].paramValue; + switch(gFXInfo[ndx].effect) + { + case CMD_VOLUMESLIDE: + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + case CMD_GLOBALVOLSLIDE: + case CMD_CHANNELVOLSLIDE: + case CMD_PANNINGSLIDE: + if (sndFile.GetType() & MOD_TYPE_S3MITMPT) + { + if (pos < 15) + param = 15 - pos; + else if (pos < 29) + param = (29 - pos) | 0xF0; + else if (pos == 29) + param = 0; + else if (pos <= 44) + param = ((pos - 29) << 4) | 0x0F; + else + if (pos <= 59) param = (pos - 44) << 4; + } else + { + if (pos < 15) + param = 15 - pos; + else + param = (pos - 15) << 4; + } + break; + case CMD_PANNING8: + if(sndFile.GetType() == MOD_TYPE_S3M) + param = (pos <= 0x80) ? pos : 0xA4; + break; + } + return param; +} + + +bool EffectInfo::GetEffectNameEx(CString &pszName, const ModCommand &m, uint32 param, CHANNELINDEX chn) const +{ + CString s; + const TCHAR *continueOrIgnore; + + auto ndx = GetIndexFromEffect(m.command, static_cast<ModCommand::PARAM>(param)); + + if(ndx < 0 || static_cast<std::size_t>(ndx) >= std::size(gFXInfo) || !gFXInfo[ndx].name) + return false; + pszName = CString{gFXInfo[ndx].name} + _T(": "); + + // for effects that don't have effect memory in MOD format. + if(sndFile.GetType() == MOD_TYPE_MOD) + continueOrIgnore = _T("ignore"); + else + continueOrIgnore = _T("continue"); + + const TCHAR *plusChar = _T("+"), *minusChar = _T("-"); + + switch(gFXInfo[ndx].effect) + { + case CMD_ARPEGGIO: + if(sndFile.GetType() == MOD_TYPE_XM) // XM also ignores this! + continueOrIgnore = _T("ignore"); + + if(param) + s.Format(_T("note+%d note+%d"), param >> 4, param & 0x0F); + else + s = continueOrIgnore; + break; + + case CMD_PORTAMENTOUP: + case CMD_PORTAMENTODOWN: + if(param) + { + TCHAR sign = (gFXInfo[ndx].effect == CMD_PORTAMENTOUP) ? _T('+') : _T('-'); + + if((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0xF0) == 0xF0)) + s.Format(_T("fine %c%d"), sign, (param & 0x0F)); + else if((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0xF0) == 0xE0)) + s.Format(_T("extra fine %c%d"), sign, (param & 0x0F)); + else + s.Format(_T("%c%d"), sign, param); + } else + { + s = continueOrIgnore; + } + break; + + case CMD_TONEPORTAMENTO: + if (param) + s.Format(_T("speed %d"), param); + else + s = _T("continue"); + break; + + case CMD_VIBRATO: + case CMD_TREMOLO: + case CMD_PANBRELLO: + case CMD_FINEVIBRATO: + if (param) + s.Format(_T("speed=%d depth=%d"), param >> 4, param & 0x0F); + else + s = _T("continue"); + break; + + case CMD_SPEED: + s.Format(_T("%d ticks/row"), param); + break; + + case CMD_TEMPO: + if (param == 0) + s = _T("continue"); + else if (param < 0x10) + s.Format(_T("-%d bpm (slower)"), param & 0x0F); + else if (param < 0x20) + s.Format(_T("+%d bpm (faster)"), param & 0x0F); + else + s.Format(_T("%d bpm"), param); + break; + + case CMD_PANNING8: + if(sndFile.GetType() == MOD_TYPE_S3M && param == 0xA4) + s = _T("Surround"); + else + s.Format(_T("%d"), param); + break; + + case CMD_RETRIG: + switch(param >> 4) + { + case 0: + if(sndFile.GetType() & MOD_TYPE_XM) + s = _T("continue"); + else + s = _T("vol *1"); + break; + case 1: s = _T("vol -1"); break; + case 2: s = _T("vol -2"); break; + case 3: s = _T("vol -4"); break; + case 4: s = _T("vol -8"); break; + case 5: s = _T("vol -16"); break; + case 6: s = _T("vol *0.66"); break; + case 7: s = _T("vol *0.5"); break; + case 8: s = _T("vol *1"); break; + case 9: s = _T("vol +1"); break; + case 10: s = _T("vol +2"); break; + case 11: s = _T("vol +4"); break; + case 12: s = _T("vol +8"); break; + case 13: s = _T("vol +16"); break; + case 14: s = _T("vol *1.5"); break; + case 15: s = _T("vol *2"); break; + } + s.AppendFormat(_T(" speed %d"), param & 0x0F); + break; + + case CMD_VOLUMESLIDE: + if(sndFile.GetType() == MOD_TYPE_MOD && !param) + { + s = continueOrIgnore; + break; + } + [[fallthrough]]; + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + case CMD_GLOBALVOLSLIDE: + case CMD_CHANNELVOLSLIDE: + case CMD_PANNINGSLIDE: + if(gFXInfo[ndx].effect == CMD_PANNINGSLIDE) + { + if(sndFile.GetType() == MOD_TYPE_XM) + { + plusChar = _T("-> "); + minusChar = _T("<- "); + } else + { + plusChar = _T("<- "); + minusChar = _T("-> "); + } + } + + if (!param) + { + s.Format(_T("continue")); + } else if ((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + s.Format(_T("fine %s%d"), plusChar, param >> 4); + } else if ((sndFile.GetType() & MOD_TYPE_S3MITMPT) && ((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + s.Format(_T("fine %s%d"), minusChar, param & 0x0F); + } else if ((param & 0x0F) != param && (param & 0xF0) != param) // both nibbles are set. + { + s = _T("undefined"); + } else if (param & 0x0F) + { + s.Format(_T("%s%d"), minusChar, param & 0x0F); + } else + { + s.Format(_T("%s%d"), plusChar, param >> 4); + } + break; + + case CMD_PATTERNBREAK: + pszName.Format(_T("Break to row %u"), param); + break; + + case CMD_POSITIONJUMP: + pszName.Format(_T("Jump to position %u"), param); + break; + + case CMD_OFFSET: + if (param) + pszName.Format(_T("Set Offset to %s"), mpt::cfmt::dec(3, ',', param).GetString()); + else + s = _T("continue"); + break; + + case CMD_CHANNELVOLUME: + case CMD_GLOBALVOLUME: + { + ModCommand::PARAM minVal = 0, maxVal = 128; + GetEffectInfo(ndx, nullptr, false, &minVal, &maxVal); + if((sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M)) && param > maxVal) + s = _T("undefined"); + else + s.Format(_T("%u"), std::min(static_cast<uint32>(param), static_cast<uint32>(maxVal))); + } + break; + + case CMD_TREMOR: + if(param) + { + uint8 ontime = (uint8)(param >> 4), offtime = (uint8)(param & 0x0F); + if(sndFile.m_SongFlags[SONG_ITOLDEFFECTS] || (sndFile.GetType() & MOD_TYPE_XM)) + { + ontime++; + offtime++; + } else + { + if(ontime == 0) ontime = 1; + if(offtime == 0) offtime = 1; + } + s.Format(_T("ontime %u, offtime %u"), ontime, offtime); + } else + { + s = _T("continue"); + } + break; + + case CMD_SETENVPOSITION: + s.Format(_T("Tick %u"), param); + break; + + case CMD_MIDI: + case CMD_SMOOTHMIDI: + if (param < 0x80) + { + if(chn != CHANNELINDEX_INVALID) + { + const uint8 macroIndex = sndFile.m_PlayState.Chn[chn].nActiveMacro; + const PLUGINDEX plugin = sndFile.GetBestPlugin(sndFile.m_PlayState, chn, PrioritiseChannel, EvenIfMuted) - 1; + IMixPlugin *pPlugin = (plugin < MAX_MIXPLUGINS ? sndFile.m_MixPlugins[plugin].pMixPlugin : nullptr); + pszName.Format(_T("SFx MIDI Macro z=%d (SF%X: %s)"), param, macroIndex, sndFile.m_MidiCfg.GetParameteredMacroName(macroIndex, pPlugin).GetString()); + } else + { + pszName.Format(_T("SFx MIDI Macro z=%02X (%d)"), param, param); + } + } else + { + pszName.Format(_T("Fixed Macro Z%02X"), param); + } + break; + + case CMD_DELAYCUT: + pszName.Format(_T("Note delay: %d, cut after %d ticks"), (param >> 4), (param & 0x0F)); + break; + + case CMD_FINETUNE: + case CMD_FINETUNE_SMOOTH: + { + int8 pwd = 1; + const TCHAR *unit = _T(" cents"); + if(m.instr > 0 && m.instr <= sndFile.GetNumInstruments() && sndFile.Instruments[m.instr] != nullptr) + pwd = sndFile.Instruments[m.instr]->midiPWD; + else if(chn != CHANNELINDEX_INVALID && sndFile.m_PlayState.Chn[chn].pModInstrument != nullptr) + pwd = sndFile.m_PlayState.Chn[chn].pModInstrument->midiPWD; + else if(sndFile.GetNumInstruments()) + unit = _T(""); + + pszName = MPT_CFORMAT("Finetune{}: {}{}{}")( + CString(gFXInfo[ndx].effect == CMD_FINETUNE ? _T("") : _T(" (Smooth)")), + CString(param >= 0x8000 ? _T("+") : _T("")), + mpt::cfmt::val((static_cast<int32>(param) - 0x8000) * pwd / 327.68), + CString(unit)); + } + break; + + default: + if (gFXInfo[ndx].paramMask == 0xF0) + { + // Sound control names + if (((gFXInfo[ndx].effect == CMD_XFINEPORTAUPDOWN) || (gFXInfo[ndx].effect == CMD_S3MCMDEX)) + && ((gFXInfo[ndx].paramValue & 0xF0) == 0x90) && ((param & 0xF0) == 0x90)) + { + switch(param & 0x0F) + { + case 0x00: s = _T("90: Surround Off"); break; + case 0x01: s = _T("91: Surround On"); break; + case 0x08: s = _T("98: Reverb Off"); break; + case 0x09: s = _T("99: Reverb On"); break; + case 0x0A: s = _T("9A: Center surround"); break; + case 0x0B: s = _T("9B: Quad surround"); break; + case 0x0C: s = _T("9C: Global filters"); break; + case 0x0D: s = _T("9D: Local filters"); break; + case 0x0E: s = _T("9E: Play Forward"); break; + case 0x0F: s = _T("9F: Play Backward"); break; + default: s.Format(_T("%02X: undefined"), param); + } + } else + if (((gFXInfo[ndx].effect == CMD_XFINEPORTAUPDOWN) || (gFXInfo[ndx].effect == CMD_S3MCMDEX)) + && ((gFXInfo[ndx].paramValue & 0xF0) == 0x70) && ((param & 0xF0) == 0x70)) + { + switch(param & 0x0F) + { + case 0x00: s = _T("70: Past note cut"); break; + case 0x01: s = _T("71: Past note off"); break; + case 0x02: s = _T("72: Past note fade"); break; + case 0x03: s = _T("73: NNA note cut"); break; + case 0x04: s = _T("74: NNA continue"); break; + case 0x05: s = _T("75: NNA note off"); break; + case 0x06: s = _T("76: NNA note fade"); break; + case 0x07: s = _T("77: Volume Env Off"); break; + case 0x08: s = _T("78: Volume Env On"); break; + case 0x09: s = _T("79: Pan Env Off"); break; + case 0x0A: s = _T("7A: Pan Env On"); break; + case 0x0B: s = _T("7B: Pitch Env Off"); break; + case 0x0C: s = _T("7C: Pitch Env On"); break; + case 0x0D: if(sndFile.GetType() == MOD_TYPE_MPT) { s = _T("7D: Force Pitch Env"); break; } + [[fallthrough]]; + case 0x0E: if(sndFile.GetType() == MOD_TYPE_MPT) { s = _T("7E: Force Filter Env"); break; } + [[fallthrough]]; + default: s.Format(_T("%02X: undefined"), param); break; + } + } else + { + s.Format(_T("%d"), param & 0x0F); + if(gFXInfo[ndx].effect == CMD_S3MCMDEX) + { + switch(param & 0xF0) + { + case 0x10: // glissando control + if((param & 0x0F) == 0) + s = _T("smooth"); + else + s = _T("semitones"); + break; + case 0x20: // set finetune + s.Format(_T("%dHz"), S3MFineTuneTable[param & 0x0F]); + break; + case 0x30: // vibrato waveform + case 0x40: // tremolo waveform + case 0x50: // panbrello waveform + if(((param & 0x0F) > 0x03) && sndFile.m_playBehaviour[kITVibratoTremoloPanbrello]) + { + s = _T("ignore"); + break; + } + switch(param & 0x0F) + { + case 0x00: s = _T("sine wave"); break; + case 0x01: s = _T("ramp down"); break; + case 0x02: s = _T("square wave"); break; + case 0x03: s = _T("random"); break; + case 0x04: s = _T("sine wave (cont.)"); break; + case 0x05: s = _T("ramp down (cont.)"); break; + case 0x06: s = _T("square wave (cont.)"); break; + case 0x07: s = _T("random (cont.)"); break; + default: s = _T("ignore"); break; + } + break; + + case 0x60: // fine pattern delay (ticks) + s += _T(" ticks"); + break; + + case 0xA0: // high offset + s.Format(_T("+ %u samples"), (param & 0x0F) * 0x10000); + break; + + case 0xB0: // pattern loop + if((param & 0x0F) == 0x00) + s = _T("loop start"); + else + s += _T(" times"); + break; + case 0xC0: // note cut + case 0xD0: // note delay + //IT compatibility 22. SD0 == SD1, SC0 == SC1 + if(((param & 0x0F) == 1) || ((param & 0x0F) == 0 && (sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))) + s = _T("1 tick"); + else + s += _T(" ticks"); + break; + case 0xE0: // pattern delay (rows) + s += _T(" rows"); + break; + case 0xF0: // macro + s = sndFile.m_MidiCfg.GetParameteredMacroName(param & 0x0F); + break; + default: + break; + } + } + if(gFXInfo[ndx].effect == CMD_MODCMDEX) + { + switch(param & 0xF0) + { + case 0x00: + // Filter + if(param & 1) + s = _T("LED Filter Off"); + else + s = _T("LED Filter On"); + break; + + case 0x30: // glissando control + if((param & 0x0F) == 0) + s = _T("smooth"); + else + s = _T("semitones"); + break; + case 0x40: // vibrato waveform + case 0x70: // tremolo waveform + switch(param & 0x0F) + { + case 0x00: case 0x08: s = _T("sine wave"); break; + case 0x01: case 0x09: s = _T("ramp down"); break; + case 0x02: case 0x0A: s = _T("square wave"); break; + case 0x03: case 0x0B: s = _T("square wave"); break; + + case 0x04: case 0x0C: s = _T("sine wave (cont.)"); break; + case 0x05: case 0x0D: s = _T("ramp down (cont.)"); break; + case 0x06: case 0x0E: s = _T("square wave (cont.)"); break; + case 0x07: case 0x0F: s = _T("square wave (cont.)"); break; + } + break; + case 0x50: // set finetune + { + int8 nFinetune = (param & 0x0F); + if(sndFile.GetType() & MOD_TYPE_XM) + { + // XM finetune + nFinetune = (nFinetune - 8) * 16; + } else + { + // MOD finetune + if(nFinetune > 7) nFinetune -= 16; + } + s.Format(_T("%d"), nFinetune); + } + break; + case 0x60: // pattern loop + if((param & 0x0F) == 0x00) + s = _T("loop start"); + else + s += _T(" times"); + break; + case 0x90: // retrigger + s.Format(_T("speed %d"), param & 0x0F); + break; + case 0xC0: // note cut + case 0xD0: // note delay + s += _T(" ticks"); + break; + case 0xE0: // pattern delay (rows) + s += _T(" rows"); + break; + case 0xF0: + if(sndFile.GetType() == MOD_TYPE_MOD) + { + // invert loop + if((param & 0x0F) == 0) + s = _T("Stop"); + else + s.Format(_T("Speed %d"), param & 0x0F); + } else + { + // macro + s = sndFile.m_MidiCfg.GetParameteredMacroName(param & 0x0F); + } + break; + default: + break; + } + } + } + + } else + { + s.Format(_T("%u"), param); + } + } + pszName += s; + return true; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// Volume column effects description + +struct MPTVolCmdInfo +{ + VolumeCommand volCmd; // VOLCMD_XXXX + FlagSet<MODTYPE> supportedFormats; // MOD_TYPE_XXX combo + const TCHAR *name; // e.g. "Set Volume" +}; + +static constexpr MPTVolCmdInfo gVolCmdInfo[] = +{ + {VOLCMD_VOLUME, MOD_TYPE_NOMOD, _T("Set Volume")}, + {VOLCMD_PANNING, MOD_TYPE_NOMOD, _T("Set Panning")}, + {VOLCMD_VOLSLIDEUP, MOD_TYPE_XMITMPT, _T("Volume slide up")}, + {VOLCMD_VOLSLIDEDOWN, MOD_TYPE_XMITMPT, _T("Volume slide down")}, + {VOLCMD_FINEVOLUP, MOD_TYPE_XMITMPT, _T("Fine volume up")}, + {VOLCMD_FINEVOLDOWN, MOD_TYPE_XMITMPT, _T("Fine volume down")}, + {VOLCMD_VIBRATOSPEED, MOD_TYPE_XM, _T("Vibrato speed")}, + {VOLCMD_VIBRATODEPTH, MOD_TYPE_XMITMPT, _T("Vibrato depth")}, + {VOLCMD_PANSLIDELEFT, MOD_TYPE_XM, _T("Pan slide left")}, + {VOLCMD_PANSLIDERIGHT, MOD_TYPE_XM, _T("Pan slide right")}, + {VOLCMD_TONEPORTAMENTO, MOD_TYPE_XMITMPT, _T("Tone portamento")}, + {VOLCMD_PORTAUP, MOD_TYPE_ITMPT, _T("Portamento up")}, + {VOLCMD_PORTADOWN, MOD_TYPE_ITMPT, _T("Portamento down")}, + {VOLCMD_PLAYCONTROL, MOD_TYPE_NONE, _T("Play Control")}, + {VOLCMD_OFFSET, MOD_TYPE_MPT, _T("Sample Cue")}, +}; + +static_assert(mpt::array_size<decltype(gVolCmdInfo)>::size == (MAX_VOLCMDS - 1)); + + +UINT EffectInfo::GetNumVolCmds() const +{ + return static_cast<UINT>(std::size(gVolCmdInfo)); +} + + +LONG EffectInfo::GetIndexFromVolCmd(ModCommand::VOLCMD volcmd) const +{ + for (UINT i = 0; i < std::size(gVolCmdInfo); i++) + { + if (gVolCmdInfo[i].volCmd == volcmd) return i; + } + return -1; +} + + +VolumeCommand EffectInfo::GetVolCmdFromIndex(UINT ndx) const +{ + return (ndx < std::size(gVolCmdInfo)) ? gVolCmdInfo[ndx].volCmd : VOLCMD_NONE; +} + + +bool EffectInfo::GetVolCmdInfo(UINT ndx, CString *s, ModCommand::VOL *prangeMin, ModCommand::VOL *prangeMax) const +{ + if (s) s->Empty(); + if (prangeMin) *prangeMin = 0; + if (prangeMax) *prangeMax = 0; + if (ndx >= std::size(gVolCmdInfo)) return false; + if (s) + { + s->Format(_T("%c: %s"), sndFile.GetModSpecifications().GetVolEffectLetter(GetVolCmdFromIndex(ndx)), gVolCmdInfo[ndx].name); + } + if ((prangeMin) && (prangeMax)) + { + switch(gVolCmdInfo[ndx].volCmd) + { + case VOLCMD_VOLUME: + case VOLCMD_PANNING: + *prangeMax = 64; + break; + + default: + *prangeMax = (sndFile.GetType() & MOD_TYPE_XM) ? 15 : 9; + } + } + return (sndFile.GetType() & gVolCmdInfo[ndx].supportedFormats); +} + + +bool EffectInfo::GetVolCmdParamInfo(const ModCommand &m, CString *s) const +{ + if(s == nullptr) return false; + s->Empty(); + + switch(m.volcmd) + { + case VOLCMD_VOLSLIDEUP: + case VOLCMD_VOLSLIDEDOWN: + case VOLCMD_FINEVOLUP: + case VOLCMD_FINEVOLDOWN: + if(m.vol > 0 || sndFile.GetType() == MOD_TYPE_XM) + { + s->Format(_T("%c%u"), + (m.volcmd == VOLCMD_VOLSLIDEUP || m.volcmd == VOLCMD_FINEVOLUP) ? _T('+') : _T('-'), + m.vol); + } else + { + *s = _T("continue"); + } + break; + + case VOLCMD_PORTAUP: + case VOLCMD_PORTADOWN: + case VOLCMD_TONEPORTAMENTO: + if(m.vol > 0) + { + ModCommand::PARAM param = m.vol << 2; + ModCommand::COMMAND cmd = CMD_PORTAMENTOUP; + if(m.volcmd == VOLCMD_PORTADOWN) + { + cmd = CMD_PORTAMENTODOWN; + } else if(m.volcmd == VOLCMD_TONEPORTAMENTO) + { + cmd = CMD_TONEPORTAMENTO; + if(sndFile.GetType() != MOD_TYPE_XM) param = ImpulseTrackerPortaVolCmd[m.vol & 0x0F]; + else param = m.vol << 4; + } + s->Format(_T("%u (%c%02X)"), + m.vol, + sndFile.GetModSpecifications().GetEffectLetter(cmd), + param); + } else + { + *s = _T("continue"); + } + break; + + case VOLCMD_OFFSET: + if(m.vol) + { + SAMPLEINDEX smp = m.instr; + if(smp > 0 && smp <= sndFile.GetNumInstruments() && m.IsNote() && sndFile.Instruments[smp] != nullptr) + { + smp = sndFile.Instruments[smp]->Keyboard[m.note - NOTE_MIN]; + } + s->Format(_T("Cue %u: "), m.vol); + if(smp > 0 && smp <= sndFile.GetNumSamples() && m.vol > 0 && m.vol <= std::size(sndFile.GetSample(smp).cues)) + { + auto cue = sndFile.GetSample(smp).cues[m.vol - 1]; + if(cue < sndFile.GetSample(smp).nLength) + s->Append(mpt::cfmt::dec(3, _T(','), sndFile.GetSample(smp).cues[m.vol - 1])); + else + s->Append(_T("unused")); + } else + s->Append(_T("unknown")); + } else + { + *s = _T("continue"); + } + break; + + case VOLCMD_PLAYCONTROL: + if(m.vol == 0) + *s = _T("Pause Playback"); + else if(m.vol == 1) + *s = _T("Continue Playback"); + break; + + default: + s->Format(_T("%u"), m.vol); + break; + } + return true; +} + + +OPENMPT_NAMESPACE_END |