aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/soundlib/modcommand.h
blob: bf9049a9bdcf1db6bdf359feec34d4bc45b7dd2e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
 * modcommand.h
 * ------------
 * Purpose: ModCommand declarations and helpers. One ModCommand corresponds to one pattern cell.
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */


#pragma once

#include "openmpt/all/BuildSettings.hpp"

#include "Snd_defs.h"
#include "../common/misc_util.h"

OPENMPT_NAMESPACE_BEGIN

class CSoundFile;

// Note definitions
enum : uint8 // ModCommand::NOTE
{
	NOTE_NONE        = 0,    // Empty note cell
	NOTE_MIN         = 1,    // Minimum note value
	NOTE_MAX         = 120,  // Maximum note value
	NOTE_MIDDLEC     = (5 * 12 + NOTE_MIN),
	NOTE_KEYOFF      = 0xFF, // === (Note Off, releases envelope / fades samples, stops plugin note)
	NOTE_NOTECUT     = 0xFE, // ^^^ (Cuts sample / stops all plugin notes)
	NOTE_FADE        = 0xFD, // ~~~ (Fades samples, stops plugin note)
	NOTE_PC          = 0xFC, // Param Control 'note'. Changes param value on first tick.
	NOTE_PCS         = 0xFB, // Param Control (Smooth) 'note'. Interpolates param value during the whole row.
	NOTE_MIN_SPECIAL = NOTE_PCS,
	NOTE_MAX_SPECIAL = NOTE_KEYOFF,
};


// Volume Column commands
enum VolumeCommand : uint8
{
	VOLCMD_NONE           = 0,
	VOLCMD_VOLUME         = 1,
	VOLCMD_PANNING        = 2,
	VOLCMD_VOLSLIDEUP     = 3,
	VOLCMD_VOLSLIDEDOWN   = 4,
	VOLCMD_FINEVOLUP      = 5,
	VOLCMD_FINEVOLDOWN    = 6,
	VOLCMD_VIBRATOSPEED   = 7,
	VOLCMD_VIBRATODEPTH   = 8,
	VOLCMD_PANSLIDELEFT   = 9,
	VOLCMD_PANSLIDERIGHT  = 10,
	VOLCMD_TONEPORTAMENTO = 11,
	VOLCMD_PORTAUP        = 12,
	VOLCMD_PORTADOWN      = 13,
	VOLCMD_PLAYCONTROL    = 14,
	VOLCMD_OFFSET         = 15,
	MAX_VOLCMDS
};


// Effect column commands
enum EffectCommand : uint8
{
	CMD_NONE                = 0,
	CMD_ARPEGGIO            = 1,
	CMD_PORTAMENTOUP        = 2,
	CMD_PORTAMENTODOWN      = 3,
	CMD_TONEPORTAMENTO      = 4,
	CMD_VIBRATO             = 5,
	CMD_TONEPORTAVOL        = 6,
	CMD_VIBRATOVOL          = 7,
	CMD_TREMOLO             = 8,
	CMD_PANNING8            = 9,
	CMD_OFFSET              = 10,
	CMD_VOLUMESLIDE         = 11,
	CMD_POSITIONJUMP        = 12,
	CMD_VOLUME              = 13,
	CMD_PATTERNBREAK        = 14,
	CMD_RETRIG              = 15,
	CMD_SPEED               = 16,
	CMD_TEMPO               = 17,
	CMD_TREMOR              = 18,
	CMD_MODCMDEX            = 19,
	CMD_S3MCMDEX            = 20,
	CMD_CHANNELVOLUME       = 21,
	CMD_CHANNELVOLSLIDE     = 22,
	CMD_GLOBALVOLUME        = 23,
	CMD_GLOBALVOLSLIDE      = 24,
	CMD_KEYOFF              = 25,
	CMD_FINEVIBRATO         = 26,
	CMD_PANBRELLO           = 27,
	CMD_XFINEPORTAUPDOWN    = 28,
	CMD_PANNINGSLIDE        = 29,
	CMD_SETENVPOSITION      = 30,
	CMD_MIDI                = 31,
	CMD_SMOOTHMIDI          = 32,
	CMD_DELAYCUT            = 33,
	CMD_XPARAM              = 34,
	CMD_FINETUNE            = 35,
	CMD_FINETUNE_SMOOTH     = 36,
	CMD_DUMMY               = 37,
	CMD_NOTESLIDEUP         = 38, // IMF Gxy / PTM Jxy (Slide y notes up every x ticks)
	CMD_NOTESLIDEDOWN       = 39, // IMF Hxy / PTM Kxy (Slide y notes down every x ticks)
	CMD_NOTESLIDEUPRETRIG   = 40, // PTM Lxy (Slide y notes up every x ticks + retrigger note)
	CMD_NOTESLIDEDOWNRETRIG = 41, // PTM Mxy (Slide y notes down every x ticks + retrigger note)
	CMD_REVERSEOFFSET       = 42, // PTM Nxx Revert sample + offset
	CMD_DBMECHO             = 43, // DBM enable/disable echo
	CMD_OFFSETPERCENTAGE    = 44, // PLM Percentage Offset
	CMD_DIGIREVERSESAMPLE   = 45, // DIGI reverse sample
	MAX_EFFECTS
};


