1053 lines
32 KiB
C++
1053 lines
32 KiB
C++
/*
|
|
* 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
|