openmpt/
mod_command.rs

1//! Data structures meant to simplify pattern-matching against pattern data.
2//!
3//! # Remarks
4//! All the consts and enums here are from openmpt's soundlib/modcommand.h
5//! They are not part of the public API and change to OpenMPT may break those without warning.
6//! They are only available here for the sake of conveinience and completeness.
7
8const NOTE_NONE:u8 = 0;
9const NOTE_MIN:u8 = 1;
10const NOTE_MAX:u8 = 120;
11const NOTE_MIDDLEC:u8 = 5 * 12 + NOTE_MIN;
12const NOTE_KEYOFF:u8 = 0xFF;
13const NOTE_NOTECUT:u8 = 0xFE;
14const NOTE_FADE:u8 = 0xFD;
15const NOTE_PC:u8 = 0xFC;
16const NOTE_PCS:u8 = 0xFB;
17
18pub struct ModCommand {
19	pub note : Note,
20	pub instr: u8,
21	pub volcmd: VolumeCommand,
22	pub command: EffectCommand,
23}
24
25impl ModCommand {
26	/// Construct a ModCommand from pattern cell data.
27	///
28	/// ### Parameters
29	/// * `note` : The raw note command
30	/// * `instr` : The raw instrument index
31	/// * `volcmd` : The raw volume command
32	/// * `command` : The raw effect command
33	/// * `vol` : The raw volume parameter
34	/// * `param` : The raw effect parameter
35	///
36	/// ### Returns
37	/// The resulting ModCommand, or an error message
38	/// if one of the parameter has an unknown or invalid value
39	pub fn new(note : u8, instr : u8, volcmd : u8, command : u8, vol : u8, param : u8) -> Result<ModCommand, String> {
40		let note_type = ModCommand::note_from_value(note);
41		let note_type = match note_type {
42			Ok(n) => n,
43			Err(e) => return Err(e),
44		};
45
46		let vol_type = ModCommand::volume_from_command_param(volcmd, vol);
47		let vol_type = match vol_type {
48			Ok(v) => v,
49			Err(e) => return Err(e),
50		};
51
52		let effect_type = ModCommand::effect_from_command_param(command, param);
53		let effect_type = match effect_type {
54			Ok(c) => c,
55			Err(e) => return Err(e),
56		};
57
58		Ok(ModCommand {
59			note: note_type,
60			instr: instr,
61			volcmd: vol_type,
62			command: effect_type,
63		})
64	}
65
66	/// Returns the note index corresponding to a middle C (C4).
67	pub fn middle_c() -> u8 {
68		NOTE_MIDDLEC
69	}
70
71	fn note_from_value(note_val : u8) -> Result<Note, String> {
72		match note_val {
73			NOTE_NONE => Ok(Note::None),
74			NOTE_MIN...NOTE_MAX => Ok(Note::Note(note_val)),
75			NOTE_KEYOFF => Ok(Note::Special(SpecialNote::KeyOff)),
76			NOTE_NOTECUT => Ok(Note::Special(SpecialNote::NoteCut)),
77			NOTE_FADE => Ok(Note::Special(SpecialNote::Fade)),
78			NOTE_PC => Ok(Note::Special(SpecialNote::ParamControl)),
79			NOTE_PCS => Ok(Note::Special(SpecialNote::ParamControlSmooth)),
80			_ => Err("Invalid note".to_owned()),
81		}
82	}
83
84	fn effect_from_command_param(cmd : u8, param : u8) -> Result<EffectCommand, String> {
85		let nibble_x = (param & 0xF0) >> 4;
86		let nibble_y = param & 0x0F;
87
88		match cmd {
89			0  => Ok(EffectCommand::None),
90			1  => Ok(EffectCommand::Arpeggio(nibble_x, nibble_y)),
91			2  => Ok(EffectCommand::PortamentoUp(param)),
92			3  => Ok(EffectCommand::PortamentoDown(param)),
93			4  => Ok(EffectCommand::TonePortamento(param)),
94			5  => Ok(EffectCommand::Vibrato(nibble_x, nibble_y)),
95			6  => Ok(EffectCommand::TonePortaVol(nibble_x, nibble_y)),
96			7  => Ok(EffectCommand::VibratoVol(nibble_x, nibble_y)),
97			8  => Ok(EffectCommand::Tremolo(nibble_x, nibble_y)),
98			9  => Ok(EffectCommand::Panning8(param)),
99			10 => Ok(EffectCommand::Offset(param)),
100			11 => Ok(EffectCommand::VolumeSlide(nibble_x, nibble_y)),
101			12 => Ok(EffectCommand::PositionJump(param)),
102			13 => Ok(EffectCommand::Volume(param)),
103			14 => Ok(EffectCommand::PatternBreak(param)),
104			15 => Ok(EffectCommand::Retrig(nibble_x, nibble_y)),
105			16 => Ok(EffectCommand::Speed(param)),
106			17 => Ok(EffectCommand::Tempo(param)),
107			18 => Ok(EffectCommand::Tremor(nibble_x, nibble_y)),
108			19 => Ok(EffectCommand::ModCmdEX(nibble_x, nibble_y)),
109			20 => Ok(EffectCommand::S3MCmdEX(nibble_x, nibble_y)),
110			21 => Ok(EffectCommand::ChannelVolume(param)),
111			22 => Ok(EffectCommand::ChannelVolSlide(nibble_x, nibble_y)),
112			23 => Ok(EffectCommand::GlobalVolume(param)),
113			24 => Ok(EffectCommand::GlobalVolSlide(nibble_x, nibble_y)),
114			25 => Ok(EffectCommand::KeyOff(param)),
115			26 => Ok(EffectCommand::FineVibrato(nibble_x, nibble_y)),
116			27 => Ok(EffectCommand::Panbrello(nibble_x, nibble_y)),
117			28 => Ok(EffectCommand::XFinePortaUpDown(nibble_x, nibble_y)),
118			29 => Ok(EffectCommand::PanningSlide(nibble_x, nibble_y)),
119			30 => Ok(EffectCommand::SetEnvPosition(param)),
120			31 => Ok(EffectCommand::Midi(param)),
121			32 => Ok(EffectCommand::SmoothMidi(param)),
122			33 => Ok(EffectCommand::DelayCut(nibble_x, nibble_y)),
123			34 => Ok(EffectCommand::XParam(param)),
124			35 => Ok(EffectCommand::NoteSlideUp(nibble_x, nibble_y)),
125			36 => Ok(EffectCommand::NoteSlideUpRetrig(nibble_x, nibble_y)),
126			37 => Ok(EffectCommand::NoteSlideDown(nibble_x, nibble_y)),
127			38 => Ok(EffectCommand::NoteSlideDownRetrig(nibble_x, nibble_y)),
128			39 => Ok(EffectCommand::ReverseOffset(param)),
129			40 => Ok(EffectCommand::DBMEcho(nibble_x, nibble_y)),
130			41 => Ok(EffectCommand::OffsetPercentage(param)),
131			_ => Err("Invalid effect".to_owned()),
132		}
133	}
134
135	fn volume_from_command_param(cmd : u8, param : u8) -> Result<VolumeCommand, String> {
136		match cmd {
137			0  => Ok(VolumeCommand::None),
138			1  => Ok(VolumeCommand::Volume(param)),
139			2  => Ok(VolumeCommand::Panning(param)),
140			3  => Ok(VolumeCommand::VolSlideUp(param)),
141			4  => Ok(VolumeCommand::VolSlideDown(param)),
142			5  => Ok(VolumeCommand::FineVolUp(param)),
143			6  => Ok(VolumeCommand::FineVolDown(param)),
144			7  => Ok(VolumeCommand::VibratoSpeed(param)),
145			8  => Ok(VolumeCommand::VibratoDepth(param)),
146			9  => Ok(VolumeCommand::PanSlideLeft(param)),
147			10 => Ok(VolumeCommand::PanSlideRight(param)),
148			11 => Ok(VolumeCommand::TonePortamento(param)),
149			12 => Ok(VolumeCommand::PortaUp(param)),
150			13 => Ok(VolumeCommand::PortaDown(param)),
151			14 => Ok(VolumeCommand::DelayCut(param)),
152			15 => Ok(VolumeCommand::Offset(param)),
153			_  => Err("Invalid volume command".to_owned()),
154		}
155	}
156}
157
158/// An enum containing the different value for Note commands.
159pub enum Note {
160	None,
161	Note(u8),
162	Special(SpecialNote),
163}
164
165/// An enum containing the special values for Note commands.
166pub enum SpecialNote {
167	KeyOff,
168	NoteCut,
169	Fade,
170	ParamControl,
171	ParamControlSmooth,
172}
173
174/// An enum containing the different value for Volume commands.
175///
176/// Each variant contains its own volume parameter where applicable.
177///
178/// ### Remarks
179/// The documentation for each effect is there for reference purposes only
180/// and can be interpreted very differently depending on the format,
181/// internal parameters, tracker last used, whether sub-semitone variations
182/// use frequencies or periods, etc.
183///
184/// The libopenmpt developpers **do not recommend** relying on these, **you have been warned**.
185pub enum VolumeCommand {
186	None,
187	Volume(u8),
188	Panning(u8),
189	VolSlideUp(u8),
190	VolSlideDown(u8),
191	FineVolUp(u8),
192	FineVolDown(u8),
193	VibratoSpeed(u8),
194	VibratoDepth(u8),
195	PanSlideLeft(u8),
196	PanSlideRight(u8),
197	// Equivalent to the effect, but may be 4 or 16 times less precise
198	TonePortamento(u8),
199	// Equivalent to the effect, but may be 4 or 16 times less precise
200	PortaUp(u8),
201	// Equivalent to the effect, but may be 4 or 16 times less precise
202	PortaDown(u8),
203	// Unused
204	DelayCut(u8),
205	Offset(u8),
206}
207
208/// An enum containing the different value for Effect commands.
209///
210/// Each variant contains its own effect parameter where applicable.
211/// Effects that read their parameter as 2 x and y values have them pre-separated.
212///
213/// ### Remarks
214/// The documentation for each effect is there for reference purposes only
215/// and can be interpreted very differently depending on the format,
216/// internal parameters, tracker last used, whether sub-semitone variations
217/// use frequencies or periods, etc.
218///
219/// The libopenmpt developpers **do not recommend** relying on these, **you have been warned**.
220pub enum EffectCommand {
221	None,
222	/// Cycle between note, note+x and note+y on each tick
223	Arpeggio (u8, u8),
224	/// Raise pitch by xy per tick, sometimes including the first
225	///
226	/// Slide fraction is generally 1/16th of a semitone
227	PortamentoUp (u8),
228	/// Lower pitch by xy per tick, sometimes including the first
229	///
230	/// Slide fraction is generally 1/16th of a semitone
231	PortamentoDown (u8),
232	/// Slide pitch of old note towards new note by xy per tick and stop once reached.
233	///
234	/// Slide fraction is generally 1/16th of a semitone
235	TonePortamento (u8),
236	/// Modulates frequency at a speed of x steps (of 64) *PER ROW* and depth y
237	///
238	/// Depth is generally in 1/16th of a semitone
239	Vibrato (u8, u8),
240	/// Volume Slide + Continue portamento
241	TonePortaVol (u8, u8),
242	/// Volume Slide + Continue vibrato
243	VibratoVol (u8, u8),
244	/// Modulates sample volume at a speed of x steps (of 64) *PER ROW* and depth y
245	Tremolo (u8, u8),
246	/// Set panning from 0x0 to 0xF
247	Panning8 (u8),
248	/// Start playing sample at position xy * 256
249	Offset(u8),
250	/// Raise sample volume by x or lower by y on each tick but the first
251	VolumeSlide(u8, u8),
252	/// Jump to pattern at order xy
253	PositionJump(u8),
254	/// Set sample volume at xy (between 0 and 0x40)
255	Volume(u8),
256	/// Jump to row xy of pattern set to play next
257	PatternBreak(u8),
258	/// Retrigger every y ticks, x affects retrigger volume when set
259	Retrig(u8, u8),
260	/// Set speed at xy ticks per row
261	Speed(u8),
262	/// Set tempo at xy beats per minute
263	Tempo(u8),
264	/// Turn volume on for x+1 ticks and mute for y+1 ticks repeatedly
265	Tremor(u8, u8),
266	/// (Mod and XM) Super command, with x the subcommand and y the parameter.
267	ModCmdEX(u8, u8),
268	/// (S3M and IT) Super command, with x the subcommand and y the parameter.
269	S3MCmdEX(u8, u8),
270	/// Set channel volume at xy (between 0 and 0x40)
271	ChannelVolume(u8),
272	/// Raise channel volume by x or lower by y on each tick but the first
273	ChannelVolSlide(u8, u8),
274	/// Set global volume at xy (between 0 and 0x40)
275	GlobalVolume(u8),
276	/// Raise global volume by x or lower by y on each tick but the first
277	GlobalVolSlide(u8, u8),
278	/// Trigger Note Off after xy ticks
279	KeyOff(u8),
280	/// Same as vibrato, but depth is 4 times finer
281	FineVibrato(u8, u8),
282	/// Modulate panning at a speed of x steps (of 64) *PER ROW* and depth y
283	Panbrello(u8, u8),
284	/// (XM only) Super command, with x the subcommand and y the parameter.
285	XFinePortaUpDown(u8, u8),
286	/// Slide panning position right by x or left by y on each tick but the first
287	///
288	/// Depending on format and settings, it could also be apply on the first tick only or on every tick.
289	PanningSlide(u8, u8),
290	/// Sets the volume envelope position to xy ticks
291	SetEnvPosition(u8),
292	/// Execute a midi macro
293	Midi(u8),
294	/// Execute an interpolated midi macro
295	SmoothMidi(u8),
296	/// Delay note for x ticks and cut after another y ticks.
297	///
298	/// If the row ends before either effect is applied (speed is greater than x or x+y), that effect won't be applied.
299	DelayCut(u8, u8),
300	/// Combines the parameter value with the one on the row above it
301	XParam(u8),
302	NoteSlideUp(u8, u8),
303	NoteSlideDown(u8, u8),
304	NoteSlideUpRetrig(u8, u8),
305	NoteSlideDownRetrig(u8, u8),
306	ReverseOffset(u8),
307	// x : chns, y: enable
308	DBMEcho(u8, u8),
309	OffsetPercentage(u8),
310}