enum EffectType : uint8
{
	EFFECT_TYPE_NORMAL  = 0,
	EFFECT_TYPE_GLOBAL  = 1,
	EFFECT_TYPE_VOLUME  = 2,
	EFFECT_TYPE_PANNING = 3,
	EFFECT_TYPE_PITCH   = 4,
	MAX_EFFECT_TYPE     = 5
};


class ModCommand
{
public:
	using NOTE = uint8;
	using INSTR = uint8;
	using VOL = uint8;
	using VOLCMD = uint8;
	using COMMAND = uint8;
	using PARAM = uint8;

	// Defines the maximum value for column data when interpreted as 2-byte value
	// (for example volcmd and vol). The valid value range is [0, maxColumnValue].
	static constexpr int maxColumnValue = 999;

	// Returns empty modcommand.
	static ModCommand Empty() { return ModCommand(); }

	bool operator==(const ModCommand &mc) const
	{
		return (note == mc.note)
			&& (instr == mc.instr)
			&& (volcmd == mc.volcmd)
			&& (command == mc.command)
			&& ((volcmd == VOLCMD_NONE && !IsPcNote()) || vol == mc.vol)
			&& ((command == CMD_NONE && !IsPcNote()) || param == mc.param);
	}
	bool operator!=(const ModCommand& mc) const { return !(*this == mc); }

	void Set(NOTE n, INSTR ins, uint16 volcol, uint16 effectcol) { note = n; instr = ins; SetValueVolCol(volcol); SetValueEffectCol(effectcol); }

	uint16 GetValueVolCol() const { return GetValueVolCol(volcmd, vol); }
	static uint16 GetValueVolCol(uint8 volcmd, uint8 vol) { return (volcmd << 8) + vol; }
	void SetValueVolCol(const uint16 val) { volcmd = static_cast<VOLCMD>(val >> 8); vol = static_cast<uint8>(val & 0xFF); }

	uint16 GetValueEffectCol() const { return GetValueEffectCol(command, param); }
	static uint16 GetValueEffectCol(uint8 command, uint8 param) { return (command << 8) + param; }
	void SetValueEffectCol(const uint16 val) { command = static_cast<COMMAND>(val >> 8); param = static_cast<uint8>(val & 0xFF); }

	// Clears modcommand.
	void Clear() { memset(this, 0, sizeof(ModCommand)); }

	// Returns true if modcommand is empty, false otherwise.
	bool IsEmpty() const
	{
		return (note == NOTE_NONE && instr == 0 && volcmd == VOLCMD_NONE && command == CMD_NONE);
	}

	// Returns true if instrument column represents plugin index.
	bool IsInstrPlug() const { return IsPcNote(); }

	// Returns true if and only if note is NOTE_PC or NOTE_PCS.
	bool IsPcNote() const { return IsPcNote(note); }
	static bool IsPcNote(NOTE note) { return note == NOTE_PC || note == NOTE_PCS; }

	// Returns true if and only if note is a valid musical note.
	bool IsNote() const { return mpt::is_in_range(note, NOTE_MIN, NOTE_MAX); }
	static bool IsNote(NOTE note) { return mpt::is_in_range(note, NOTE_MIN, NOTE_MAX); }
	// Returns true if and only if note is a valid special note.
	bool IsSpecialNote() const { return mpt::is_in_range(note, NOTE_MIN_SPECIAL, NOTE_MAX_SPECIAL); }
	static bool IsSpecialNote(NOTE note) { return mpt::is_in_range(note, NOTE_MIN_SPECIAL, NOTE_MAX_SPECIAL); }
	// Returns true if and only if note is a valid musical note or the note entry is empty.
	bool IsNoteOrEmpty() const { return note == NOTE_NONE || IsNote(); }
	static bool IsNoteOrEmpty(NOTE note) { return note == NOTE_NONE || IsNote(note); }
	// Returns true if any of the commands in this cell trigger a tone portamento.
	bool IsPortamento() const { return command == CMD_TONEPORTAMENTO || command == CMD_TONEPORTAVOL || volcmd == VOLCMD_TONEPORTAMENTO; }
	// Returns true if the cell contains a sliding or otherwise continuous effect command.
	bool IsContinousCommand(const CSoundFile &sndFile) const;
	bool IsContinousVolColCommand() const;
	// Returns true if the cell contains a sliding command with separate up/down nibbles.
	bool IsSlideUpDownCommand() const;
	// Returns true if the cell contains an effect command that may affect the global state of the module.
	bool IsGlobalCommand() const { return IsGlobalCommand(command, param); }
	static bool IsGlobalCommand(COMMAND command, PARAM param);

	// Returns true if the note is inside the Amiga frequency range
	bool IsAmigaNote() const { return IsAmigaNote(note); }
	static bool IsAmigaNote(NOTE note) { return !IsNote(note) || (note >= NOTE_MIDDLEC - 12 && note < NOTE_MIDDLEC + 24); }

	static EffectType GetEffectType(COMMAND cmd);
	EffectType GetEffectType() const { return GetEffectType(command); }
	static EffectType GetVolumeEffectType(VOLCMD volcmd);
	EffectType GetVolumeEffectType() const { return GetVolumeEffectType(volcmd); }

	// Convert a complete ModCommand item from one format to another
	void Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &sndFile);
	// Convert MOD/XM Exx to S3M/IT Sxx
	void ExtendedMODtoS3MEffect();
	// Convert S3M/IT Sxx to MOD/XM Exx
	void ExtendedS3MtoMODEffect();

	// "Importance" of every FX command. Table is used for importing from formats with multiple effect columns
	// and is approximately the same as in SchismTracker.
	static size_t GetEffectWeight(COMMAND cmd);
	// Try to convert a an effect into a volume column effect. Returns true on success.
	static bool ConvertVolEffect(uint8 &effect, uint8 &param, bool force);
	// Takes two "normal" effect commands and converts them to volume column + effect column commands. Returns the dropped command + param (CMD_NONE if nothing had to be dropped).
	static std::pair<EffectCommand, PARAM> TwoRegularCommandsToMPT(uint8 &effect1, uint8 &param1, uint8 &effect2, uint8 &param2);
	// Try to combine two commands into one. Returns true on success and the combined command is placed in eff1 / param1.
	static bool CombineEffects(uint8 &eff1, uint8 &param1, uint8 &eff2, uint8 &param2);

public:
	uint8 note = NOTE_NONE;
	uint8 instr = 0;
	uint8 volcmd = VOLCMD_NONE;
	uint8 command = CMD_NONE;
	uint8 vol = 0;
	uint8 param = 0;
};

OPENMPT_NAMESPACE_END