1 /*
2  * Snd_fx.cpp
3  * -----------
4  * Purpose: Processing of pattern commands, song length calculation...
5  * Notes  : This needs some heavy refactoring.
6  *          I thought of actually adding an effect interface class. Every pattern effect
7  *          could then be moved into its own class that inherits from the effect interface.
8  *          If effect handling differs severly between module formats, every format would have
9  *          its own class for that effect. Then, a call chain of effect classes could be set up
10  *          for each format, since effects cannot be processed in the same order in all formats.
11  * Authors: Olivier Lapicque
12  *          OpenMPT Devs
13  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
14  */
15 
16 
17 #include "stdafx.h"
18 #include "Sndfile.h"
19 #include "mod_specifications.h"
20 #ifdef MODPLUG_TRACKER
21 #include "../mptrack/Moddoc.h"
22 #endif // MODPLUG_TRACKER
23 #include "tuning.h"
24 #include "Tables.h"
25 #include "modsmp_ctrl.h"	// For updating the loop wraparound data with the invert loop effect
26 #include "plugins/PlugInterface.h"
27 #include "OPL.h"
28 #include "MIDIEvents.h"
29 
30 OPENMPT_NAMESPACE_BEGIN
31 
32 // Formats which have 7-bit (0...128) instead of 6-bit (0...64) global volume commands, or which are imported to this range (mostly formats which are converted to IT internally)
33 #ifdef MODPLUG_TRACKER
34 static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_MT2;
35 #else
36 static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_NONE;
37 #endif // MODPLUG_TRACKER
38 static constexpr auto GLOBALVOL_7BIT_FORMATS = MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT;
39 
40 
41 // Compensate frequency slide LUTs depending on whether we are handling periods or frequency - "up" and "down" in function name are seen from frequency perspective.
GetLinearSlideDownTable(const CSoundFile * sndFile,uint32 i)42 static uint32 GetLinearSlideDownTable    (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(LinearSlideDownTable));     return sndFile->m_playBehaviour[kPeriodsAreHertz] ? LinearSlideDownTable[i]     : LinearSlideUpTable[i]; }
GetLinearSlideUpTable(const CSoundFile * sndFile,uint32 i)43 static uint32 GetLinearSlideUpTable      (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(LinearSlideDownTable));     return sndFile->m_playBehaviour[kPeriodsAreHertz] ? LinearSlideUpTable[i]       : LinearSlideDownTable[i]; }
GetFineLinearSlideDownTable(const CSoundFile * sndFile,uint32 i)44 static uint32 GetFineLinearSlideDownTable(const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideDownTable[i] : FineLinearSlideUpTable[i]; }
GetFineLinearSlideUpTable(const CSoundFile * sndFile,uint32 i)45 static uint32 GetFineLinearSlideUpTable  (const CSoundFile *sndFile, uint32 i) { MPT_ASSERT(i < std::size(FineLinearSlideDownTable)); return sndFile->m_playBehaviour[kPeriodsAreHertz] ? FineLinearSlideUpTable[i]   : FineLinearSlideDownTable[i]; }
46 
47 
48 ////////////////////////////////////////////////////////////
49 // Length
50 
51 
52 // Memory class for GetLength() code
53 class GetLengthMemory
54 {
55 protected:
56 	const CSoundFile &sndFile;
57 
58 public:
59 	std::unique_ptr<CSoundFile::PlayState> state;
60 	struct ChnSettings
61 	{
62 		uint32 ticksToRender = 0;	// When using sample sync, we still need to render this many ticks
63 		bool incChanged = false;	// When using sample sync, note frequency has changed
64 		uint8 vol = 0xFF;
65 	};
66 
67 #ifndef NO_PLUGINS
68 	typedef std::map<std::pair<ModCommand::INSTR, uint16>, uint16> PlugParamMap;
69 	PlugParamMap plugParams;
70 #endif
71 	std::vector<ChnSettings> chnSettings;
72 	double elapsedTime;
73 	static constexpr uint32 IGNORE_CHANNEL = uint32_max;
74 
GetLengthMemory(const CSoundFile & sf)75 	GetLengthMemory(const CSoundFile &sf)
76 		: sndFile(sf)
77 		, state(std::make_unique<CSoundFile::PlayState>(sf.m_PlayState))
78 	{
79 		Reset();
80 	}
81 
Reset()82 	void Reset()
83 	{
84 #ifndef NO_PLUGINS
85 		plugParams.clear();
86 #endif
87 		elapsedTime = 0.0;
88 		state->m_lTotalSampleCount = 0;
89 		state->m_nMusicSpeed = sndFile.m_nDefaultSpeed;
90 		state->m_nMusicTempo = sndFile.m_nDefaultTempo;
91 		state->m_nGlobalVolume = sndFile.m_nDefaultGlobalVolume;
92 		chnSettings.assign(sndFile.GetNumChannels(), ChnSettings());
93 		const auto muteFlag = CSoundFile::GetChannelMuteFlag();
94 		for(CHANNELINDEX chn = 0; chn < sndFile.GetNumChannels(); chn++)
95 		{
96 			state->Chn[chn].Reset(ModChannel::resetTotal, sndFile, chn, muteFlag);
97 			state->Chn[chn].nOldGlobalVolSlide = 0;
98 			state->Chn[chn].nOldChnVolSlide = 0;
99 			state->Chn[chn].nNote = state->Chn[chn].nNewNote = state->Chn[chn].nLastNote = NOTE_NONE;
100 		}
101 	}
102 
103 	// Increment playback position of sample and envelopes on a channel
RenderChannel(CHANNELINDEX channel,uint32 tickDuration,uint32 portaStart=uint32_max)104 	void RenderChannel(CHANNELINDEX channel, uint32 tickDuration, uint32 portaStart = uint32_max)
105 	{
106 		ModChannel &chn = state->Chn[channel];
107 		uint32 numTicks = chnSettings[channel].ticksToRender;
108 		if(numTicks == IGNORE_CHANNEL || numTicks == 0 || (!chn.IsSamplePlaying() && !chnSettings[channel].incChanged) || chn.pModSample == nullptr)
109 		{
110 			return;
111 		}
112 
113 		const SamplePosition loopStart(chn.dwFlags[CHN_LOOP] ? chn.nLoopStart : 0u, 0);
114 		const SamplePosition sampleEnd(chn.dwFlags[CHN_LOOP] ? chn.nLoopEnd : chn.nLength, 0);
115 		const SmpLength loopLength = chn.nLoopEnd - chn.nLoopStart;
116 		const bool itEnvMode = sndFile.m_playBehaviour[kITEnvelopePositionHandling];
117 		const bool updatePitchEnv = (chn.PitchEnv.flags & (ENV_ENABLED | ENV_FILTER)) == ENV_ENABLED;
118 		bool stopNote = false;
119 
120 		SamplePosition inc = chn.increment * tickDuration;
121 		if(chn.dwFlags[CHN_PINGPONGFLAG]) inc.Negate();
122 
123 		for(uint32 i = 0; i < numTicks; i++)
124 		{
125 			bool updateInc = (chn.PitchEnv.flags & (ENV_ENABLED | ENV_FILTER)) == ENV_ENABLED;
126 			if(i >= portaStart)
127 			{
128 				chn.isFirstTick = false;
129 				const ModCommand &m = *sndFile.Patterns[state->m_nPattern].GetpModCommand(state->m_nRow, channel);
130 				auto command = m.command;
131 				if(m.volcmd == VOLCMD_TONEPORTAMENTO)
132 				{
133 					const auto [porta, clearEffectCommand] = sndFile.GetVolCmdTonePorta(m, 0);
134 					sndFile.TonePortamento(chn, porta);
135 					if(clearEffectCommand)
136 						command = CMD_NONE;
137 				}
138 				if(command == CMD_TONEPORTAMENTO)
139 					sndFile.TonePortamento(chn, m.param);
140 				else if(command == CMD_TONEPORTAVOL)
141 					sndFile.TonePortamento(chn, 0);
142 				updateInc = true;
143 			}
144 
145 			int32 period = chn.nPeriod;
146 			if(itEnvMode) sndFile.IncrementEnvelopePositions(chn);
147 			if(updatePitchEnv)
148 			{
149 				sndFile.ProcessPitchFilterEnvelope(chn, period);
150 				updateInc = true;
151 			}
152 			if(!itEnvMode) sndFile.IncrementEnvelopePositions(chn);
153 			int vol = 0;
154 			sndFile.ProcessInstrumentFade(chn, vol);
155 
156 			if(chn.dwFlags[CHN_ADLIB])
157 				continue;
158 
159 			if(updateInc || chnSettings[channel].incChanged)
160 			{
161 				if(chn.m_CalculateFreq || chn.m_ReCalculateFreqOnFirstTick)
162 				{
163 					chn.RecalcTuningFreq(1, 0, sndFile);
164 					if(!chn.m_CalculateFreq)
165 						chn.m_ReCalculateFreqOnFirstTick = false;
166 					else
167 						chn.m_CalculateFreq = false;
168 				}
169 				chn.increment = sndFile.GetChannelIncrement(chn, period, 0).first;
170 				chnSettings[channel].incChanged = false;
171 				inc = chn.increment * tickDuration;
172 				if(chn.dwFlags[CHN_PINGPONGFLAG]) inc.Negate();
173 			}
174 
175 			chn.position += inc;
176 
177 			if(chn.position >= sampleEnd || (chn.position < loopStart && inc.IsNegative()))
178 			{
179 				if(!chn.dwFlags[CHN_LOOP])
180 				{
181 					// Past sample end.
182 					stopNote = true;
183 					break;
184 				}
185 				// We exceeded the sample loop, go back to loop start.
186 				if(chn.dwFlags[CHN_PINGPONGLOOP])
187 				{
188 					if(chn.position < loopStart)
189 					{
190 						chn.position = SamplePosition(chn.nLoopStart + chn.nLoopStart, 0) - chn.position;
191 						chn.dwFlags.flip(CHN_PINGPONGFLAG);
192 						inc.Negate();
193 					}
194 					SmpLength posInt = chn.position.GetUInt() - chn.nLoopStart;
195 					SmpLength pingpongLength = loopLength * 2;
196 					if(sndFile.m_playBehaviour[kITPingPongMode]) pingpongLength--;
197 					posInt %= pingpongLength;
198 					bool forward = (posInt < loopLength);
199 					if(forward)
200 						chn.position.SetInt(chn.nLoopStart + posInt);
201 					else
202 						chn.position.SetInt(chn.nLoopEnd - (posInt - loopLength));
203 					if(forward == chn.dwFlags[CHN_PINGPONGFLAG])
204 					{
205 						chn.dwFlags.flip(CHN_PINGPONGFLAG);
206 						inc.Negate();
207 					}
208 				} else
209 				{
210 					SmpLength posInt = chn.position.GetUInt();
211 					if(posInt >= chn.nLoopEnd + loopLength)
212 					{
213 						const SmpLength overshoot = posInt - chn.nLoopEnd;
214 						posInt -= (overshoot / loopLength) * loopLength;
215 					}
216 					while(posInt >= chn.nLoopEnd)
217 					{
218 						posInt -= loopLength;
219 					}
220 					chn.position.SetInt(posInt);
221 				}
222 			}
223 		}
224 
225 		if(stopNote)
226 		{
227 			chn.Stop();
228 			chn.nPortamentoDest = 0;
229 		}
230 		chnSettings[channel].ticksToRender = 0;
231 	}
232 };
233 
234 
235 // Get mod length in various cases. Parameters:
236 // [in]  adjustMode: See enmGetLengthResetMode for possible adjust modes.
237 // [in]  target: Time or position target which should be reached, or no target to get length of the first sub song. Use GetLengthTarget::StartPos to also specify a position from where the seeking should begin.
238 // [out] See definition of type GetLengthType for the returned values.
GetLength(enmGetLengthResetMode adjustMode,GetLengthTarget target)239 std::vector<GetLengthType> CSoundFile::GetLength(enmGetLengthResetMode adjustMode, GetLengthTarget target)
240 {
241 	std::vector<GetLengthType> results;
242 	GetLengthType retval;
243 
244 	// Are we trying to reach a certain pattern position?
245 	const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs;
246 	const bool adjustSamplePos = (adjustMode & eAdjustSamplePositions) == eAdjustSamplePositions;
247 
248 	SEQUENCEINDEX sequence = target.sequence;
249 	if(sequence >= Order.GetNumSequences()) sequence = Order.GetCurrentSequenceIndex();
250 	const ModSequence &orderList = Order(sequence);
251 
252 	GetLengthMemory memory(*this);
253 	CSoundFile::PlayState &playState = *memory.state;
254 	// Temporary visited rows vector (so that GetLength() won't interfere with the player code if the module is playing at the same time)
255 	RowVisitor visitedRows(*this, sequence);
256 	ROWINDEX allowedPatternLoopComplexity = 32768;
257 
258 	// If sequence starts with some non-existent patterns, find a better start
259 	while(target.startOrder < orderList.size() && !orderList.IsValidPat(target.startOrder))
260 	{
261 		target.startOrder++;
262 		target.startRow = 0;
263 	}
264 	retval.startRow = playState.m_nNextRow = playState.m_nRow = target.startRow;
265 	retval.startOrder = playState.m_nNextOrder = playState.m_nCurrentOrder = target.startOrder;
266 
267 	// Fast LUTs for commands that are too weird / complicated / whatever to emulate in sample position adjust mode.
268 	std::bitset<MAX_EFFECTS> forbiddenCommands;
269 	std::bitset<MAX_VOLCMDS> forbiddenVolCommands;
270 
271 	if(adjustSamplePos)
272 	{
273 		forbiddenCommands.set(CMD_ARPEGGIO);       forbiddenCommands.set(CMD_PORTAMENTOUP);
274 		forbiddenCommands.set(CMD_PORTAMENTODOWN); forbiddenCommands.set(CMD_XFINEPORTAUPDOWN);
275 		forbiddenCommands.set(CMD_NOTESLIDEUP);    forbiddenCommands.set(CMD_NOTESLIDEUPRETRIG);
276 		forbiddenCommands.set(CMD_NOTESLIDEDOWN);  forbiddenCommands.set(CMD_NOTESLIDEDOWNRETRIG);
277 		forbiddenVolCommands.set(VOLCMD_PORTAUP);  forbiddenVolCommands.set(VOLCMD_PORTADOWN);
278 
279 		if(target.mode == GetLengthTarget::SeekPosition && target.pos.order < orderList.size())
280 		{
281 			// If we know where to seek, we can directly rule out any channels on which a new note would be triggered right at the start.
282 			const PATTERNINDEX seekPat = orderList[target.pos.order];
283 			if(Patterns.IsValidPat(seekPat) && Patterns[seekPat].IsValidRow(target.pos.row))
284 			{
285 				const ModCommand *m = Patterns[seekPat].GetpModCommand(target.pos.row, 0);
286 				for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++)
287 				{
288 					if(m->note == NOTE_NOTECUT || m->note == NOTE_KEYOFF || (m->note == NOTE_FADE && GetNumInstruments())
289 						|| (m->IsNote() && !m->IsPortamento()))
290 					{
291 						memory.chnSettings[i].ticksToRender = GetLengthMemory::IGNORE_CHANNEL;
292 					}
293 				}
294 			}
295 		}
296 	}
297 
298 	// If samples are being synced, force them to resync if tick duration changes
299 	uint32 oldTickDuration = 0;
300 	bool breakToRow = false;
301 
302 	for (;;)
303 	{
304 		const bool ignoreRow = NextRow(playState, breakToRow).first;
305 
306 		// Time target reached.
307 		if(target.mode == GetLengthTarget::SeekSeconds && memory.elapsedTime >= target.time)
308 		{
309 			retval.targetReached = true;
310 			break;
311 		}
312 
313 		// Check if pattern is valid
314 		playState.m_nPattern = playState.m_nCurrentOrder < orderList.size() ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex();
315 
316 		if(!Patterns.IsValidPat(playState.m_nPattern) && playState.m_nPattern != orderList.GetInvalidPatIndex() && target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order)
317 		{
318 			// Early test: Target is inside +++ or non-existing pattern
319 			retval.targetReached = true;
320 			break;
321 		}
322 
323 		while(playState.m_nPattern >= Patterns.Size())
324 		{
325 			// End of song?
326 			if((playState.m_nPattern == orderList.GetInvalidPatIndex()) || (playState.m_nCurrentOrder >= orderList.size()))
327 			{
328 				if(playState.m_nCurrentOrder == orderList.GetRestartPos())
329 					break;
330 				else
331 					playState.m_nCurrentOrder = orderList.GetRestartPos();
332 			} else
333 			{
334 				playState.m_nCurrentOrder++;
335 			}
336 			playState.m_nPattern = (playState.m_nCurrentOrder < orderList.size()) ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex();
337 			playState.m_nNextOrder = playState.m_nCurrentOrder;
338 			if((!Patterns.IsValidPat(playState.m_nPattern)) && visitedRows.Visit(playState.m_nCurrentOrder, 0, playState.Chn, ignoreRow))
339 			{
340 				if(!hasSearchTarget)
341 				{
342 					retval.lastOrder = playState.m_nCurrentOrder;
343 					retval.lastRow = 0;
344 				}
345 				if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
346 				{
347 					// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
348 					break;
349 				} else
350 				{
351 					// We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
352 					retval.duration = memory.elapsedTime;
353 					results.push_back(retval);
354 					retval.startRow = playState.m_nRow;
355 					retval.startOrder = playState.m_nNextOrder;
356 					memory.Reset();
357 
358 					playState.m_nCurrentOrder = playState.m_nNextOrder;
359 					playState.m_nPattern = orderList[playState.m_nCurrentOrder];
360 					playState.m_nNextRow = playState.m_nRow;
361 					break;
362 				}
363 			}
364 		}
365 		if(playState.m_nNextOrder == ORDERINDEX_INVALID)
366 		{
367 			// GetFirstUnvisitedRow failed, so there is nothing more to play
368 			break;
369 		}
370 
371 		// Skip non-existing patterns
372 		if(!Patterns.IsValidPat(playState.m_nPattern))
373 		{
374 			// If there isn't even a tune, we should probably stop here.
375 			if(playState.m_nCurrentOrder == orderList.GetRestartPos())
376 			{
377 				if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
378 				{
379 					// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
380 					break;
381 				} else
382 				{
383 					// We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
384 					retval.duration = memory.elapsedTime;
385 					results.push_back(retval);
386 					retval.startRow = playState.m_nRow;
387 					retval.startOrder = playState.m_nNextOrder;
388 					memory.Reset();
389 					playState.m_nNextRow = playState.m_nRow;
390 					continue;
391 				}
392 			}
393 			playState.m_nNextOrder = playState.m_nCurrentOrder + 1;
394 			continue;
395 		}
396 		// Should never happen
397 		if(playState.m_nRow >= Patterns[playState.m_nPattern].GetNumRows())
398 			playState.m_nRow = 0;
399 
400 		// Check whether target was reached.
401 		if(target.mode == GetLengthTarget::SeekPosition && playState.m_nCurrentOrder == target.pos.order && playState.m_nRow == target.pos.row)
402 		{
403 			retval.targetReached = true;
404 			break;
405 		}
406 
407 		// If pattern loops are nested too deeply, they can cause an effectively infinite amount of loop evalations to be generated.
408 		// As we don't want the user to wait forever, we bail out if the pattern loops are too complex.
409 		const bool moduleTooComplex = target.mode != GetLengthTarget::SeekSeconds && visitedRows.ModuleTooComplex(allowedPatternLoopComplexity);
410 		if(moduleTooComplex)
411 		{
412 			memory.elapsedTime = std::numeric_limits<decltype(memory.elapsedTime)>::infinity();
413 			// Decrease allowed complexity with each subsong, as this seems to be a malicious module
414 			if(allowedPatternLoopComplexity > 256)
415 				allowedPatternLoopComplexity /= 2;
416 			visitedRows.ResetComplexity();
417 		}
418 
419 		if(visitedRows.Visit(playState.m_nCurrentOrder, playState.m_nRow, playState.Chn, ignoreRow) || moduleTooComplex)
420 		{
421 			if(!hasSearchTarget)
422 			{
423 				retval.lastOrder = playState.m_nCurrentOrder;
424 				retval.lastRow = playState.m_nRow;
425 			}
426 			if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true))
427 			{
428 				// We aren't searching for a specific row, or we couldn't find any more unvisited rows.
429 				break;
430 			} else
431 			{
432 				// We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
433 				retval.duration = memory.elapsedTime;
434 				results.push_back(retval);
435 				retval.startRow = playState.m_nRow;
436 				retval.startOrder = playState.m_nNextOrder;
437 				memory.Reset();
438 				playState.m_nNextRow = playState.m_nRow;
439 				continue;
440 			}
441 		}
442 
443 		retval.endOrder = playState.m_nCurrentOrder;
444 		retval.endRow = playState.m_nRow;
445 
446 		// Update next position
447 		SetupNextRow(playState, false);
448 
449 		// Jumped to invalid pattern row?
450 		if(playState.m_nRow >= Patterns[playState.m_nPattern].GetNumRows())
451 		{
452 			playState.m_nRow = 0;
453 		}
454 
455 		if(ignoreRow)
456 			continue;
457 
458 		// For various effects, we need to know first how many ticks there are in this row.
459 		const ModCommand *p = Patterns[playState.m_nPattern].GetpModCommand(playState.m_nRow, 0);
460 		const bool ignoreMutedChn = m_playBehaviour[kST3NoMutedChannels];
461 		for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, p++)
462 		{
463 			ModChannel &chn = playState.Chn[nChn];
464 			if(p->IsEmpty() || (ignoreMutedChn && ChnSettings[nChn].dwFlags[CHN_MUTE]))  // not even effects are processed on muted S3M channels
465 			{
466 				chn.rowCommand.Clear();
467 				continue;
468 			}
469 			if(p->IsPcNote())
470 			{
471 #ifndef NO_PLUGINS
472 				if((adjustMode & eAdjust) && p->instr > 0 && p->instr <= MAX_MIXPLUGINS)
473 				{
474 					memory.plugParams[std::make_pair(p->instr, p->GetValueVolCol())] = p->GetValueEffectCol();
475 				}
476 #endif // NO_PLUGINS
477 				chn.rowCommand.Clear();
478 				continue;
479 			}
480 			chn.rowCommand = *p;
481 			switch(p->command)
482 			{
483 			case CMD_SPEED:
484 				SetSpeed(playState, p->param);
485 				break;
486 
487 			case CMD_TEMPO:
488 				if(m_playBehaviour[kMODVBlankTiming])
489 				{
490 					// ProTracker MODs with VBlank timing: All Fxx parameters set the tick count.
491 					if(p->param != 0) SetSpeed(playState, p->param);
492 				}
493 				break;
494 
495 			case CMD_S3MCMDEX:
496 				if((p->param & 0xF0) == 0x60)
497 				{
498 					// Fine Pattern Delay
499 					playState.m_nFrameDelay += (p->param & 0x0F);
500 				} else if((p->param & 0xF0) == 0xE0 && !playState.m_nPatternDelay)
501 				{
502 					// Pattern Delay
503 					if(!(GetType() & MOD_TYPE_S3M) || (p->param & 0x0F) != 0)
504 					{
505 						// While Impulse Tracker *does* count S60 as a valid row delay (and thus ignores any other row delay commands on the right),
506 						// Scream Tracker 3 simply ignores such commands.
507 						playState.m_nPatternDelay = 1 + (p->param & 0x0F);
508 					}
509 				}
510 				break;
511 
512 			case CMD_MODCMDEX:
513 				if((p->param & 0xF0) == 0xE0)
514 				{
515 					// Pattern Delay
516 					playState.m_nPatternDelay = 1 + (p->param & 0x0F);
517 				}
518 				break;
519 			}
520 		}
521 		const uint32 numTicks = playState.TicksOnRow();
522 		const uint32 nonRowTicks = numTicks - std::max(playState.m_nPatternDelay, uint32(1));
523 
524 		playState.m_patLoopRow = ROWINDEX_INVALID;
525 		playState.m_breakRow = ROWINDEX_INVALID;
526 		playState.m_posJump = ORDERINDEX_INVALID;
527 
528 		for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
529 		{
530 			ModChannel &chn = playState.Chn[nChn];
531 			if(chn.rowCommand.IsEmpty())
532 				continue;
533 			ModCommand::COMMAND command = chn.rowCommand.command;
534 			ModCommand::PARAM param = chn.rowCommand.param;
535 			ModCommand::NOTE note = chn.rowCommand.note;
536 
537 			if(adjustMode & eAdjust)
538 			{
539 				if(chn.rowCommand.instr)
540 				{
541 					chn.nNewIns = chn.rowCommand.instr;
542 					chn.nLastNote = NOTE_NONE;
543 					memory.chnSettings[nChn].vol = 0xFF;
544 				}
545 				if(chn.rowCommand.IsNote())
546 					chn.nLastNote = note;
547 
548 				// Update channel panning
549 				if(chn.rowCommand.IsNote() || chn.rowCommand.instr)
550 				{
551 					ModInstrument *pIns;
552 					if(chn.nNewIns > 0 && chn.nNewIns <= GetNumInstruments() && (pIns = Instruments[chn.nNewIns]) != nullptr)
553 					{
554 						if(pIns->dwFlags[INS_SETPANNING])
555 							chn.SetInstrumentPan(pIns->nPan, *this);
556 					}
557 					const SAMPLEINDEX smp = GetSampleIndex(note, chn.nNewIns);
558 					if(smp > 0)
559 					{
560 						if(Samples[smp].uFlags[CHN_PANNING])
561 							chn.SetInstrumentPan(Samples[smp].nPan, *this);
562 					}
563 				}
564 
565 				switch(chn.rowCommand.volcmd)
566 				{
567 				case VOLCMD_VOLUME:
568 					memory.chnSettings[nChn].vol = chn.rowCommand.vol;
569 					break;
570 				case VOLCMD_VOLSLIDEUP:
571 				case VOLCMD_VOLSLIDEDOWN:
572 					if(chn.rowCommand.vol != 0)
573 						chn.nOldVolParam = chn.rowCommand.vol;
574 					break;
575 				case VOLCMD_TONEPORTAMENTO:
576 					if(chn.rowCommand.vol)
577 					{
578 						const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, 0);
579 						chn.portamentoSlide = porta;
580 						if(clearEffectCommand)
581 							command = CMD_NONE;
582 					}
583 					break;
584 				}
585 			}
586 
587 			switch(command)
588 			{
589 			// Position Jump
590 			case CMD_POSITIONJUMP:
591 				PositionJump(playState, nChn);
592 				break;
593 
594 			// Pattern Break
595 			case CMD_PATTERNBREAK:
596 				if(ROWINDEX row = PatternBreak(playState, nChn, param); row != ROWINDEX_INVALID)
597 					playState.m_breakRow = row;
598 			break;
599 
600 			// Set Tempo
601 			case CMD_TEMPO:
602 				if(!m_playBehaviour[kMODVBlankTiming])
603 				{
604 					TEMPO tempo(CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn), 0);
605 					if ((adjustMode & eAdjust) && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)))
606 					{
607 						if (tempo.GetInt()) chn.nOldTempo = static_cast<uint8>(tempo.GetInt()); else tempo.Set(chn.nOldTempo);
608 					}
609 
610 					if (tempo.GetInt() >= 0x20) playState.m_nMusicTempo = tempo;
611 					else
612 					{
613 						// Tempo Slide
614 						TEMPO tempoDiff((tempo.GetInt() & 0x0F) * nonRowTicks, 0);
615 						if ((tempo.GetInt() & 0xF0) == 0x10)
616 						{
617 							playState.m_nMusicTempo += tempoDiff;
618 						} else
619 						{
620 							if(tempoDiff < playState.m_nMusicTempo)
621 								playState.m_nMusicTempo -= tempoDiff;
622 							else
623 								playState.m_nMusicTempo.Set(0);
624 						}
625 					}
626 
627 					TEMPO tempoMin = GetModSpecifications().GetTempoMin(), tempoMax = GetModSpecifications().GetTempoMax();
628 					if(m_playBehaviour[kTempoClamp])  // clamp tempo correctly in compatible mode
629 					{
630 						tempoMax.Set(255);
631 					}
632 					Limit(playState.m_nMusicTempo, tempoMin, tempoMax);
633 				}
634 				break;
635 
636 			case CMD_S3MCMDEX:
637 				switch(param & 0xF0)
638 				{
639 				case 0x90:
640 					if(param <= 0x91)
641 						chn.dwFlags.set(CHN_SURROUND, param == 0x91);
642 					break;
643 
644 				case 0xA0:  // High sample offset
645 					chn.nOldHiOffset = param & 0x0F;
646 					break;
647 
648 				case 0xB0:  // Pattern Loop
649 					PatternLoop(playState, chn, param & 0x0F);
650 					break;
651 
652 				case 0xF0:  // Active macro
653 					chn.nActiveMacro = param & 0x0F;
654 					break;
655 				}
656 				break;
657 
658 			case CMD_MODCMDEX:
659 				switch(param & 0xF0)
660 				{
661 				case 0x60:  // Pattern Loop
662 					PatternLoop(playState, chn, param & 0x0F);
663 					break;
664 
665 				case 0xF0:  // Active macro
666 					chn.nActiveMacro = param & 0x0F;
667 					break;
668 				}
669 				break;
670 
671 			case CMD_XFINEPORTAUPDOWN:
672 				// ignore high offset in compatible mode
673 				if(((param & 0xF0) == 0xA0) && !m_playBehaviour[kFT2RestrictXCommand])
674 					chn.nOldHiOffset = param & 0x0F;
675 				break;
676 			}
677 
678 			// The following calculations are not interesting if we just want to get the song length.
679 			if(!(adjustMode & eAdjust))
680 				continue;
681 			switch(command)
682 			{
683 			// Portamento Up/Down
684 			case CMD_PORTAMENTOUP:
685 				if(param)
686 				{
687 					// FT2 compatibility: Separate effect memory for all portamento commands
688 					// Test case: Porta-LinkMem.xm
689 					if(!m_playBehaviour[kFT2PortaUpDownMemory])
690 						chn.nOldPortaDown = param;
691 					chn.nOldPortaUp = param;
692 				}
693 				break;
694 			case CMD_PORTAMENTODOWN:
695 				if(param)
696 				{
697 					// FT2 compatibility: Separate effect memory for all portamento commands
698 					// Test case: Porta-LinkMem.xm
699 					if(!m_playBehaviour[kFT2PortaUpDownMemory])
700 						chn.nOldPortaUp = param;
701 					chn.nOldPortaDown = param;
702 				}
703 				break;
704 			// Tone-Portamento
705 			case CMD_TONEPORTAMENTO:
706 				if (param) chn.portamentoSlide = param;
707 				break;
708 			// Offset
709 			case CMD_OFFSET:
710 				if(param)
711 					chn.oldOffset = param << 8;
712 				break;
713 			// Volume Slide
714 			case CMD_VOLUMESLIDE:
715 			case CMD_TONEPORTAVOL:
716 				if (param) chn.nOldVolumeSlide = param;
717 				break;
718 			// Set Volume
719 			case CMD_VOLUME:
720 				memory.chnSettings[nChn].vol = param;
721 				break;
722 			// Global Volume
723 			case CMD_GLOBALVOLUME:
724 				if(!(GetType() & GLOBALVOL_7BIT_FORMATS) && param < 128) param *= 2;
725 				// IT compatibility 16. ST3 and IT ignore out-of-range values
726 				if(param <= 128)
727 				{
728 					playState.m_nGlobalVolume = param * 2;
729 				} else if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M)))
730 				{
731 					playState.m_nGlobalVolume = 256;
732 				}
733 				break;
734 			// Global Volume Slide
735 			case CMD_GLOBALVOLSLIDE:
736 				if(m_playBehaviour[kPerChannelGlobalVolSlide])
737 				{
738 					// IT compatibility 16. Global volume slide params are stored per channel (FT2/IT)
739 					if (param) chn.nOldGlobalVolSlide = param; else param = chn.nOldGlobalVolSlide;
740 				} else
741 				{
742 					if (param) playState.Chn[0].nOldGlobalVolSlide = param; else param = playState.Chn[0].nOldGlobalVolSlide;
743 				}
744 				if (((param & 0x0F) == 0x0F) && (param & 0xF0))
745 				{
746 					param >>= 4;
747 					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
748 					playState.m_nGlobalVolume += param << 1;
749 				} else if (((param & 0xF0) == 0xF0) && (param & 0x0F))
750 				{
751 					param = (param & 0x0F) << 1;
752 					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
753 					playState.m_nGlobalVolume -= param;
754 				} else if (param & 0xF0)
755 				{
756 					param >>= 4;
757 					param <<= 1;
758 					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
759 					playState.m_nGlobalVolume += param * nonRowTicks;
760 				} else
761 				{
762 					param = (param & 0x0F) << 1;
763 					if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param <<= 1;
764 					playState.m_nGlobalVolume -= param * nonRowTicks;
765 				}
766 				Limit(playState.m_nGlobalVolume, 0, 256);
767 				break;
768 			case CMD_CHANNELVOLUME:
769 				if (param <= 64) chn.nGlobalVol = param;
770 				break;
771 			case CMD_CHANNELVOLSLIDE:
772 				{
773 					if (param) chn.nOldChnVolSlide = param; else param = chn.nOldChnVolSlide;
774 					int32 volume = chn.nGlobalVol;
775 					if((param & 0x0F) == 0x0F && (param & 0xF0))
776 						volume += (param >> 4);		// Fine Up
777 					else if((param & 0xF0) == 0xF0 && (param & 0x0F))
778 						volume -= (param & 0x0F);	// Fine Down
779 					else if(param & 0x0F)			// Down
780 						volume -= (param & 0x0F) * nonRowTicks;
781 					else							// Up
782 						volume += ((param & 0xF0) >> 4) * nonRowTicks;
783 					Limit(volume, 0, 64);
784 					chn.nGlobalVol = volume;
785 				}
786 				break;
787 			case CMD_PANNING8:
788 				Panning(chn, param, Pan8bit);
789 				break;
790 			case CMD_MODCMDEX:
791 				if(param < 0x10)
792 				{
793 					// LED filter
794 					for(CHANNELINDEX channel = 0; channel < GetNumChannels(); channel++)
795 					{
796 						playState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1));
797 					}
798 				}
799 				[[fallthrough]];
800 			case CMD_S3MCMDEX:
801 				if((param & 0xF0) == 0x80)
802 				{
803 					Panning(chn, (param & 0x0F), Pan4bit);
804 				}
805 				break;
806 
807 			case CMD_VIBRATOVOL:
808 				if (param) chn.nOldVolumeSlide = param;
809 				param = 0;
810 				[[fallthrough]];
811 			case CMD_VIBRATO:
812 				Vibrato(chn, param);
813 				break;
814 			case CMD_FINEVIBRATO:
815 				FineVibrato(chn, param);
816 				break;
817 			case CMD_TREMOLO:
818 				Tremolo(chn, param);
819 				break;
820 			case CMD_PANBRELLO:
821 				Panbrello(chn, param);
822 				break;
823 			}
824 
825 			switch(chn.rowCommand.volcmd)
826 			{
827 			case VOLCMD_PANNING:
828 				Panning(chn, chn.rowCommand.vol, Pan6bit);
829 				break;
830 
831 			case VOLCMD_VIBRATOSPEED:
832 				// FT2 does not automatically enable vibrato with the "set vibrato speed" command
833 				if(m_playBehaviour[kFT2VolColVibrato])
834 					chn.nVibratoSpeed = chn.rowCommand.vol & 0x0F;
835 				else
836 					Vibrato(chn, chn.rowCommand.vol << 4);
837 				break;
838 			case VOLCMD_VIBRATODEPTH:
839 				Vibrato(chn, chn.rowCommand.vol);
840 				break;
841 			}
842 
843 			// Process vibrato / tremolo / panbrello
844 			switch(chn.rowCommand.command)
845 			{
846 			case CMD_VIBRATO:
847 			case CMD_FINEVIBRATO:
848 			case CMD_VIBRATOVOL:
849 				if(adjustMode & eAdjust)
850 				{
851 					uint32 vibTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks;
852 					uint32 inc = chn.nVibratoSpeed * vibTicks;
853 					if(m_playBehaviour[kITVibratoTremoloPanbrello])
854 						inc *= 4;
855 					chn.nVibratoPos += static_cast<uint8>(inc);
856 				}
857 				break;
858 
859 			case CMD_TREMOLO:
860 				if(adjustMode & eAdjust)
861 				{
862 					uint32 tremTicks = ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]) ? numTicks : nonRowTicks;
863 					uint32 inc = chn.nTremoloSpeed * tremTicks;
864 					if(m_playBehaviour[kITVibratoTremoloPanbrello])
865 						inc *= 4;
866 					chn.nTremoloPos += static_cast<uint8>(inc);
867 				}
868 				break;
869 
870 			case CMD_PANBRELLO:
871 				if(adjustMode & eAdjust)
872 				{
873 					// Panbrello effect is permanent in compatible mode, so actually apply panbrello for the last tick of this row
874 					chn.nPanbrelloPos += static_cast<uint8>(chn.nPanbrelloSpeed * (numTicks - 1));
875 					ProcessPanbrello(chn);
876 				}
877 				break;
878 			}
879 		}
880 
881 		// Interpret F00 effect in XM files as "stop song"
882 		if(GetType() == MOD_TYPE_XM && playState.m_nMusicSpeed == uint16_max)
883 		{
884 			break;
885 		}
886 
887 		playState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat;
888 		if(Patterns[playState.m_nPattern].GetOverrideSignature())
889 		{
890 			playState.m_nCurrentRowsPerBeat = Patterns[playState.m_nPattern].GetRowsPerBeat();
891 		}
892 
893 		const uint32 tickDuration = GetTickDuration(playState);
894 		const uint32 rowDuration = tickDuration * numTicks;
895 		memory.elapsedTime += static_cast<double>(rowDuration) / static_cast<double>(m_MixerSettings.gdwMixingFreq);
896 		playState.m_lTotalSampleCount += rowDuration;
897 
898 		if(adjustSamplePos)
899 		{
900 			// Super experimental and dirty sample seeking
901 			for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
902 			{
903 				if(memory.chnSettings[nChn].ticksToRender == GetLengthMemory::IGNORE_CHANNEL)
904 					continue;
905 
906 				ModChannel &chn = playState.Chn[nChn];
907 				const ModCommand &m = chn.rowCommand;
908 				if(!chn.nPeriod && m.IsEmpty())
909 					continue;
910 
911 				uint32 paramHi = m.param >> 4, paramLo = m.param & 0x0F;
912 				uint32 startTick = 0;
913 				bool porta = m.command == CMD_TONEPORTAMENTO || m.command == CMD_TONEPORTAVOL || m.volcmd == VOLCMD_TONEPORTAMENTO;
914 				bool stopNote = false;
915 
916 				if(m.instr) chn.prevNoteOffset = 0;
917 				if(m.IsNote())
918 				{
919 					if(porta && memory.chnSettings[nChn].incChanged)
920 					{
921 						// If there's a portamento, the current channel increment mustn't be 0 in NoteChange()
922 						chn.increment = GetChannelIncrement(chn, chn.nPeriod, 0).first;
923 					}
924 					int32 setPan = chn.nPan;
925 					chn.nNewNote = chn.nLastNote;
926 					if(chn.nNewIns != 0) InstrumentChange(chn, chn.nNewIns, porta);
927 					NoteChange(chn, m.note, porta);
928 					memory.chnSettings[nChn].incChanged = true;
929 
930 					if((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xD0 && paramLo < numTicks)
931 					{
932 						startTick = paramLo;
933 					} else if(m.command == CMD_DELAYCUT && paramHi < numTicks)
934 					{
935 						startTick = paramHi;
936 					}
937 					if(playState.m_nPatternDelay > 1 && startTick != 0 && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)))
938 					{
939 						startTick += (playState.m_nMusicSpeed + playState.m_nFrameDelay) * (playState.m_nPatternDelay - 1);
940 					}
941 					if(!porta) memory.chnSettings[nChn].ticksToRender = 0;
942 
943 					// Panning commands have to be re-applied after a note change with potential pan change.
944 					if(m.command == CMD_PANNING8
945 						|| ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && paramHi == 0x8)
946 						|| m.volcmd == VOLCMD_PANNING)
947 					{
948 						chn.nPan = setPan;
949 					}
950 				}
951 
952 				if(m.IsNote() || m_playBehaviour[kApplyOffsetWithoutNote])
953 				{
954 					if(m.command == CMD_OFFSET)
955 					{
956 						ProcessSampleOffset(chn, nChn, playState);
957 					} else if(m.command == CMD_OFFSETPERCENTAGE)
958 					{
959 						SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 256));
960 					} else if(m.command == CMD_REVERSEOFFSET && chn.pModSample != nullptr)
961 					{
962 						memory.RenderChannel(nChn, oldTickDuration);	// Re-sync what we've got so far
963 						ReverseSampleOffset(chn, m.param);
964 						startTick = playState.m_nMusicSpeed - 1;
965 					} else if(m.volcmd == VOLCMD_OFFSET)
966 					{
967 						if(chn.pModSample != nullptr && m.vol <= std::size(chn.pModSample->cues))
968 						{
969 							SmpLength offset;
970 							if(m.vol == 0)
971 								offset = chn.oldOffset;
972 							else
973 								offset = chn.oldOffset = chn.pModSample->cues[m.vol - 1];
974 							SampleOffset(chn, offset);
975 						}
976 					}
977 				}
978 
979 				if(m.note == NOTE_KEYOFF || m.note == NOTE_NOTECUT || (m.note == NOTE_FADE && GetNumInstruments())
980 					|| ((m.command == CMD_MODCMDEX || m.command == CMD_S3MCMDEX) && (m.param & 0xF0) == 0xC0 && paramLo < numTicks)
981 					|| (m.command == CMD_DELAYCUT && paramLo != 0 && startTick + paramLo < numTicks)
982 					|| m.command == CMD_KEYOFF)
983 				{
984 					stopNote = true;
985 				}
986 
987 				if(m.command == CMD_VOLUME)
988 				{
989 					chn.nVolume = m.param * 4;
990 				} else if(m.volcmd == VOLCMD_VOLUME)
991 				{
992 					chn.nVolume = m.vol * 4;
993 				}
994 
995 				if(chn.pModSample && !stopNote)
996 				{
997 					// Check if we don't want to emulate some effect and thus stop processing.
998 					if(m.command < MAX_EFFECTS)
999 					{
1000 						if(forbiddenCommands[m.command])
1001 						{
1002 							stopNote = true;
1003 						} else if(m.command == CMD_MODCMDEX)
1004 						{
1005 							// Special case: Slides using extended commands
1006 							switch(m.param & 0xF0)
1007 							{
1008 							case 0x10:
1009 							case 0x20:
1010 								stopNote = true;
1011 							}
1012 						}
1013 					}
1014 
1015 					if(m.volcmd < forbiddenVolCommands.size() && forbiddenVolCommands[m.volcmd])
1016 					{
1017 						stopNote = true;
1018 					}
1019 				}
1020 
1021 				if(stopNote)
1022 				{
1023 					chn.Stop();
1024 					memory.chnSettings[nChn].ticksToRender = 0;
1025 				} else
1026 				{
1027 					if(oldTickDuration != tickDuration && oldTickDuration != 0)
1028 					{
1029 						memory.RenderChannel(nChn, oldTickDuration);	// Re-sync what we've got so far
1030 					}
1031 
1032 					switch(m.command)
1033 					{
1034 					case CMD_TONEPORTAVOL:
1035 					case CMD_VOLUMESLIDE:
1036 					case CMD_VIBRATOVOL:
1037 						if(m.param || (GetType() != MOD_TYPE_MOD))
1038 						{
1039 							for(uint32 i = 0; i < numTicks; i++)
1040 							{
1041 								chn.isFirstTick = (i == 0);
1042 								VolumeSlide(chn, m.param);
1043 							}
1044 						}
1045 						break;
1046 
1047 					case CMD_MODCMDEX:
1048 						if((m.param & 0x0F) || (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)))
1049 						{
1050 							chn.isFirstTick = true;
1051 							switch(m.param & 0xF0)
1052 							{
1053 							case 0xA0: FineVolumeUp(chn, m.param & 0x0F, false); break;
1054 							case 0xB0: FineVolumeDown(chn, m.param & 0x0F, false); break;
1055 							}
1056 						}
1057 						break;
1058 
1059 					case CMD_S3MCMDEX:
1060 						if(m.param == 0x9E)
1061 						{
1062 							// Play forward
1063 							memory.RenderChannel(nChn, oldTickDuration);  // Re-sync what we've got so far
1064 							chn.dwFlags.reset(CHN_PINGPONGFLAG);
1065 						} else if(m.param == 0x9F)
1066 						{
1067 							// Reverse
1068 							memory.RenderChannel(nChn, oldTickDuration);  // Re-sync what we've got so far
1069 							chn.dwFlags.set(CHN_PINGPONGFLAG);
1070 							if(!chn.position.GetInt() && chn.nLength && (m.IsNote() || !chn.dwFlags[CHN_LOOP]))
1071 							{
1072 								chn.position.Set(chn.nLength - 1, SamplePosition::fractMax);
1073 							}
1074 						} else if((m.param & 0xF0) == 0x70)
1075 						{
1076 							if(m.param >= 0x73)
1077 								chn.InstrumentControl(m.param, *this);
1078 						}
1079 						break;
1080 
1081 					case CMD_FINETUNE:
1082 					case CMD_FINETUNE_SMOOTH:
1083 						memory.RenderChannel(nChn, oldTickDuration);  // Re-sync what we've got so far
1084 						SetFinetune(nChn, playState, false);  // TODO should render each tick individually for CMD_FINETUNE_SMOOTH for higher sync accuracy
1085 						break;
1086 					}
1087 					chn.isFirstTick = true;
1088 					switch(m.volcmd)
1089 					{
1090 					case VOLCMD_FINEVOLUP:		FineVolumeUp(chn, m.vol, m_playBehaviour[kITVolColMemory]); break;
1091 					case VOLCMD_FINEVOLDOWN:	FineVolumeDown(chn, m.vol, m_playBehaviour[kITVolColMemory]); break;
1092 					case VOLCMD_VOLSLIDEUP:
1093 					case VOLCMD_VOLSLIDEDOWN:
1094 						{
1095 							// IT Compatibility: Volume column volume slides have their own memory
1096 							// Test case: VolColMemory.it
1097 							ModCommand::VOL vol = m.vol;
1098 							if(vol == 0 && m_playBehaviour[kITVolColMemory])
1099 							{
1100 								vol = chn.nOldVolParam;
1101 								if(vol == 0)
1102 									break;
1103 							}
1104 							if(m.volcmd == VOLCMD_VOLSLIDEUP)
1105 								vol <<= 4;
1106 							for(uint32 i = 0; i < numTicks; i++)
1107 							{
1108 								chn.isFirstTick = (i == 0);
1109 								VolumeSlide(chn, vol);
1110 							}
1111 						}
1112 						break;
1113 					case VOLCMD_PLAYCONTROL:
1114 						if(m.vol <= 1)
1115 							chn.isPaused = (m.vol == 0);
1116 						break;
1117 					}
1118 
1119 					if(chn.isPaused)
1120 						continue;
1121 					if(porta)
1122 					{
1123 						// Portamento needs immediate syncing, as the pitch changes on each tick
1124 						uint32 portaTick = memory.chnSettings[nChn].ticksToRender + startTick + 1;
1125 						memory.chnSettings[nChn].ticksToRender += numTicks;
1126 						memory.RenderChannel(nChn, tickDuration, portaTick);
1127 					} else
1128 					{
1129 						memory.chnSettings[nChn].ticksToRender += (numTicks - startTick);
1130 					}
1131 				}
1132 			}
1133 		}
1134 		oldTickDuration = tickDuration;
1135 
1136 		breakToRow = HandleNextRow(playState, orderList, false);
1137 	}
1138 
1139 	// Now advance the sample positions for sample seeking on channels that are still playing
1140 	if(adjustSamplePos)
1141 	{
1142 		for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
1143 		{
1144 			if(memory.chnSettings[nChn].ticksToRender != GetLengthMemory::IGNORE_CHANNEL)
1145 			{
1146 				memory.RenderChannel(nChn, oldTickDuration);
1147 			}
1148 		}
1149 	}
1150 
1151 	if(retval.targetReached)
1152 	{
1153 		retval.lastOrder = playState.m_nCurrentOrder;
1154 		retval.lastRow = playState.m_nRow;
1155 	}
1156 	retval.duration = memory.elapsedTime;
1157 	results.push_back(retval);
1158 
1159 	// Store final variables
1160 	if(adjustMode & eAdjust)
1161 	{
1162 		if(retval.targetReached || target.mode == GetLengthTarget::NoTarget)
1163 		{
1164 			// Target found, or there is no target (i.e. play whole song)...
1165 			m_PlayState = std::move(playState);
1166 			m_PlayState.ResetGlobalVolumeRamping();
1167 			m_PlayState.m_nNextRow = m_PlayState.m_nRow;
1168 			m_PlayState.m_nFrameDelay = m_PlayState.m_nPatternDelay = 0;
1169 			m_PlayState.m_nTickCount = TICKS_ROW_FINISHED;
1170 			m_PlayState.m_bPositionChanged = true;
1171 			if(m_opl != nullptr)
1172 				m_opl->Reset();
1173 			for(CHANNELINDEX n = 0; n < GetNumChannels(); n++)
1174 			{
1175 				auto &chn = m_PlayState.Chn[n];
1176 				if(chn.nLastNote != NOTE_NONE)
1177 				{
1178 					chn.nNewNote = chn.nLastNote;
1179 				}
1180 				if(memory.chnSettings[n].vol != 0xFF && !adjustSamplePos)
1181 				{
1182 					chn.nVolume = std::min(memory.chnSettings[n].vol, uint8(64)) * 4;
1183 				}
1184 				if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl)
1185 				{
1186 					m_opl->Patch(n, chn.pModSample->adlib);
1187 					m_opl->NoteCut(n);
1188 				}
1189 				chn.pCurrentSample = nullptr;
1190 			}
1191 
1192 #ifndef NO_PLUGINS
1193 			// If there were any PC events, update plugin parameters to their latest value.
1194 			std::bitset<MAX_MIXPLUGINS> plugSetProgram;
1195 			for(const auto &param : memory.plugParams)
1196 			{
1197 				PLUGINDEX plug = param.first.first - 1;
1198 				IMixPlugin *plugin = m_MixPlugins[plug].pMixPlugin;
1199 				if(plugin != nullptr)
1200 				{
1201 					if(!plugSetProgram[plug])
1202 					{
1203 						// Used for bridged plugins to avoid sending out individual messages for each parameter.
1204 						plugSetProgram.set(plug);
1205 						plugin->BeginSetProgram();
1206 					}
1207 					plugin->SetParameter(param.first.second, param.second / PlugParamValue(ModCommand::maxColumnValue));
1208 				}
1209 			}
1210 			if(plugSetProgram.any())
1211 			{
1212 				for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
1213 				{
1214 					if(plugSetProgram[i])
1215 					{
1216 						m_MixPlugins[i].pMixPlugin->EndSetProgram();
1217 					}
1218 				}
1219 			}
1220 #endif // NO_PLUGINS
1221 		} else if(adjustMode != eAdjustOnSuccess)
1222 		{
1223 			// Target not found (e.g. when jumping to a hidden sub song), reset global variables...
1224 			m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
1225 			m_PlayState.m_nMusicTempo = m_nDefaultTempo;
1226 			m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume;
1227 		}
1228 		// When adjusting the playback status, we will also want to update the visited rows vector according to the current position.
1229 		if(sequence != Order.GetCurrentSequenceIndex())
1230 		{
1231 			Order.SetSequence(sequence);
1232 		}
1233 	}
1234 	if(adjustMode & (eAdjust | eAdjustOnlyVisitedRows))
1235 		m_visitedRows.MoveVisitedRowsFrom(visitedRows);
1236 
1237 	return results;
1238 }
1239 
1240 
1241 //////////////////////////////////////////////////////////////////////////////////////////////////
1242 // Effects
1243 
1244 // Change sample or instrument number.
InstrumentChange(ModChannel & chn,uint32 instr,bool bPorta,bool bUpdVol,bool bResetEnv) const1245 void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bool bUpdVol, bool bResetEnv) const
1246 {
1247 	const ModInstrument *pIns = instr <= GetNumInstruments() ? Instruments[instr] : nullptr;
1248 	const ModSample *pSmp = &Samples[instr];
1249 	const auto oldInsVol = chn.nInsVol;
1250 	ModCommand::NOTE note = chn.nNewNote;
1251 
1252 	if(note == NOTE_NONE && m_playBehaviour[kITInstrWithoutNote]) return;
1253 
1254 	if(pIns != nullptr && ModCommand::IsNote(note))
1255 	{
1256 		// Impulse Tracker ignores empty slots.
1257 		// We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended.
1258 		// Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it
1259 		if(pIns->Keyboard[note - NOTE_MIN] == 0 && m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel())
1260 		{
1261 			chn.pModInstrument = pIns;
1262 			return;
1263 		}
1264 
1265 		if(pIns->NoteMap[note - NOTE_MIN] > NOTE_MAX) return;
1266 		uint32 n = pIns->Keyboard[note - NOTE_MIN];
1267 		pSmp = ((n) && (n < MAX_SAMPLES)) ? &Samples[n] : nullptr;
1268 	} else if(GetNumInstruments())
1269 	{
1270 		// No valid instrument, or not a valid note.
1271 		if (note >= NOTE_MIN_SPECIAL) return;
1272 		if(m_playBehaviour[kITEmptyNoteMapSlot] && (pIns == nullptr || !pIns->HasValidMIDIChannel()))
1273 		{
1274 			// Impulse Tracker ignores empty slots.
1275 			// We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended.
1276 			// Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it
1277 			chn.pModInstrument = nullptr;
1278 			chn.nNewIns = 0;
1279 			return;
1280 		}
1281 		pSmp = nullptr;
1282 	}
1283 
1284 	bool returnAfterVolumeAdjust = false;
1285 
1286 	// instrumentChanged is used for IT carry-on env option
1287 	bool instrumentChanged = (pIns != chn.pModInstrument);
1288 	const bool sampleChanged = (chn.pModSample != nullptr) && (pSmp != chn.pModSample);
1289 	const bool newTuning = (GetType() == MOD_TYPE_MPT && pIns && pIns->pTuning);
1290 
1291 	if(!bPorta || instrumentChanged || sampleChanged)
1292 		chn.microTuning = 0;
1293 
1294 	// Playback behavior change for MPT: With portamento don't change sample if it is in
1295 	// the same instrument as previous sample.
1296 	if(bPorta && newTuning && pIns == chn.pModInstrument && sampleChanged)
1297 		return;
1298 
1299 	if(sampleChanged && bPorta)
1300 	{
1301 		// IT compatibility: No sample change (also within multi-sample instruments) during portamento when using Compatible Gxx.
1302 		// Test case: PortaInsNumCompat.it, PortaSampleCompat.it, PortaCutCompat.it
1303 		if(m_playBehaviour[kITPortamentoInstrument] && m_SongFlags[SONG_ITCOMPATGXX] && !chn.increment.IsZero())
1304 		{
1305 			pSmp = chn.pModSample;
1306 		}
1307 
1308 		// Special XM hack (also applies to MOD / S3M, except when playing IT-style S3Ms, such as k_vision.s3m)
1309 		// Test case: PortaSmpChange.mod, PortaSmpChange.s3m, PortaSwap.s3m
1310 		if((!instrumentChanged && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && pIns)
1311 			|| (GetType() == MOD_TYPE_PLM)
1312 			|| (GetType() == MOD_TYPE_MOD && chn.IsSamplePlaying())
1313 			|| (m_playBehaviour[kST3PortaSampleChange] && chn.IsSamplePlaying()))
1314 		{
1315 			// FT2 doesn't change the sample in this case,
1316 			// but still uses the sample info from the old one (bug?)
1317 			returnAfterVolumeAdjust = true;
1318 		}
1319 	}
1320 	// IT compatibility: A lone instrument number should only reset sample properties to those of the corresponding sample in instrument mode.
1321 	// C#5 01 ... <-- sample 1
1322 	// C-5 .. g02 <-- sample 2
1323 	// ... 01 ... <-- still sample 1, but with properties of sample 2
1324 	// In the above example, no sample change happens on the second row. In the third row, sample 1 keeps playing but with the
1325 	// volume and panning properties of sample 2.
1326 	// Test case: InstrAfterMultisamplePorta.it
1327 	if(m_nInstruments && !instrumentChanged && sampleChanged && chn.pCurrentSample != nullptr && m_playBehaviour[kITMultiSampleInstrumentNumber] && !chn.rowCommand.IsNote())
1328 	{
1329 		returnAfterVolumeAdjust = true;
1330 	}
1331 
1332 	// IT Compatibility: Envelope pickup after SCx cut (but don't do this when working with plugins, or else envelope carry stops working)
1333 	// Test case: cut-carry.it
1334 	if(!chn.IsSamplePlaying() && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (!pIns || !pIns->HasValidMIDIChannel()))
1335 	{
1336 		instrumentChanged = true;
1337 	}
1338 
1339 	// FT2 compatibility: new instrument + portamento = ignore new instrument number, but reload old instrument settings (the world of XM is upside down...)
1340 	// And this does *not* happen if volume column portamento is used together with note delay... (handled in ProcessEffects(), where all the other note delay stuff is.)
1341 	// Test case: porta-delay.xm, SamplePortaInInstrument.xm
1342 	if((instrumentChanged || sampleChanged) && bPorta && m_playBehaviour[kFT2PortaIgnoreInstr] && (chn.pModInstrument != nullptr || chn.pModSample != nullptr))
1343 	{
1344 		pIns = chn.pModInstrument;
1345 		pSmp = chn.pModSample;
1346 		instrumentChanged = false;
1347 	} else
1348 	{
1349 		chn.pModInstrument = pIns;
1350 	}
1351 
1352 	// Update Volume
1353 	if (bUpdVol && (!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M)) || ((pSmp != nullptr && pSmp->HasSampleData()) || chn.HasMIDIOutput())))
1354 	{
1355 		if(pSmp)
1356 		{
1357 			if(!pSmp->uFlags[SMP_NODEFAULTVOLUME])
1358 				chn.nVolume = pSmp->nVolume;
1359 		} else if(pIns && pIns->nMixPlug)
1360 		{
1361 			chn.nVolume = chn.GetVSTVolume();
1362 		} else
1363 		{
1364 			chn.nVolume = 0;
1365 		}
1366 	}
1367 
1368 	if(returnAfterVolumeAdjust && sampleChanged && pSmp != nullptr)
1369 	{
1370 		// ProTracker applies new instrument's finetune but keeps the old sample playing.
1371 		// Test case: PortaSwapPT.mod
1372 		if(m_playBehaviour[kMODSampleSwap])
1373 			chn.nFineTune = pSmp->nFineTune;
1374 		// ST3 does it similarly for middle-C speed.
1375 		// Test case: PortaSwap.s3m, SampleSwap.s3m
1376 		if(GetType() == MOD_TYPE_S3M && pSmp->HasSampleData())
1377 			chn.nC5Speed = pSmp->nC5Speed;
1378 	}
1379 
1380 	if(returnAfterVolumeAdjust) return;
1381 
1382 	// Instrument adjust
1383 	chn.nNewIns = 0;
1384 
1385 	// IT Compatiblity: NNA is reset on every note change, not every instrument change (fixes s7xinsnum.it).
1386 	if (pIns && ((!m_playBehaviour[kITNNAReset] && pSmp) || pIns->nMixPlug || instrumentChanged))
1387 		chn.nNNA = pIns->nNNA;
1388 
1389 	// Update volume
1390 	chn.UpdateInstrumentVolume(pSmp, pIns);
1391 
1392 	// Update panning
1393 	// FT2 compatibility: Only reset panning on instrument numbers, not notes (bUpdVol condition)
1394 	// Test case: PanMemory.xm
1395 	// IT compatibility: Sample and instrument panning is only applied on note change, not instrument change
1396 	// Test case: PanReset.it
1397 	if((bUpdVol || !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) && !m_playBehaviour[kITPanningReset])
1398 	{
1399 		ApplyInstrumentPanning(chn, pIns, pSmp);
1400 	}
1401 
1402 	// Reset envelopes
1403 	if(bResetEnv)
1404 	{
1405 		// Blurb by Storlek (from the SchismTracker code):
1406 		// Conditions experimentally determined to cause envelope reset in Impulse Tracker:
1407 		// - no note currently playing (of course)
1408 		// - note given, no portamento
1409 		// - instrument number given, portamento, compat gxx enabled
1410 		// - instrument number given, no portamento, after keyoff, old effects enabled
1411 		// If someone can enlighten me to what the logic really is here, I'd appreciate it.
1412 		// Seems like it's just a total mess though, probably to get XMs to play right.
1413 
1414 		bool reset, resetAlways;
1415 
1416 		// IT Compatibility: Envelope reset
1417 		// Test case: EnvReset.it
1418 		if(m_playBehaviour[kITEnvelopeReset])
1419 		{
1420 			const bool insNumber = (instr != 0);
1421 			reset = (!chn.nLength
1422 				|| (insNumber && bPorta && m_SongFlags[SONG_ITCOMPATGXX])
1423 				|| (insNumber && !bPorta && chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] && m_SongFlags[SONG_ITOLDEFFECTS]));
1424 			// NOTE: IT2.14 with SB/GUS/etc. output is different. We are going after IT's WAV writer here.
1425 			// For SB/GUS/etc. emulation, envelope carry should only apply when the NNA isn't set to "Note Cut".
1426 			// Test case: CarryNNA.it
1427 			resetAlways = (!chn.nFadeOutVol || instrumentChanged || chn.dwFlags[CHN_KEYOFF]);
1428 		} else
1429 		{
1430 			reset = (!bPorta || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) || m_SongFlags[SONG_ITCOMPATGXX]
1431 				|| !chn.nLength || (chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol));
1432 			resetAlways = !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) || instrumentChanged || pIns == nullptr || chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE];
1433 		}
1434 
1435 		if(reset)
1436 		{
1437 			chn.dwFlags.set(CHN_FASTVOLRAMP);
1438 			if(pIns != nullptr)
1439 			{
1440 				if(resetAlways)
1441 				{
1442 					chn.ResetEnvelopes();
1443 				} else
1444 				{
1445 					if(!pIns->VolEnv.dwFlags[ENV_CARRY]) chn.VolEnv.Reset();
1446 					if(!pIns->PanEnv.dwFlags[ENV_CARRY]) chn.PanEnv.Reset();
1447 					if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) chn.PitchEnv.Reset();
1448 				}
1449 			}
1450 
1451 			// IT Compatibility: Autovibrato reset
1452 			if(!m_playBehaviour[kITVibratoTremoloPanbrello])
1453 			{
1454 				chn.nAutoVibDepth = 0;
1455 				chn.nAutoVibPos = 0;
1456 			}
1457 		} else if(pIns != nullptr && !pIns->VolEnv.dwFlags[ENV_ENABLED])
1458 		{
1459 			if(m_playBehaviour[kITPortamentoInstrument])
1460 			{
1461 				chn.VolEnv.Reset();
1462 			} else
1463 			{
1464 				chn.ResetEnvelopes();
1465 			}
1466 		}
1467 	}
1468 	// Invalid sample ?
1469 	if(pSmp == nullptr && (pIns == nullptr || !pIns->HasValidMIDIChannel()))
1470 	{
1471 		chn.pModSample = nullptr;
1472 		chn.nInsVol = 0;
1473 		return;
1474 	}
1475 
1476 	// Tone-Portamento doesn't reset the pingpong direction flag
1477 	if(bPorta && pSmp == chn.pModSample && pSmp != nullptr)
1478 	{
1479 		// If channel length is 0, we cut a previous sample using SCx. In that case, we have to update sample length, loop points, etc...
1480 		if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && chn.nLength != 0)
1481 			return;
1482 		// FT2 compatibility: Do not reset key-off status on portamento without instrument number
1483 		// Test case: Off-Porta.xm
1484 		if(GetType() != MOD_TYPE_XM || !m_playBehaviour[kITFT2DontResetNoteOffOnPorta] || chn.rowCommand.instr != 0)
1485 			chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE);
1486 		chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG));
1487 	} else //if(!instrumentChanged || chn.rowCommand.instr != 0 || !IsCompatibleMode(TRK_FASTTRACKER2))	// SampleChange.xm?
1488 	{
1489 		chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE);
1490 
1491 		// IT compatibility: Don't change bidi loop direction when no sample nor instrument is changed.
1492 		if((m_playBehaviour[kITPingPongNoReset] || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) && pSmp == chn.pModSample && !instrumentChanged)
1493 			chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG));
1494 		else
1495 			chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS);
1496 
1497 		if(pIns)
1498 		{
1499 			// Copy envelope flags (we actually only need the "enabled" and "pitch" flag)
1500 			chn.VolEnv.flags = pIns->VolEnv.dwFlags;
1501 			chn.PanEnv.flags = pIns->PanEnv.dwFlags;
1502 			chn.PitchEnv.flags = pIns->PitchEnv.dwFlags;
1503 
1504 			// A cutoff frequency of 0 should not be reset just because the filter envelope is enabled.
1505 			// Test case: FilterEnvReset.it
1506 			if((pIns->PitchEnv.dwFlags & (ENV_ENABLED | ENV_FILTER)) == (ENV_ENABLED | ENV_FILTER) && !m_playBehaviour[kITFilterBehaviour])
1507 			{
1508 				if(!chn.nCutOff) chn.nCutOff = 0x7F;
1509 			}
1510 
1511 			if(pIns->IsCutoffEnabled()) chn.nCutOff = pIns->GetCutoff();
1512 			if(pIns->IsResonanceEnabled()) chn.nResonance = pIns->GetResonance();
1513 		}
1514 	}
1515 
1516 	if(pSmp == nullptr)
1517 	{
1518 		chn.pModSample = nullptr;
1519 		chn.nLength = 0;
1520 		return;
1521 	}
1522 
1523 	if(bPorta && chn.nLength == 0 && (m_playBehaviour[kFT2PortaNoNote] || m_playBehaviour[kITPortaNoNote]))
1524 	{
1525 		// IT/FT2 compatibility: If the note just stopped on the previous tick, prevent it from restarting.
1526 		// Test cases: PortaJustStoppedNote.xm, PortaJustStoppedNote.it
1527 		chn.increment.Set(0);
1528 	}
1529 
1530 	// IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes.
1531 	// If the instrument changes, keep playing the previous sample, but load the new instrument's envelopes.
1532 	// Test case: ResetEnvNoteOffOldFx.it
1533 	if(chn.rowCommand.note == NOTE_KEYOFF && m_playBehaviour[kITInstrWithNoteOffOldEffects] && m_SongFlags[SONG_ITOLDEFFECTS] && sampleChanged)
1534 	{
1535 		if(chn.pModSample)
1536 		{
1537 			chn.dwFlags |= (chn.pModSample->uFlags & CHN_SAMPLEFLAGS);
1538 		}
1539 		chn.nInsVol = oldInsVol;
1540 		chn.nVolume = pSmp->nVolume;
1541 		if(pSmp->uFlags[CHN_PANNING]) chn.SetInstrumentPan(pSmp->nPan, *this);
1542 		return;
1543 	}
1544 
1545 	chn.pModSample = pSmp;
1546 	chn.nLength = pSmp->nLength;
1547 	chn.nLoopStart = pSmp->nLoopStart;
1548 	chn.nLoopEnd = pSmp->nLoopEnd;
1549 	// ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end)
1550 	if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) chn.nLoopEnd = pSmp->nLength;
1551 	chn.dwFlags |= (pSmp->uFlags & CHN_SAMPLEFLAGS);
1552 
1553 	// IT Compatibility: Autovibrato reset
1554 	if(m_playBehaviour[kITVibratoTremoloPanbrello])
1555 	{
1556 		chn.nAutoVibDepth = 0;
1557 		chn.nAutoVibPos = 0;
1558 	}
1559 
1560 	if(newTuning)
1561 	{
1562 		chn.nC5Speed = pSmp->nC5Speed;
1563 		chn.m_CalculateFreq = true;
1564 		chn.nFineTune = 0;
1565 	} else if(!bPorta || sampleChanged || !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM)))
1566 	{
1567 		// Don't reset finetune changed by "set finetune" command.
1568 		// Test case: finetune.xm, finetune.mod
1569 		// But *do* change the finetune if we switch to a different sample, to fix
1570 		// Miranda`s axe by Jamson (jam007.xm).
1571 		chn.nC5Speed = pSmp->nC5Speed;
1572 		chn.nFineTune = pSmp->nFineTune;
1573 	}
1574 
1575 	chn.nTranspose = UseFinetuneAndTranspose() ? pSmp->RelativeTone : 0;
1576 
1577 	// FT2 compatibility: Don't reset portamento target with new instrument numbers.
1578 	// Test case: Porta-Pickup.xm
1579 	// ProTracker does the same.
1580 	// Test case: PortaTarget.mod
1581 	if(!m_playBehaviour[kFT2PortaTargetNoReset] && GetType() != MOD_TYPE_MOD)
1582 	{
1583 		chn.nPortamentoDest = 0;
1584 	}
1585 	chn.m_PortamentoFineSteps = 0;
1586 
1587 	if(chn.dwFlags[CHN_SUSTAINLOOP])
1588 	{
1589 		chn.nLoopStart = pSmp->nSustainStart;
1590 		chn.nLoopEnd = pSmp->nSustainEnd;
1591 		if(chn.dwFlags[CHN_PINGPONGSUSTAIN]) chn.dwFlags.set(CHN_PINGPONGLOOP);
1592 		chn.dwFlags.set(CHN_LOOP);
1593 	}
1594 	if(chn.dwFlags[CHN_LOOP] && chn.nLoopEnd < chn.nLength) chn.nLength = chn.nLoopEnd;
1595 
1596 	// Fix sample position on instrument change. This is needed for IT "on the fly" sample change.
1597 	// XXX is this actually called? In ProcessEffects(), a note-on effect is emulated if there's an on the fly sample change!
1598 	if(chn.position.GetUInt() >= chn.nLength)
1599 	{
1600 		if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))
1601 		{
1602 			chn.position.Set(0);
1603 		}
1604 	}
1605 }
1606 
1607 
NoteChange(ModChannel & chn,int note,bool bPorta,bool bResetEnv,bool bManual,CHANNELINDEX channelHint) const1608 void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetEnv, bool bManual, CHANNELINDEX channelHint) const
1609 {
1610 	if(note < NOTE_MIN)
1611 		return;
1612 	const int origNote = note;
1613 	const ModSample *pSmp = chn.pModSample;
1614 	const ModInstrument *pIns = chn.pModInstrument;
1615 
1616 	const bool newTuning = (GetType() == MOD_TYPE_MPT && pIns != nullptr && pIns->pTuning);
1617 	// save the note that's actually used, as it's necessary to properly calculate PPS and stuff
1618 	const int realnote = note;
1619 
1620 	if((pIns) && (note - NOTE_MIN < (int)std::size(pIns->Keyboard)))
1621 	{
1622 		uint32 n = pIns->Keyboard[note - NOTE_MIN];
1623 		if((n) && (n < MAX_SAMPLES))
1624 		{
1625 			pSmp = &Samples[n];
1626 		} else if(m_playBehaviour[kITEmptyNoteMapSlot] && !chn.HasMIDIOutput())
1627 		{
1628 			// Impulse Tracker ignores empty slots.
1629 			// We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended.
1630 			// Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it
1631 			return;
1632 		}
1633 		note = pIns->NoteMap[note - NOTE_MIN];
1634 	}
1635 	// Key Off
1636 	if(note > NOTE_MAX)
1637 	{
1638 		// Key Off (+ Invalid Note for XM - TODO is this correct?)
1639 		if(note == NOTE_KEYOFF || !(GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT)))
1640 		{
1641 			KeyOff(chn);
1642 			// IT compatibility: Note-off + instrument releases sample sustain but does not release envelopes or fade the instrument
1643 			// Test case: noteoff3.it, ResetEnvNoteOffOldFx2.it
1644 			if(!bPorta && m_playBehaviour[kITInstrWithNoteOffOldEffects] && m_SongFlags[SONG_ITOLDEFFECTS] && chn.rowCommand.instr)
1645 				chn.dwFlags.reset(CHN_NOTEFADE | CHN_KEYOFF);
1646 		} else // Invalid Note -> Note Fade
1647 		{
1648 			if(/*note == NOTE_FADE && */ GetNumInstruments())
1649 				chn.dwFlags.set(CHN_NOTEFADE);
1650 		}
1651 
1652 		// Note Cut
1653 		if (note == NOTE_NOTECUT)
1654 		{
1655 			if(chn.dwFlags[CHN_ADLIB] && GetType() == MOD_TYPE_S3M)
1656 			{
1657 				// OPL voices are not cut but enter the release portion of their envelope
1658 				// In S3M we can still modify the volume after note-off, in legacy MPTM mode we can't
1659 				chn.dwFlags.set(CHN_KEYOFF);
1660 			} else
1661 			{
1662 				chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP);
1663 				// IT compatibility: Stopping sample playback by setting sample increment to 0 rather than volume
1664 				// Test case: NoteOffInstr.it
1665 				if ((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) || (m_nInstruments != 0 && !m_playBehaviour[kITInstrWithNoteOff])) chn.nVolume = 0;
1666 				if (m_playBehaviour[kITInstrWithNoteOff]) chn.increment.Set(0);
1667 				chn.nFadeOutVol = 0;
1668 			}
1669 		}
1670 
1671 		// IT compatibility tentative fix: Clear channel note memory (TRANCE_N.IT by A3F).
1672 		if(m_playBehaviour[kITClearOldNoteAfterCut])
1673 		{
1674 			chn.nNote = chn.nNewNote = NOTE_NONE;
1675 		}
1676 		return;
1677 	}
1678 
1679 	if(newTuning)
1680 	{
1681 		if(!bPorta || chn.nNote == NOTE_NONE)
1682 			chn.nPortamentoDest = 0;
1683 		else
1684 		{
1685 			chn.nPortamentoDest = pIns->pTuning->GetStepDistance(chn.nNote, chn.m_PortamentoFineSteps, static_cast<Tuning::NOTEINDEXTYPE>(note), 0);
1686 			//Here chn.nPortamentoDest means 'steps to slide'.
1687 			chn.m_PortamentoFineSteps = -chn.nPortamentoDest;
1688 		}
1689 	}
1690 
1691 	if(!bPorta && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MED | MOD_TYPE_MT2)))
1692 	{
1693 		if(pSmp)
1694 		{
1695 			chn.nTranspose = pSmp->RelativeTone;
1696 			chn.nFineTune = pSmp->nFineTune;
1697 		}
1698 	}
1699 	// IT Compatibility: Update multisample instruments frequency even if instrument is not specified (fixes the guitars in spx-shuttledeparture.it)
1700 	// Test case: freqreset-noins.it
1701 	if(!bPorta && pSmp && m_playBehaviour[kITMultiSampleBehaviour])
1702 		chn.nC5Speed = pSmp->nC5Speed;
1703 
1704 	if(bPorta && !chn.IsSamplePlaying())
1705 	{
1706 		if(m_playBehaviour[kFT2PortaNoNote])
1707 		{
1708 			// FT2 Compatibility: Ignore notes with portamento if there was no note playing.
1709 			// Test case: 3xx-no-old-samp.xm
1710 			chn.nPeriod = 0;
1711 			return;
1712 		} else if(m_playBehaviour[kITPortaNoNote])
1713 		{
1714 			// IT Compatibility: Ignore portamento command if no note was playing (e.g. if a previous note has faded out).
1715 			// Test case: Fade-Porta.it
1716 			bPorta = false;
1717 		}
1718 	}
1719 
1720 	if(UseFinetuneAndTranspose())
1721 	{
1722 		note += chn.nTranspose;
1723 		// RealNote = PatternNote + RelativeTone; (0..118, 0 = C-0, 118 = A#9)
1724 		Limit(note, NOTE_MIN + 11, NOTE_MIN + 130);	// 119 possible notes
1725 	} else
1726 	{
1727 		Limit(note, NOTE_MIN, NOTE_MAX);
1728 	}
1729 	if(m_playBehaviour[kITRealNoteMapping])
1730 	{
1731 		// need to memorize the original note for various effects (e.g. PPS)
1732 		chn.nNote = static_cast<ModCommand::NOTE>(Clamp(realnote, NOTE_MIN, NOTE_MAX));
1733 	} else
1734 	{
1735 		chn.nNote = static_cast<ModCommand::NOTE>(note);
1736 	}
1737 	chn.m_CalculateFreq = true;
1738 	chn.isPaused = false;
1739 
1740 	if ((!bPorta) || (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)))
1741 		chn.nNewIns = 0;
1742 
1743 	uint32 period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed);
1744 	chn.nPanbrelloOffset = 0;
1745 
1746 	// IT compatibility: Sample and instrument panning is only applied on note change, not instrument change
1747 	// Test case: PanReset.it
1748 	if(m_playBehaviour[kITPanningReset])
1749 		ApplyInstrumentPanning(chn, pIns, pSmp);
1750 
1751 	// IT compatibility: Pitch/Pan Separation can be overriden by panning commands, and shouldn't be affected by note-off commands
1752 	// Test case: PitchPanReset.it
1753 	if(m_playBehaviour[kITPitchPanSeparation] && pIns && pIns->nPPS)
1754 	{
1755 		if(!chn.nRestorePanOnNewNote)
1756 			chn.nRestorePanOnNewNote = static_cast<uint16>(chn.nPan + 1);
1757 		ProcessPitchPanSeparation(chn.nPan, origNote, *pIns);
1758 	}
1759 
1760 	if(bResetEnv && !bPorta)
1761 	{
1762 		chn.nVolSwing = chn.nPanSwing = 0;
1763 		chn.nResSwing = chn.nCutSwing = 0;
1764 		if(pIns)
1765 		{
1766 			// IT Compatiblity: NNA is reset on every note change, not every instrument change (fixes spx-farspacedance.it).
1767 			if(m_playBehaviour[kITNNAReset]) chn.nNNA = pIns->nNNA;
1768 
1769 			if(!pIns->VolEnv.dwFlags[ENV_CARRY]) chn.VolEnv.Reset();
1770 			if(!pIns->PanEnv.dwFlags[ENV_CARRY]) chn.PanEnv.Reset();
1771 			if(!pIns->PitchEnv.dwFlags[ENV_CARRY]) chn.PitchEnv.Reset();
1772 
1773 			// Volume Swing
1774 			if(pIns->nVolSwing)
1775 			{
1776 				chn.nVolSwing = static_cast<int16>(((mpt::random<int8>(AccessPRNG()) * pIns->nVolSwing) / 64 + 1) * (m_playBehaviour[kITSwingBehaviour] ? chn.nInsVol : ((chn.nVolume + 1) / 2)) / 199);
1777 			}
1778 			// Pan Swing
1779 			if(pIns->nPanSwing)
1780 			{
1781 				chn.nPanSwing = static_cast<int16>(((mpt::random<int8>(AccessPRNG()) * pIns->nPanSwing * 4) / 128));
1782 				if(!m_playBehaviour[kITSwingBehaviour] && chn.nRestorePanOnNewNote == 0)
1783 				{
1784 					chn.nRestorePanOnNewNote = static_cast<uint16>(chn.nPan + 1);
1785 				}
1786 			}
1787 			// Cutoff Swing
1788 			if(pIns->nCutSwing)
1789 			{
1790 				int32 d = ((int32)pIns->nCutSwing * (int32)(static_cast<int32>(mpt::random<int8>(AccessPRNG())) + 1)) / 128;
1791 				chn.nCutSwing = static_cast<int16>((d * chn.nCutOff + 1) / 128);
1792 				chn.nRestoreCutoffOnNewNote = chn.nCutOff + 1;
1793 			}
1794 			// Resonance Swing
1795 			if(pIns->nResSwing)
1796 			{
1797 				int32 d = ((int32)pIns->nResSwing * (int32)(static_cast<int32>(mpt::random<int8>(AccessPRNG())) + 1)) / 128;
1798 				chn.nResSwing = static_cast<int16>((d * chn.nResonance + 1) / 128);
1799 				chn.nRestoreResonanceOnNewNote = chn.nResonance + 1;
1800 			}
1801 		}
1802 	}
1803 
1804 	if(!pSmp) return;
1805 	if(period)
1806 	{
1807 		if((!bPorta) || (!chn.nPeriod)) chn.nPeriod = period;
1808 		if(!newTuning)
1809 		{
1810 			// FT2 compatibility: Don't reset portamento target with new notes.
1811 			// Test case: Porta-Pickup.xm
1812 			// ProTracker does the same.
1813 			// Test case: PortaTarget.mod
1814 			// IT compatibility: Portamento target is completely cleared with new notes.
1815 			// Test case: PortaReset.it
1816 			if(bPorta || !(m_playBehaviour[kFT2PortaTargetNoReset] || m_playBehaviour[kITClearPortaTarget] || GetType() == MOD_TYPE_MOD))
1817 			{
1818 				chn.nPortamentoDest = period;
1819 				chn.portaTargetReached = false;
1820 			}
1821 		}
1822 
1823 		if(!bPorta || (!chn.nLength && !(GetType() & MOD_TYPE_S3M)))
1824 		{
1825 			chn.pModSample = pSmp;
1826 			chn.nLength = pSmp->nLength;
1827 			chn.nLoopEnd = pSmp->nLength;
1828 			chn.nLoopStart = 0;
1829 			chn.position.Set(0);
1830 			if((m_SongFlags[SONG_PT_MODE] || m_playBehaviour[kST3OffsetWithoutInstrument]) && !chn.rowCommand.instr)
1831 			{
1832 				chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - SmpLength(1)));
1833 			} else
1834 			{
1835 				chn.prevNoteOffset = 0;
1836 			}
1837 			chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | (pSmp->uFlags & CHN_SAMPLEFLAGS);
1838 			chn.dwFlags.reset(CHN_PORTAMENTO);
1839 			if(chn.dwFlags[CHN_SUSTAINLOOP])
1840 			{
1841 				chn.nLoopStart = pSmp->nSustainStart;
1842 				chn.nLoopEnd = pSmp->nSustainEnd;
1843 				chn.dwFlags.set(CHN_PINGPONGLOOP, chn.dwFlags[CHN_PINGPONGSUSTAIN]);
1844 				chn.dwFlags.set(CHN_LOOP);
1845 				if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd;
1846 			} else if(chn.dwFlags[CHN_LOOP])
1847 			{
1848 				chn.nLoopStart = pSmp->nLoopStart;
1849 				chn.nLoopEnd = pSmp->nLoopEnd;
1850 				if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd;
1851 			}
1852 			// ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end)
1853 			if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) chn.nLoopEnd = chn.nLength = pSmp->nLength;
1854 
1855 			if(chn.dwFlags[CHN_REVERSE] && chn.nLength > 0)
1856 			{
1857 				chn.dwFlags.set(CHN_PINGPONGFLAG);
1858 				chn.position.SetInt(chn.nLength - 1);
1859 			}
1860 
1861 			// Handle "retrigger" waveform type
1862 			if(chn.nVibratoType < 4)
1863 			{
1864 				// IT Compatibilty: Slightly different waveform offsets (why does MPT have two different offsets here with IT old effects enabled and disabled?)
1865 				if(!m_playBehaviour[kITVibratoTremoloPanbrello] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS])
1866 					chn.nVibratoPos = 0x10;
1867 				else if(GetType() == MOD_TYPE_MTM)
1868 					chn.nVibratoPos = 0x20;
1869 				else if(!(GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM)))
1870 					chn.nVibratoPos = 0;
1871 			}
1872 			// IT Compatibility: No "retrigger" waveform here
1873 			if(!m_playBehaviour[kITVibratoTremoloPanbrello] && chn.nTremoloType < 4)
1874 			{
1875 				chn.nTremoloPos = 0;
1876 			}
1877 		}
1878 		if(chn.position.GetUInt() >= chn.nLength) chn.position.SetInt(chn.nLoopStart);
1879 	} else
1880 	{
1881 		bPorta = false;
1882 	}
1883 
1884 	if (!bPorta
1885 		|| (!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)))
1886 		|| (chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol)
1887 		|| (m_SongFlags[SONG_ITCOMPATGXX] && chn.rowCommand.instr != 0))
1888 	{
1889 		if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_DBM)) && chn.dwFlags[CHN_NOTEFADE] && !chn.nFadeOutVol)
1890 		{
1891 			chn.ResetEnvelopes();
1892 			// IT Compatibility: Autovibrato reset
1893 			if(!m_playBehaviour[kITVibratoTremoloPanbrello])
1894 			{
1895 				chn.nAutoVibDepth = 0;
1896 				chn.nAutoVibPos = 0;
1897 			}
1898 			chn.dwFlags.reset(CHN_NOTEFADE);
1899 			chn.nFadeOutVol = 65536;
1900 		}
1901 		if ((!bPorta) || (!m_SongFlags[SONG_ITCOMPATGXX]) || (chn.rowCommand.instr))
1902 		{
1903 			if ((!(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (chn.rowCommand.instr))
1904 			{
1905 				chn.dwFlags.reset(CHN_NOTEFADE);
1906 				chn.nFadeOutVol = 65536;
1907 			}
1908 		}
1909 	}
1910 
1911 	// IT compatibility: Don't reset key-off flag on porta notes unless Compat Gxx is enabled.
1912 	// Test case: Off-Porta.it, Off-Porta-CompatGxx.it, Off-Porta.xm
1913 	if(m_playBehaviour[kITFT2DontResetNoteOffOnPorta] && bPorta && (!m_SongFlags[SONG_ITCOMPATGXX] || chn.rowCommand.instr == 0))
1914 		chn.dwFlags.reset(CHN_EXTRALOUD);
1915 	else
1916 		chn.dwFlags.reset(CHN_EXTRALOUD | CHN_KEYOFF);
1917 
1918 	// Enable Ramping
1919 	if(!bPorta)
1920 	{
1921 		chn.nLeftVU = chn.nRightVU = 0xFF;
1922 		chn.dwFlags.reset(CHN_FILTER);
1923 		chn.dwFlags.set(CHN_FASTVOLRAMP);
1924 
1925 		// IT compatibility 15. Retrigger is reset in RetrigNote (Tremor doesn't store anything here, so we just don't reset this as well)
1926 		if(!m_playBehaviour[kITRetrigger] && !m_playBehaviour[kITTremor])
1927 		{
1928 			// FT2 compatibility: Retrigger is reset in RetrigNote, tremor in ProcessEffects
1929 			if(!m_playBehaviour[kFT2Retrigger] && !m_playBehaviour[kFT2Tremor])
1930 			{
1931 				chn.nRetrigCount = 0;
1932 				chn.nTremorCount = 0;
1933 			}
1934 		}
1935 
1936 		if(bResetEnv)
1937 		{
1938 			chn.nAutoVibDepth = 0;
1939 			chn.nAutoVibPos = 0;
1940 		}
1941 		chn.rightVol = chn.leftVol = 0;
1942 		bool useFilter = !m_SongFlags[SONG_MPTFILTERMODE];
1943 		// Setup Initial Filter for this note
1944 		if(pIns)
1945 		{
1946 			if(pIns->IsResonanceEnabled())
1947 			{
1948 				chn.nResonance = pIns->GetResonance();
1949 				useFilter = true;
1950 			}
1951 			if(pIns->IsCutoffEnabled())
1952 			{
1953 				chn.nCutOff = pIns->GetCutoff();
1954 				useFilter = true;
1955 			}
1956 			if(useFilter && (pIns->filterMode != FilterMode::Unchanged))
1957 			{
1958 				chn.nFilterMode = pIns->filterMode;
1959 			}
1960 		} else
1961 		{
1962 			chn.nVolSwing = chn.nPanSwing = 0;
1963 			chn.nCutSwing = chn.nResSwing = 0;
1964 		}
1965 		if((chn.nCutOff < 0x7F || m_playBehaviour[kITFilterBehaviour]) && useFilter)
1966 		{
1967 			int cutoff = SetupChannelFilter(chn, true);
1968 			if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl && channelHint != CHANNELINDEX_INVALID)
1969 				m_opl->Volume(channelHint, chn.nCutOff / 2u, true);
1970 		}
1971 
1972 		if(chn.dwFlags[CHN_ADLIB] && m_opl && channelHint != CHANNELINDEX_INVALID)
1973 		{
1974 			// Test case: AdlibZeroVolumeNote.s3m
1975 			if(m_playBehaviour[kOPLNoteOffOnNoteChange])
1976 				m_opl->NoteOff(channelHint);
1977 			else if(m_playBehaviour[kOPLNoteStopWith0Hz])
1978 				m_opl->Frequency(channelHint, 0, true, false);
1979 		}
1980 	}
1981 
1982 	// Special case for MPT
1983 	if (bManual) chn.dwFlags.reset(CHN_MUTE);
1984 	if((chn.dwFlags[CHN_MUTE] && (m_MixerSettings.MixerFlags & SNDMIX_MUTECHNMODE))
1985 		|| (chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_MUTE] && !bManual)
1986 		|| (chn.pModInstrument != nullptr && chn.pModInstrument->dwFlags[INS_MUTE] && !bManual))
1987 	{
1988 		if (!bManual) chn.nPeriod = 0;
1989 	}
1990 
1991 	// Reset the Amiga resampler for this channel
1992 	if(!bPorta)
1993 	{
1994 		chn.paulaState.Reset();
1995 	}
1996 }
1997 
1998 
1999 // Apply sample or instrument panning
ApplyInstrumentPanning(ModChannel & chn,const ModInstrument * instr,const ModSample * smp) const2000 void CSoundFile::ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *instr, const ModSample *smp) const
2001 {
2002 	int32 newPan = int32_min;
2003 	// Default instrument panning
2004 	if(instr != nullptr && instr->dwFlags[INS_SETPANNING])
2005 		newPan = instr->nPan;
2006 	// Default sample panning
2007 	if(smp != nullptr && smp->uFlags[CHN_PANNING])
2008 		newPan = smp->nPan;
2009 
2010 	if(newPan != int32_min)
2011 	{
2012 		chn.SetInstrumentPan(newPan, *this);
2013 		// IT compatibility: Sample and instrument panning overrides channel surround status.
2014 		// Test case: SmpInsPanSurround.it
2015 		if(m_playBehaviour[kPanOverride] && !m_SongFlags[SONG_SURROUNDPAN])
2016 		{
2017 			chn.dwFlags.reset(CHN_SURROUND);
2018 		}
2019 	}
2020 }
2021 
2022 
GetNNAChannel(CHANNELINDEX nChn) const2023 CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const
2024 {
2025 	// Check for empty channel
2026 	for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++)
2027 	{
2028 		const ModChannel &c = m_PlayState.Chn[i];
2029 		// No sample and no plugin playing
2030 		if(!c.nLength && !c.HasMIDIOutput())
2031 			return i;
2032 		// Plugin channel with already released note
2033 		if(!c.nLength && c.dwFlags[CHN_KEYOFF | CHN_NOTEFADE])
2034 			return i;
2035 		// Stopped OPL channel
2036 		if(c.dwFlags[CHN_ADLIB] && (!m_opl || !m_opl->IsActive(i)))
2037 			return i;
2038 	}
2039 
2040 	uint32 vol = 0x800000;
2041 	if(nChn < MAX_CHANNELS)
2042 	{
2043 		const ModChannel &srcChn = m_PlayState.Chn[nChn];
2044 		if(!srcChn.nFadeOutVol && srcChn.nLength)
2045 			return CHANNELINDEX_INVALID;
2046 		vol = (srcChn.nRealVolume << 9) | srcChn.nVolume;
2047 	}
2048 
2049 	// All channels are used: check for lowest volume
2050 	CHANNELINDEX result = CHANNELINDEX_INVALID;
2051 	uint32 envpos = 0;
2052 	for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++)
2053 	{
2054 		const ModChannel &c = m_PlayState.Chn[i];
2055 		if(c.nLength && !c.nFadeOutVol)
2056 			return i;
2057 		// Use a combination of real volume [14 bit] (which includes volume envelopes, but also potentially global volume) and note volume [9 bit].
2058 		// Rationale: We need volume envelopes in case e.g. all NNA channels are playing at full volume but are looping on a 0-volume envelope node.
2059 		// But if global volume is not applied to master and the global volume temporarily drops to 0, we would kill arbitrary channels. Hence, add the note volume as well.
2060 		uint32 v = (c.nRealVolume << 9) | c.nVolume;
2061 		if(c.dwFlags[CHN_LOOP])
2062 			v /= 2;
2063 		if((v < vol) || ((v == vol) && (c.VolEnv.nEnvPosition > envpos)))
2064 		{
2065 			envpos = c.VolEnv.nEnvPosition;
2066 			vol = v;
2067 			result = i;
2068 		}
2069 	}
2070 	return result;
2071 }
2072 
2073 
CheckNNA(CHANNELINDEX nChn,uint32 instr,int note,bool forceCut)2074 CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, bool forceCut)
2075 {
2076 	ModChannel &srcChn = m_PlayState.Chn[nChn];
2077 	const ModInstrument *pIns = nullptr;
2078 	if(!ModCommand::IsNote(static_cast<ModCommand::NOTE>(note)))
2079 		return CHANNELINDEX_INVALID;
2080 
2081 	// Always NNA cut - using
2082 	if((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_MT2)) || !m_nInstruments || forceCut) && !srcChn.HasMIDIOutput())
2083 	{
2084 		if(!srcChn.nLength || srcChn.dwFlags[CHN_MUTE] || !(srcChn.rightVol | srcChn.leftVol))
2085 			return CHANNELINDEX_INVALID;
2086 
2087 		if(srcChn.dwFlags[CHN_ADLIB] && m_opl)
2088 		{
2089 			m_opl->NoteCut(nChn, false);
2090 			return CHANNELINDEX_INVALID;
2091 		}
2092 
2093 		const CHANNELINDEX nnaChn = GetNNAChannel(nChn);
2094 		if(nnaChn == CHANNELINDEX_INVALID)
2095 			return CHANNELINDEX_INVALID;
2096 		ModChannel &chn = m_PlayState.Chn[nnaChn];
2097 		// Copy Channel
2098 		chn = srcChn;
2099 		chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_MUTE | CHN_PORTAMENTO);
2100 		chn.nPanbrelloOffset = 0;
2101 		chn.nMasterChn = nChn + 1;
2102 		chn.nCommand = CMD_NONE;
2103 		chn.rowCommand.Clear();
2104 		// Cut the note
2105 		chn.nFadeOutVol = 0;
2106 		chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP);
2107 		// Stop this channel
2108 		srcChn.nLength = 0;
2109 		srcChn.position.Set(0);
2110 		srcChn.nROfs = srcChn.nLOfs = 0;
2111 		srcChn.rightVol = srcChn.leftVol = 0;
2112 		return nnaChn;
2113 	}
2114 	if(instr > GetNumInstruments())
2115 		instr = 0;
2116 	const ModSample *pSample = srcChn.pModSample;
2117 	// If no instrument is given, assume previous instrument to still be valid.
2118 	// Test case: DNA-NoInstr.it
2119 	pIns = instr > 0 ? Instruments[instr] : srcChn.pModInstrument;
2120 	auto dnaNote = note;
2121 	if(pIns != nullptr)
2122 	{
2123 		auto smp = pIns->Keyboard[note - NOTE_MIN];
2124 		// IT compatibility: DCT = note uses pattern notes for comparison
2125 		// Note: This is not applied in case kITRealNoteMapping is not set to keep playback of legacy modules simple (chn.nNote is translated note in that case)
2126 		// Test case: dct_smp_note_test.it
2127 		if(!m_playBehaviour[kITDCTBehaviour] || !m_playBehaviour[kITRealNoteMapping])
2128 			dnaNote = pIns->NoteMap[note - NOTE_MIN];
2129 		if(smp > 0 && smp < MAX_SAMPLES)
2130 		{
2131 			pSample = &Samples[smp];
2132 		} else if(m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel())
2133 		{
2134 			// Impulse Tracker ignores empty slots.
2135 			// We won't ignore them if a plugin is assigned to this slot, so that VSTis still work as intended.
2136 			// Test case: emptyslot.it, PortaInsNum.it, gxsmp.it, gxsmp2.it
2137 			return CHANNELINDEX_INVALID;
2138 		}
2139 	}
2140 	if(srcChn.dwFlags[CHN_MUTE])
2141 		return CHANNELINDEX_INVALID;
2142 
2143 	for(CHANNELINDEX i = nChn; i < MAX_CHANNELS; i++)
2144 	{
2145 		// Only apply to background channels, or the same pattern channel
2146 		if(i < m_nChannels && i != nChn)
2147 			continue;
2148 
2149 		ModChannel &chn = m_PlayState.Chn[i];
2150 		bool applyDNAtoPlug = false;
2151 		if((chn.nMasterChn == nChn + 1 || i == nChn) && chn.pModInstrument != nullptr)
2152 		{
2153 			bool applyDNA = false;
2154 			// Duplicate Check Type
2155 			switch(chn.pModInstrument->nDCT)
2156 			{
2157 			case DuplicateCheckType::None:
2158 				break;
2159 			// Note
2160 			case DuplicateCheckType::Note:
2161 				if(dnaNote != NOTE_NONE && chn.nNote == dnaNote && pIns == chn.pModInstrument)
2162 					applyDNA = true;
2163 				if(pIns && pIns->nMixPlug)
2164 					applyDNAtoPlug = true;
2165 				break;
2166 			// Sample
2167 			case DuplicateCheckType::Sample:
2168 				// IT compatibility: DCT = sample only applies to same instrument
2169 				// Test case: dct_smp_note_test.it
2170 				if(pSample != nullptr && pSample == chn.pModSample && (pIns == chn.pModInstrument || !m_playBehaviour[kITDCTBehaviour]))
2171 					applyDNA = true;
2172 				break;
2173 			// Instrument
2174 			case DuplicateCheckType::Instrument:
2175 				if(pIns == chn.pModInstrument)
2176 					applyDNA = true;
2177 				if(pIns && pIns->nMixPlug)
2178 					applyDNAtoPlug = true;
2179 				break;
2180 			// Plugin
2181 			case DuplicateCheckType::Plugin:
2182 				if(pIns && (pIns->nMixPlug) && (pIns->nMixPlug == chn.pModInstrument->nMixPlug))
2183 				{
2184 					applyDNAtoPlug = true;
2185 					applyDNA = true;
2186 				}
2187 				break;
2188 			}
2189 
2190 			// Duplicate Note Action
2191 			if(applyDNA)
2192 			{
2193 #ifndef NO_PLUGINS
2194 				if(applyDNAtoPlug && chn.nNote != NOTE_NONE)
2195 				{
2196 					switch(chn.pModInstrument->nDNA)
2197 					{
2198 					case DuplicateNoteAction::NoteCut:
2199 					case DuplicateNoteAction::NoteOff:
2200 					case DuplicateNoteAction::NoteFade:
2201 						// Switch off duplicated note played on this plugin
2202 						SendMIDINote(i, chn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]) + NOTE_MAX_SPECIAL, 0);
2203 						chn.nArpeggioLastNote = NOTE_NONE;
2204 						break;
2205 					}
2206 				}
2207 #endif // NO_PLUGINS
2208 
2209 				switch(chn.pModInstrument->nDNA)
2210 				{
2211 				// Cut
2212 				case DuplicateNoteAction::NoteCut:
2213 					KeyOff(chn);
2214 					chn.nVolume = 0;
2215 					if(chn.dwFlags[CHN_ADLIB] && m_opl)
2216 						m_opl->NoteCut(i);
2217 					break;
2218 				// Note Off
2219 				case DuplicateNoteAction::NoteOff:
2220 					KeyOff(chn);
2221 					if(chn.dwFlags[CHN_ADLIB] && m_opl)
2222 						m_opl->NoteOff(i);
2223 					break;
2224 				// Note Fade
2225 				case DuplicateNoteAction::NoteFade:
2226 					chn.dwFlags.set(CHN_NOTEFADE);
2227 					if(chn.dwFlags[CHN_ADLIB] && m_opl && !m_playBehaviour[kOPLwithNNA])
2228 						m_opl->NoteOff(i);
2229 					break;
2230 				}
2231 				if(!chn.nVolume)
2232 				{
2233 					chn.nFadeOutVol = 0;
2234 					chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP);
2235 				}
2236 			}
2237 		}
2238 	}
2239 
2240 	// Do we need to apply New/Duplicate Note Action to a VSTi?
2241 	bool applyNNAtoPlug = false;
2242 #ifndef NO_PLUGINS
2243 	IMixPlugin *pPlugin = nullptr;
2244 	if(srcChn.HasMIDIOutput() && ModCommand::IsNote(srcChn.nNote)) // instro sends to a midi chan
2245 	{
2246 		PLUGINDEX plugin = GetBestPlugin(nChn, PrioritiseInstrument, RespectMutes);
2247 
2248 		if(plugin > 0 && plugin <= MAX_MIXPLUGINS)
2249 		{
2250 			pPlugin =  m_MixPlugins[plugin - 1].pMixPlugin;
2251 			if(pPlugin)
2252 			{
2253 				// apply NNA to this plugin iff it is currently playing a note on this tracker channel
2254 				// (and if it is playing a note, we know that would be the last note played on this chan).
2255 				applyNNAtoPlug = pPlugin->IsNotePlaying(srcChn.GetPluginNote(m_playBehaviour[kITRealNoteMapping]), nChn);
2256 			}
2257 		}
2258 	}
2259 #endif // NO_PLUGINS
2260 
2261 	// New Note Action
2262 	if(!srcChn.IsSamplePlaying() && !applyNNAtoPlug)
2263 		return CHANNELINDEX_INVALID;
2264 
2265 	CHANNELINDEX nnaChn = GetNNAChannel(nChn);
2266 	if(nnaChn == CHANNELINDEX_INVALID)
2267 		return CHANNELINDEX_INVALID;
2268 
2269 	ModChannel &chn = m_PlayState.Chn[nnaChn];
2270 	if(chn.dwFlags[CHN_ADLIB] && m_opl)
2271 		m_opl->NoteCut(nnaChn);
2272 	// Copy Channel
2273 	chn = srcChn;
2274 	chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_PORTAMENTO);
2275 	chn.nPanbrelloOffset = 0;
2276 
2277 	chn.nMasterChn = nChn < GetNumChannels() ? nChn + 1 : 0;
2278 	chn.nCommand = CMD_NONE;
2279 #ifndef NO_PLUGINS
2280 	if(applyNNAtoPlug && pPlugin)
2281 	{
2282 		switch(srcChn.nNNA)
2283 		{
2284 		case NewNoteAction::NoteOff:
2285 		case NewNoteAction::NoteCut:
2286 		case NewNoteAction::NoteFade:
2287 			// Switch off note played on this plugin, on this tracker channel and midi channel
2288 			SendMIDINote(nChn, NOTE_KEYOFF, 0);
2289 			srcChn.nArpeggioLastNote = NOTE_NONE;
2290 			break;
2291 		case NewNoteAction::Continue:
2292 			break;
2293 		}
2294 	}
2295 #endif // NO_PLUGINS
2296 
2297 	// Key Off the note
2298 	switch(srcChn.nNNA)
2299 	{
2300 	case NewNoteAction::NoteOff:
2301 		KeyOff(chn);
2302 		if(chn.dwFlags[CHN_ADLIB] && m_opl)
2303 		{
2304 			m_opl->NoteOff(nChn);
2305 			if(m_playBehaviour[kOPLwithNNA])
2306 				m_opl->MoveChannel(nChn, nnaChn);
2307 		}
2308 		break;
2309 	case NewNoteAction::NoteCut:
2310 		chn.nFadeOutVol = 0;
2311 		chn.dwFlags.set(CHN_NOTEFADE);
2312 		if(chn.dwFlags[CHN_ADLIB] && m_opl)
2313 			m_opl->NoteCut(nChn);
2314 		break;
2315 	case NewNoteAction::NoteFade:
2316 		chn.dwFlags.set(CHN_NOTEFADE);
2317 		if(chn.dwFlags[CHN_ADLIB] && m_opl)
2318 		{
2319 			if(m_playBehaviour[kOPLwithNNA])
2320 				m_opl->MoveChannel(nChn, nnaChn);
2321 			else
2322 				m_opl->NoteOff(nChn);
2323 		}
2324 		break;
2325 	case NewNoteAction::Continue:
2326 		if(chn.dwFlags[CHN_ADLIB] && m_opl)
2327 			m_opl->MoveChannel(nChn, nnaChn);
2328 		break;
2329 	}
2330 	if(!chn.nVolume)
2331 	{
2332 		chn.nFadeOutVol = 0;
2333 		chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP);
2334 	}
2335 	// Stop this channel
2336 	srcChn.nLength = 0;
2337 	srcChn.position.Set(0);
2338 	srcChn.nROfs = srcChn.nLOfs = 0;
2339 
2340 	return nnaChn;
2341 }
2342 
2343 
ProcessEffects()2344 bool CSoundFile::ProcessEffects()
2345 {
2346 	m_PlayState.m_breakRow = ROWINDEX_INVALID;    // Is changed if a break to row command is encountered
2347 	m_PlayState.m_patLoopRow = ROWINDEX_INVALID;  // Is changed if a pattern loop jump-back is executed
2348 	m_PlayState.m_posJump = ORDERINDEX_INVALID;
2349 
2350 	for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
2351 	{
2352 		ModChannel &chn = m_PlayState.Chn[nChn];
2353 		const uint32 tickCount = m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay);
2354 		uint32 instr = chn.rowCommand.instr;
2355 		ModCommand::VOLCMD volcmd = chn.rowCommand.volcmd;
2356 		uint32 vol = chn.rowCommand.vol;
2357 		ModCommand::COMMAND cmd = chn.rowCommand.command;
2358 		uint32 param = chn.rowCommand.param;
2359 		bool bPorta = chn.rowCommand.IsPortamento();
2360 
2361 		uint32 nStartTick = 0;
2362 		chn.isFirstTick = m_SongFlags[SONG_FIRSTTICK];
2363 
2364 		// Process parameter control note.
2365 		if(chn.rowCommand.note == NOTE_PC)
2366 		{
2367 #ifndef NO_PLUGINS
2368 			const PLUGINDEX plug = chn.rowCommand.instr;
2369 			const PlugParamIndex plugparam = chn.rowCommand.GetValueVolCol();
2370 			const PlugParamValue value = chn.rowCommand.GetValueEffectCol() / PlugParamValue(ModCommand::maxColumnValue);
2371 
2372 			if(plug > 0 && plug <= MAX_MIXPLUGINS && m_MixPlugins[plug - 1].pMixPlugin)
2373 				m_MixPlugins[plug-1].pMixPlugin->SetParameter(plugparam, value);
2374 #endif // NO_PLUGINS
2375 		}
2376 
2377 		// Process continuous parameter control note.
2378 		// Row data is cleared after first tick so on following
2379 		// ticks using channels m_nPlugParamValueStep to identify
2380 		// the need for parameter control. The condition cmd == 0
2381 		// is to make sure that m_nPlugParamValueStep != 0 because
2382 		// of NOTE_PCS, not because of macro.
2383 		if(chn.rowCommand.note == NOTE_PCS || (cmd == CMD_NONE && chn.m_plugParamValueStep != 0))
2384 		{
2385 #ifndef NO_PLUGINS
2386 			const bool isFirstTick = m_SongFlags[SONG_FIRSTTICK];
2387 			if(isFirstTick)
2388 				chn.m_RowPlug = chn.rowCommand.instr;
2389 			const PLUGINDEX plugin = chn.m_RowPlug;
2390 			const bool hasValidPlug = (plugin > 0 && plugin <= MAX_MIXPLUGINS && m_MixPlugins[plugin - 1].pMixPlugin);
2391 			if(hasValidPlug)
2392 			{
2393 				if(isFirstTick)
2394 					chn.m_RowPlugParam = ModCommand::GetValueVolCol(chn.rowCommand.volcmd, chn.rowCommand.vol);
2395 				const PlugParamIndex plugparam = chn.m_RowPlugParam;
2396 				if(isFirstTick)
2397 				{
2398 					PlugParamValue targetvalue = ModCommand::GetValueEffectCol(chn.rowCommand.command, chn.rowCommand.param) / PlugParamValue(ModCommand::maxColumnValue);
2399 					chn.m_plugParamTargetValue = targetvalue;
2400 					chn.m_plugParamValueStep = (targetvalue - m_MixPlugins[plugin - 1].pMixPlugin->GetParameter(plugparam)) / PlugParamValue(m_PlayState.TicksOnRow());
2401 				}
2402 				if(m_PlayState.m_nTickCount + 1 == m_PlayState.TicksOnRow())
2403 				{	// On last tick, set parameter exactly to target value.
2404 					m_MixPlugins[plugin - 1].pMixPlugin->SetParameter(plugparam, chn.m_plugParamTargetValue);
2405 				}
2406 				else
2407 					m_MixPlugins[plugin - 1].pMixPlugin->ModifyParameter(plugparam, chn.m_plugParamValueStep);
2408 			}
2409 #endif // NO_PLUGINS
2410 		}
2411 
2412 		// Apart from changing parameters, parameter control notes are intended to be 'invisible'.
2413 		// To achieve this, clearing the note data so that rest of the process sees the row as empty row.
2414 		if(ModCommand::IsPcNote(chn.rowCommand.note))
2415 		{
2416 			chn.ClearRowCmd();
2417 			instr = 0;
2418 			volcmd = VOLCMD_NONE;
2419 			vol = 0;
2420 			cmd = CMD_NONE;
2421 			param = 0;
2422 			bPorta = false;
2423 		}
2424 
2425 		// Process Invert Loop (MOD Effect, called every row if it's active)
2426 		if(!m_SongFlags[SONG_FIRSTTICK])
2427 		{
2428 			InvertLoop(m_PlayState.Chn[nChn]);
2429 		} else
2430 		{
2431 			if(instr) m_PlayState.Chn[nChn].nEFxOffset = 0;
2432 		}
2433 
2434 		// Process special effects (note delay, pattern delay, pattern loop)
2435 		if (cmd == CMD_DELAYCUT)
2436 		{
2437 			//:xy --> note delay until tick x, note cut at tick x+y
2438 			nStartTick = (param & 0xF0) >> 4;
2439 			const uint32 cutAtTick = nStartTick + (param & 0x0F);
2440 			NoteCut(nChn, cutAtTick, m_playBehaviour[kITSCxStopsSample]);
2441 		} else if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX))
2442 		{
2443 			if ((!param) && (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)))
2444 				param = chn.nOldCmdEx;
2445 			else
2446 				chn.nOldCmdEx = static_cast<ModCommand::PARAM>(param);
2447 
2448 			// Note Delay ?
2449 			if ((param & 0xF0) == 0xD0)
2450 			{
2451 				nStartTick = param & 0x0F;
2452 				if(nStartTick == 0)
2453 				{
2454 					//IT compatibility 22. SD0 == SD1
2455 					if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
2456 						nStartTick = 1;
2457 					//ST3 ignores notes with SD0 completely
2458 					else if(GetType() == MOD_TYPE_S3M)
2459 						continue;
2460 				} else if(nStartTick >= (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay) && m_playBehaviour[kITOutOfRangeDelay])
2461 				{
2462 					// IT compatibility 08. Handling of out-of-range delay command.
2463 					// Additional test case: tickdelay.it
2464 					if(instr)
2465 					{
2466 						chn.nNewIns = static_cast<ModCommand::INSTR>(instr);
2467 					}
2468 					continue;
2469 				}
2470 			} else if(m_SongFlags[SONG_FIRSTTICK])
2471 			{
2472 				// Pattern Loop ?
2473 				if((param & 0xF0) == 0xE0)
2474 				{
2475 					// Pattern Delay
2476 					// In Scream Tracker 3 / Impulse Tracker, only the first delay command on this row is considered.
2477 					// Test cases: PatternDelays.it, PatternDelays.s3m, PatternDelays.xm
2478 					// XXX In Scream Tracker 3, the "left" channels are evaluated before the "right" channels, which is not emulated here!
2479 					if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)) || !m_PlayState.m_nPatternDelay)
2480 					{
2481 						if(!(GetType() & (MOD_TYPE_S3M)) || (param & 0x0F) != 0)
2482 						{
2483 							// While Impulse Tracker *does* count S60 as a valid row delay (and thus ignores any other row delay commands on the right),
2484 							// Scream Tracker 3 simply ignores such commands.
2485 							m_PlayState.m_nPatternDelay = 1 + (param & 0x0F);
2486 						}
2487 					}
2488 				}
2489 			}
2490 		}
2491 
2492 		if(GetType() == MOD_TYPE_MTM && cmd == CMD_MODCMDEX && (param & 0xF0) == 0xD0)
2493 		{
2494 			// Apparently, retrigger and note delay have the same behaviour in MultiTracker:
2495 			// They both restart the note at tick x, and if there is a note on the same row,
2496 			// this note is started on the first tick.
2497 			nStartTick = 0;
2498 			param = 0x90 | (param & 0x0F);
2499 		}
2500 
2501 		if(nStartTick != 0 && chn.rowCommand.note == NOTE_KEYOFF && chn.rowCommand.volcmd == VOLCMD_PANNING && m_playBehaviour[kFT2PanWithDelayedNoteOff])
2502 		{
2503 			// FT2 compatibility: If there's a delayed note off, panning commands are ignored. WTF!
2504 			// Test case: PanOff.xm
2505 			chn.rowCommand.volcmd = VOLCMD_NONE;
2506 		}
2507 
2508 		bool triggerNote = (m_PlayState.m_nTickCount == nStartTick);	// Can be delayed by a note delay effect
2509 		if(m_playBehaviour[kFT2OutOfRangeDelay] && nStartTick >= m_PlayState.m_nMusicSpeed)
2510 		{
2511 			// FT2 compatibility: Note delays greater than the song speed should be ignored.
2512 			// However, EEx pattern delay is *not* considered at all.
2513 			// Test case: DelayCombination.xm, PortaDelay.xm
2514 			triggerNote = false;
2515 		} else if(m_playBehaviour[kRowDelayWithNoteDelay] && nStartTick > 0 && tickCount == nStartTick)
2516 		{
2517 			// IT compatibility: Delayed notes (using SDx) that are on the same row as a Row Delay effect are retriggered.
2518 			// ProTracker / Scream Tracker 3 / FastTracker 2 do the same.
2519 			// Test case: PatternDelay-NoteDelay.it, PatternDelay-NoteDelay.xm, PatternDelaysRetrig.mod
2520 			triggerNote = true;
2521 		}
2522 
2523 		// IT compatibility: Tick-0 vs non-tick-0 effect distinction is always based on tick delay.
2524 		// Test case: SlideDelay.it
2525 		if(m_playBehaviour[kITFirstTickHandling])
2526 		{
2527 			chn.isFirstTick = tickCount == nStartTick;
2528 		}
2529 		chn.triggerNote = triggerNote;
2530 
2531 		// FT2 compatibility: Note + portamento + note delay = no portamento
2532 		// Test case: PortaDelay.xm
2533 		if(m_playBehaviour[kFT2PortaDelay] && nStartTick != 0)
2534 		{
2535 			bPorta = false;
2536 		}
2537 
2538 		if(m_SongFlags[SONG_PT_MODE] && instr && !m_PlayState.m_nTickCount)
2539 		{
2540 			// Instrument number resets the stacked ProTracker offset.
2541 			// Test case: ptoffset.mod
2542 			chn.prevNoteOffset = 0;
2543 			// ProTracker compatibility: Sample properties are always loaded on the first tick, even when there is a note delay.
2544 			// Test case: InstrDelay.mod
2545 			if(!triggerNote && chn.IsSamplePlaying())
2546 			{
2547 				chn.nNewIns = static_cast<ModCommand::INSTR>(instr);
2548 				if(instr <= GetNumSamples())
2549 				{
2550 					chn.nVolume = Samples[instr].nVolume;
2551 					chn.nFineTune = Samples[instr].nFineTune;
2552 				}
2553 			}
2554 		}
2555 
2556 		// Handles note/instrument/volume changes
2557 		if(triggerNote)
2558 		{
2559 			ModCommand::NOTE note = chn.rowCommand.note;
2560 			if(instr) chn.nNewIns = static_cast<ModCommand::INSTR>(instr);
2561 
2562 			if(ModCommand::IsNote(note) && m_playBehaviour[kFT2Transpose])
2563 			{
2564 				// Notes that exceed FT2's limit are completely ignored.
2565 				// Test case: NoteLimit.xm
2566 				int transpose = chn.nTranspose;
2567 				if(instr && !bPorta)
2568 				{
2569 					// Refresh transpose
2570 					// Test case: NoteLimit2.xm
2571 					const SAMPLEINDEX sample = GetSampleIndex(note, instr);
2572 					if(sample > 0)
2573 						transpose = GetSample(sample).RelativeTone;
2574 				}
2575 
2576 				const int computedNote = note + transpose;
2577 				if((computedNote < NOTE_MIN + 11 || computedNote > NOTE_MIN + 130))
2578 				{
2579 					note = NOTE_NONE;
2580 				}
2581 			} else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_J2B)) && GetNumInstruments() != 0 && ModCommand::IsNoteOrEmpty(static_cast<ModCommand::NOTE>(note)))
2582 			{
2583 				// IT compatibility: Invalid instrument numbers do nothing, but they are remembered for upcoming notes and do not trigger a note in that case.
2584 				// Test case: InstrumentNumberChange.it
2585 				INSTRUMENTINDEX instrToCheck = static_cast<INSTRUMENTINDEX>((instr != 0) ? instr : chn.nOldIns);
2586 				if(instrToCheck != 0 && (instrToCheck > GetNumInstruments() || Instruments[instrToCheck] == nullptr))
2587 				{
2588 					note = NOTE_NONE;
2589 					instr = 0;
2590 				}
2591 			}
2592 
2593 			// XM: FT2 ignores a note next to a K00 effect, and a fade-out seems to be done when no volume envelope is present (not exactly the Kxx behaviour)
2594 			if(cmd == CMD_KEYOFF && param == 0 && m_playBehaviour[kFT2KeyOff])
2595 			{
2596 				note = NOTE_NONE;
2597 				instr = 0;
2598 			}
2599 
2600 			bool retrigEnv = note == NOTE_NONE && instr != 0;
2601 
2602 			// Apparently, any note number in a pattern causes instruments to recall their original volume settings - no matter if there's a Note Off next to it or whatever.
2603 			// Test cases: keyoff+instr.xm, delay.xm
2604 			bool reloadSampleSettings = (m_playBehaviour[kFT2ReloadSampleSettings] && instr != 0);
2605 			// ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it
2606 			// Test case: PTSwapEmpty.mod, PTInstrVolume.mod, SampleSwap.s3m
2607 			bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
2608 				|| m_playBehaviour[kST3SampleSwap]
2609 				|| (m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying() && (chn.pModSample == nullptr || !chn.pModSample->HasSampleData()));
2610 
2611 			// Now it's time for some FT2 crap...
2612 			if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
2613 			{
2614 				// XM: Key-Off + Sample == Note Cut (BUT: Only if no instr number or volume effect is present!)
2615 				// Test case: NoteOffVolume.xm
2616 				if(note == NOTE_KEYOFF
2617 					&& ((!instr && volcmd != VOLCMD_VOLUME && cmd != CMD_VOLUME) || !m_playBehaviour[kFT2KeyOff])
2618 					&& (chn.pModInstrument == nullptr || !chn.pModInstrument->VolEnv.dwFlags[ENV_ENABLED]))
2619 				{
2620 					chn.dwFlags.set(CHN_FASTVOLRAMP);
2621 					chn.nVolume = 0;
2622 					note = NOTE_NONE;
2623 					instr = 0;
2624 					retrigEnv = false;
2625 					// FT2 Compatbility: Start fading the note for notes with no delay. Only relevant when a volume command is encountered after the note-off.
2626 					// Test case: NoteOffFadeNoEnv.xm
2627 					if(m_SongFlags[SONG_FIRSTTICK] && m_playBehaviour[kFT2NoteOffFlags])
2628 						chn.dwFlags.set(CHN_NOTEFADE);
2629 				} else if(m_playBehaviour[kFT2RetrigWithNoteDelay] && !m_SongFlags[SONG_FIRSTTICK])
2630 				{
2631 					// FT2 Compatibility: Some special hacks for rogue note delays... (EDx with x > 0)
2632 					// Apparently anything that is next to a note delay behaves totally unpredictable in FT2. Swedish tracker logic. :)
2633 
2634 					retrigEnv = true;
2635 
2636 					// Portamento + Note Delay = No Portamento
2637 					// Test case: porta-delay.xm
2638 					bPorta = false;
2639 
2640 					if(note == NOTE_NONE)
2641 					{
2642 						// If there's a note delay but no real note, retrig the last note.
2643 						// Test case: delay2.xm, delay3.xm
2644 						note = static_cast<ModCommand::NOTE>(chn.nNote - chn.nTranspose);
2645 					} else if(note >= NOTE_MIN_SPECIAL)
2646 					{
2647 						// Gah! Even Note Off + Note Delay will cause envelopes to *retrigger*! How stupid is that?
2648 						// ... Well, and that is actually all it does if there's an envelope. No fade out, no nothing. *sigh*
2649 						// Test case: OffDelay.xm
2650 						note = NOTE_NONE;
2651 						keepInstr = false;
2652 						reloadSampleSettings = true;
2653 					} else if(instr || !m_playBehaviour[kFT2NoteDelayWithoutInstr])
2654 					{
2655 						// Normal note (only if there is an instrument, test case: DelayVolume.xm)
2656 						keepInstr = true;
2657 						reloadSampleSettings = true;
2658 					}
2659 				}
2660 			}
2661 
2662 			if((retrigEnv && !m_playBehaviour[kFT2ReloadSampleSettings]) || reloadSampleSettings)
2663 			{
2664 				const ModSample *oldSample = nullptr;
2665 				// Reset default volume when retriggering envelopes
2666 
2667 				if(GetNumInstruments())
2668 				{
2669 					oldSample = chn.pModSample;
2670 				} else if (instr <= GetNumSamples())
2671 				{
2672 					// Case: Only samples are used; no instruments.
2673 					oldSample = &Samples[instr];
2674 				}
2675 
2676 				if(oldSample != nullptr)
2677 				{
2678 					if(!oldSample->uFlags[SMP_NODEFAULTVOLUME] && (GetType() != MOD_TYPE_S3M || oldSample->HasSampleData()))
2679 						chn.nVolume = oldSample->nVolume;
2680 					if(reloadSampleSettings)
2681 					{
2682 						// Also reload panning
2683 						chn.SetInstrumentPan(oldSample->nPan, *this);
2684 					}
2685 				}
2686 			}
2687 
2688 			// FT2 compatibility: Instrument number disables tremor effect
2689 			// Test case: TremorInstr.xm, TremoRecover.xm
2690 			if(m_playBehaviour[kFT2Tremor] && instr != 0)
2691 			{
2692 				chn.nTremorCount = 0x20;
2693 			}
2694 
2695 			// IT compatibility: Envelope retriggering with instrument number based on Old Effects and Compatible Gxx flags:
2696 			// OldFX CompatGxx Env Behaviour
2697 			// ----- --------- -------------
2698 			//  off     off    never reset
2699 			//  on      off    reset on instrument without portamento
2700 			//  off     on     reset on instrument with portamento
2701 			//  on      on     always reset
2702 			// Test case: ins-xx.it, ins-ox.it, ins-oc.it, ins-xc.it, ResetEnvNoteOffOldFx.it, ResetEnvNoteOffOldFx2.it, noteoff3.it
2703 			if(GetNumInstruments() && m_playBehaviour[kITInstrWithNoteOffOldEffects]
2704 				&& instr && !ModCommand::IsNote(note))
2705 			{
2706 				if((bPorta && m_SongFlags[SONG_ITCOMPATGXX])
2707 					|| (!bPorta && m_SongFlags[SONG_ITOLDEFFECTS]))
2708 				{
2709 					chn.ResetEnvelopes();
2710 					chn.dwFlags.set(CHN_FASTVOLRAMP);
2711 					chn.nFadeOutVol = 65536;
2712 				}
2713 			}
2714 
2715 			if(retrigEnv) //Case: instrument with no note data.
2716 			{
2717 				//IT compatibility: Instrument with no note.
2718 				if(m_playBehaviour[kITInstrWithoutNote] || GetType() == MOD_TYPE_PLM)
2719 				{
2720 					// IT compatibility: Completely retrigger note after sample end to also reset portamento.
2721 					// Test case: PortaResetAfterRetrigger.it
2722 					bool triggerAfterSmpEnd = m_playBehaviour[kITMultiSampleInstrumentNumber] && !chn.IsSamplePlaying();
2723 					if(GetNumInstruments())
2724 					{
2725 						// Instrument mode
2726 						if(instr <= GetNumInstruments() && (chn.pModInstrument != Instruments[instr] || triggerAfterSmpEnd))
2727 							note = chn.nNote;
2728 					} else
2729 					{
2730 						// Sample mode
2731 						if(instr < MAX_SAMPLES && (chn.pModSample != &Samples[instr] || triggerAfterSmpEnd))
2732 							note = chn.nNote;
2733 					}
2734 				}
2735 
2736 				if(GetNumInstruments() && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED)))
2737 				{
2738 					chn.ResetEnvelopes();
2739 					chn.dwFlags.set(CHN_FASTVOLRAMP);
2740 					chn.dwFlags.reset(CHN_NOTEFADE);
2741 					chn.nAutoVibDepth = 0;
2742 					chn.nAutoVibPos = 0;
2743 					chn.nFadeOutVol = 65536;
2744 					// FT2 Compatbility: Reset key-off status with instrument number
2745 					// Test case: NoteOffInstrChange.xm
2746 					if(m_playBehaviour[kFT2NoteOffFlags])
2747 						chn.dwFlags.reset(CHN_KEYOFF);
2748 				}
2749 				if (!keepInstr) instr = 0;
2750 			}
2751 
2752 			// Note Cut/Off/Fade => ignore instrument
2753 			if (note >= NOTE_MIN_SPECIAL)
2754 			{
2755 				// IT compatibility: Default volume of sample is recalled if instrument number is next to a note-off.
2756 				// Test case: NoteOffInstr.it, noteoff2.it
2757 				if(m_playBehaviour[kITInstrWithNoteOff] && instr)
2758 				{
2759 					const SAMPLEINDEX smp = GetSampleIndex(chn.nLastNote, instr);
2760 					if(smp > 0 && !Samples[smp].uFlags[SMP_NODEFAULTVOLUME])
2761 						chn.nVolume = Samples[smp].nVolume;
2762 				}
2763 				// IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes.
2764 				// Test case: ResetEnvNoteOffOldFx.it
2765 				if(!m_playBehaviour[kITInstrWithNoteOffOldEffects] || !m_SongFlags[SONG_ITOLDEFFECTS])
2766 					instr = 0;
2767 			}
2768 
2769 			if(ModCommand::IsNote(note))
2770 			{
2771 				chn.nNewNote = chn.nLastNote = note;
2772 
2773 				// New Note Action ?
2774 				if(!bPorta)
2775 				{
2776 					CheckNNA(nChn, instr, note, false);
2777 				}
2778 
2779 				if(chn.nRestorePanOnNewNote > 0)
2780 				{
2781 					chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1;
2782 					if(chn.nRestorePanOnNewNote & 0x8000)
2783 						chn.dwFlags.set(CHN_SURROUND);
2784 					chn.nRestorePanOnNewNote = 0;
2785 				}
2786 				if(chn.nRestoreResonanceOnNewNote > 0)
2787 				{
2788 					chn.nResonance = chn.nRestoreResonanceOnNewNote - 1;
2789 					chn.nRestoreResonanceOnNewNote = 0;
2790 				}
2791 				if(chn.nRestoreCutoffOnNewNote > 0)
2792 				{
2793 					chn.nCutOff = chn.nRestoreCutoffOnNewNote - 1;
2794 					chn.nRestoreCutoffOnNewNote = 0;
2795 				}
2796 			}
2797 
2798 			// Instrument Change ?
2799 			if(instr)
2800 			{
2801 				const ModSample *oldSample = chn.pModSample;
2802 				//const ModInstrument *oldInstrument = chn.pModInstrument;
2803 
2804 				InstrumentChange(chn, instr, bPorta, true);
2805 
2806 				if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl)
2807 				{
2808 					m_opl->Patch(nChn, chn.pModSample->adlib);
2809 				}
2810 
2811 				// IT compatibility: Keep new instrument number for next instrument-less note even if sample playback is stopped
2812 				// Test case: StoppedInstrSwap.it
2813 				if(GetType() == MOD_TYPE_MOD)
2814 				{
2815 					// Test case: PortaSwapPT.mod
2816 					if(!bPorta || !m_playBehaviour[kMODSampleSwap]) chn.nNewIns = 0;
2817 				} else
2818 				{
2819 					if(!m_playBehaviour[kITInstrWithNoteOff] || ModCommand::IsNote(note)) chn.nNewIns = 0;
2820 				}
2821 
2822 				if(m_playBehaviour[kITPortamentoSwapResetsPos])
2823 				{
2824 					// Test cases: PortaInsNum.it, PortaSample.it
2825 					if(ModCommand::IsNote(note) && oldSample != chn.pModSample)
2826 					{
2827 						//const bool newInstrument = oldInstrument != chn.pModInstrument && chn.pModInstrument->Keyboard[chn.nNewNote - NOTE_MIN] != 0;
2828 						chn.position.Set(0);
2829 					}
2830 				} else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && oldSample != chn.pModSample && ModCommand::IsNote(note))
2831 				{
2832 					// Special IT case: portamento+note causes sample change -> ignore portamento
2833 					bPorta = false;
2834 				} else if(m_playBehaviour[kST3SampleSwap] && oldSample != chn.pModSample && (bPorta || !ModCommand::IsNote(note)) && chn.position.GetUInt() > chn.nLength)
2835 				{
2836 					// ST3 with SoundBlaster does sample swapping and continues playing the new sample where the old sample was stopped.
2837 					// If the new sample is shorter than that, it is stopped, even if it could be looped.
2838 					// This also applies to portamento between different samples.
2839 					// Test case: SampleSwap.s3m
2840 					chn.nLength = 0;
2841 				} else if(m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying())
2842 				{
2843 					// If channel was paused and is resurrected by a lone instrument number, reset the sample position.
2844 					// Test case: PTSwapEmpty.mod
2845 					chn.position.Set(0);
2846 				}
2847 			}
2848 			// New Note ?
2849 			if (note != NOTE_NONE)
2850 			{
2851 				const bool instrChange = (!instr) && (chn.nNewIns) && ModCommand::IsNote(note);
2852 				if(instrChange)
2853 				{
2854 					InstrumentChange(chn, chn.nNewIns, bPorta, chn.pModSample == nullptr && chn.pModInstrument == nullptr, !(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)));
2855 					chn.nNewIns = 0;
2856 				}
2857 				if(chn.pModSample != nullptr && chn.pModSample->uFlags[CHN_ADLIB] && m_opl && (instrChange || !m_opl->IsActive(nChn)))
2858 				{
2859 					m_opl->Patch(nChn, chn.pModSample->adlib);
2860 				}
2861 
2862 				NoteChange(chn, note, bPorta, !(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)), false, nChn);
2863 				if ((bPorta) && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr))
2864 				{
2865 					chn.dwFlags.set(CHN_FASTVOLRAMP);
2866 					chn.ResetEnvelopes();
2867 					chn.nAutoVibDepth = 0;
2868 					chn.nAutoVibPos = 0;
2869 				}
2870 				if(chn.dwFlags[CHN_ADLIB] && m_opl
2871 					&& ((note == NOTE_NOTECUT || note == NOTE_KEYOFF) || (note == NOTE_FADE && !m_playBehaviour[kOPLFlexibleNoteOff])))
2872 				{
2873 					if(m_playBehaviour[kOPLNoteStopWith0Hz])
2874 						m_opl->Frequency(nChn, 0, true, false);
2875 					m_opl->NoteOff(nChn);
2876 				}
2877 			}
2878 			// Tick-0 only volume commands
2879 			if (volcmd == VOLCMD_VOLUME)
2880 			{
2881 				if (vol > 64) vol = 64;
2882 				chn.nVolume = vol << 2;
2883 				chn.dwFlags.set(CHN_FASTVOLRAMP);
2884 			} else
2885 			if (volcmd == VOLCMD_PANNING)
2886 			{
2887 				Panning(chn, vol, Pan6bit);
2888 			}
2889 
2890 #ifndef NO_PLUGINS
2891 			if (m_nInstruments) ProcessMidiOut(nChn);
2892 #endif // NO_PLUGINS
2893 		}
2894 
2895 		if(m_playBehaviour[kST3NoMutedChannels] && ChnSettings[nChn].dwFlags[CHN_MUTE])	// not even effects are processed on muted S3M channels
2896 			continue;
2897 
2898 		// Volume Column Effect (except volume & panning)
2899 		/*	A few notes, paraphrased from ITTECH.TXT by Storlek (creator of schismtracker):
2900 			Ex/Fx/Gx are shared with Exx/Fxx/Gxx; Ex/Fx are 4x the 'normal' slide value
2901 			Gx is linked with Ex/Fx if Compat Gxx is off, just like Gxx is with Exx/Fxx
2902 			Gx values: 1, 4, 8, 16, 32, 64, 96, 128, 255
2903 			Ax/Bx/Cx/Dx values are used directly (i.e. D9 == D09), and are NOT shared with Dxx
2904 			(value is stored into nOldVolParam and used by A0/B0/C0/D0)
2905 			Hx uses the same value as Hxx and Uxx, and affects the *depth*
2906 			so... hxx = (hx | (oldhxx & 0xf0))  ???
2907 			TODO is this done correctly?
2908 		*/
2909 		bool doVolumeColumn = m_PlayState.m_nTickCount >= nStartTick;
2910 		// FT2 compatibility: If there's a note delay, volume column effects are NOT executed
2911 		// on the first tick and, if there's an instrument number, on the delayed tick.
2912 		// Test case: VolColDelay.xm, PortaDelay.xm
2913 		if(m_playBehaviour[kFT2VolColDelay] && nStartTick != 0)
2914 		{
2915 			doVolumeColumn = m_PlayState.m_nTickCount != 0 && (m_PlayState.m_nTickCount != nStartTick || (chn.rowCommand.instr == 0 && volcmd != VOLCMD_TONEPORTAMENTO));
2916 		}
2917 		if(volcmd > VOLCMD_PANNING && doVolumeColumn)
2918 		{
2919 			if(volcmd == VOLCMD_TONEPORTAMENTO)
2920 			{
2921 				const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, nStartTick);
2922 				if(clearEffectCommand)
2923 					cmd = CMD_NONE;
2924 
2925 				TonePortamento(chn, porta);
2926 			} else
2927 			{
2928 				// FT2 Compatibility: FT2 ignores some volume commands with parameter = 0.
2929 				if(m_playBehaviour[kFT2VolColMemory] && vol == 0)
2930 				{
2931 					switch(volcmd)
2932 					{
2933 					case VOLCMD_VOLUME:
2934 					case VOLCMD_PANNING:
2935 					case VOLCMD_VIBRATODEPTH:
2936 						break;
2937 					case VOLCMD_PANSLIDELEFT:
2938 						// FT2 Compatibility: Pan slide left with zero parameter causes panning to be set to full left on every non-row tick.
2939 						// Test case: PanSlideZero.xm
2940 						if(!m_SongFlags[SONG_FIRSTTICK])
2941 						{
2942 							chn.nPan = 0;
2943 						}
2944 						[[fallthrough]];
2945 					default:
2946 						// no memory here.
2947 						volcmd = VOLCMD_NONE;
2948 					}
2949 
2950 				} else if(!m_playBehaviour[kITVolColMemory])
2951 				{
2952 					// IT Compatibility: Effects in the volume column don't have an unified memory.
2953 					// Test case: VolColMemory.it
2954 					if(vol) chn.nOldVolParam = static_cast<ModCommand::PARAM>(vol); else vol = chn.nOldVolParam;
2955 				}
2956 
2957 				switch(volcmd)
2958 				{
2959 				case VOLCMD_VOLSLIDEUP:
2960 				case VOLCMD_VOLSLIDEDOWN:
2961 					// IT Compatibility: Volume column volume slides have their own memory
2962 					// Test case: VolColMemory.it
2963 					if(vol == 0 && m_playBehaviour[kITVolColMemory])
2964 					{
2965 						vol = chn.nOldVolParam;
2966 						if(vol == 0)
2967 							break;
2968 					} else
2969 					{
2970 						chn.nOldVolParam = static_cast<ModCommand::PARAM>(vol);
2971 					}
2972 					VolumeSlide(chn, static_cast<ModCommand::PARAM>(volcmd == VOLCMD_VOLSLIDEUP ? (vol << 4) : vol));
2973 					break;
2974 
2975 				case VOLCMD_FINEVOLUP:
2976 					// IT Compatibility: Fine volume slides in the volume column are only executed on the first tick, not on multiples of the first tick in case of pattern delay
2977 					// Test case: FineVolColSlide.it
2978 					if(m_PlayState.m_nTickCount == nStartTick || !m_playBehaviour[kITVolColMemory])
2979 					{
2980 						// IT Compatibility: Volume column volume slides have their own memory
2981 						// Test case: VolColMemory.it
2982 						FineVolumeUp(chn, static_cast<ModCommand::PARAM>(vol), m_playBehaviour[kITVolColMemory]);
2983 					}
2984 					break;
2985 
2986 				case VOLCMD_FINEVOLDOWN:
2987 					// IT Compatibility: Fine volume slides in the volume column are only executed on the first tick, not on multiples of the first tick in case of pattern delay
2988 					// Test case: FineVolColSlide.it
2989 					if(m_PlayState.m_nTickCount == nStartTick || !m_playBehaviour[kITVolColMemory])
2990 					{
2991 						// IT Compatibility: Volume column volume slides have their own memory
2992 						// Test case: VolColMemory.it
2993 						FineVolumeDown(chn, static_cast<ModCommand::PARAM>(vol), m_playBehaviour[kITVolColMemory]);
2994 					}
2995 					break;
2996 
2997 				case VOLCMD_VIBRATOSPEED:
2998 					// FT2 does not automatically enable vibrato with the "set vibrato speed" command
2999 					if(m_playBehaviour[kFT2VolColVibrato])
3000 						chn.nVibratoSpeed = vol & 0x0F;
3001 					else
3002 						Vibrato(chn, vol << 4);
3003 					break;
3004 
3005 				case VOLCMD_VIBRATODEPTH:
3006 					Vibrato(chn, vol);
3007 					break;
3008 
3009 				case VOLCMD_PANSLIDELEFT:
3010 					PanningSlide(chn, static_cast<ModCommand::PARAM>(vol), !m_playBehaviour[kFT2VolColMemory]);
3011 					break;
3012 
3013 				case VOLCMD_PANSLIDERIGHT:
3014 					PanningSlide(chn, static_cast<ModCommand::PARAM>(vol << 4), !m_playBehaviour[kFT2VolColMemory]);
3015 					break;
3016 
3017 				case VOLCMD_PORTAUP:
3018 					// IT compatibility (one of the first testcases - link effect memory)
3019 					PortamentoUp(nChn, static_cast<ModCommand::PARAM>(vol << 2), m_playBehaviour[kITVolColFinePortamento]);
3020 					break;
3021 
3022 				case VOLCMD_PORTADOWN:
3023 					// IT compatibility (one of the first testcases - link effect memory)
3024 					PortamentoDown(nChn, static_cast<ModCommand::PARAM>(vol << 2), m_playBehaviour[kITVolColFinePortamento]);
3025 					break;
3026 
3027 				case VOLCMD_OFFSET:
3028 					if(triggerNote && chn.pModSample && vol <= std::size(chn.pModSample->cues))
3029 					{
3030 						SmpLength offset;
3031 						if(vol == 0)
3032 							offset = chn.oldOffset;
3033 						else
3034 							offset = chn.oldOffset = chn.pModSample->cues[vol - 1];
3035 						SampleOffset(chn, offset);
3036 					}
3037 					break;
3038 
3039 				case VOLCMD_PLAYCONTROL:
3040 					if(vol <= 1)
3041 						chn.isPaused = (vol == 0);
3042 					break;
3043 				}
3044 			}
3045 		}
3046 
3047 		// Effects
3048 		if(cmd != CMD_NONE) switch (cmd)
3049 		{
3050 		// Set Volume
3051 		case CMD_VOLUME:
3052 			if(m_SongFlags[SONG_FIRSTTICK])
3053 			{
3054 				chn.nVolume = (param < 64) ? param * 4 : 256;
3055 				chn.dwFlags.set(CHN_FASTVOLRAMP);
3056 			}
3057 			break;
3058 
3059 		// Portamento Up
3060 		case CMD_PORTAMENTOUP:
3061 			if ((!param) && (GetType() & MOD_TYPE_MOD)) break;
3062 			PortamentoUp(nChn, static_cast<ModCommand::PARAM>(param));
3063 			break;
3064 
3065 		// Portamento Down
3066 		case CMD_PORTAMENTODOWN:
3067 			if ((!param) && (GetType() & MOD_TYPE_MOD)) break;
3068 			PortamentoDown(nChn, static_cast<ModCommand::PARAM>(param));
3069 			break;
3070 
3071 		// Volume Slide
3072 		case CMD_VOLUMESLIDE:
3073 			if (param || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast<ModCommand::PARAM>(param));
3074 			break;
3075 
3076 		// Tone-Portamento
3077 		case CMD_TONEPORTAMENTO:
3078 			TonePortamento(chn, static_cast<uint16>(param));
3079 			break;
3080 
3081 		// Tone-Portamento + Volume Slide
3082 		case CMD_TONEPORTAVOL:
3083 			if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast<ModCommand::PARAM>(param));
3084 			TonePortamento(chn, 0);
3085 			break;
3086 
3087 		// Vibrato
3088 		case CMD_VIBRATO:
3089 			Vibrato(chn, param);
3090 			break;
3091 
3092 		// Vibrato + Volume Slide
3093 		case CMD_VIBRATOVOL:
3094 			if ((param) || (GetType() != MOD_TYPE_MOD)) VolumeSlide(chn, static_cast<ModCommand::PARAM>(param));
3095 			Vibrato(chn, 0);
3096 			break;
3097 
3098 		// Set Speed
3099 		case CMD_SPEED:
3100 			if(m_SongFlags[SONG_FIRSTTICK])
3101 				SetSpeed(m_PlayState, param);
3102 			break;
3103 
3104 		// Set Tempo
3105 		case CMD_TEMPO:
3106 			if(m_playBehaviour[kMODVBlankTiming])
3107 			{
3108 				// ProTracker MODs with VBlank timing: All Fxx parameters set the tick count.
3109 				if(m_SongFlags[SONG_FIRSTTICK] && param != 0) SetSpeed(m_PlayState, param);
3110 				break;
3111 			}
3112 			{
3113 				param = CalculateXParam(m_PlayState.m_nPattern, m_PlayState.m_nRow, nChn);
3114 				if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))
3115 				{
3116 					if (param) chn.nOldTempo = static_cast<ModCommand::PARAM>(param); else param = chn.nOldTempo;
3117 				}
3118 				TEMPO t(param, 0);
3119 				LimitMax(t, GetModSpecifications().GetTempoMax());
3120 				SetTempo(t);
3121 			}
3122 			break;
3123 
3124 		// Set Offset
3125 		case CMD_OFFSET:
3126 			if(triggerNote)
3127 			{
3128 				// FT2 compatibility: Portamento + Offset = Ignore offset
3129 				// Test case: porta-offset.xm
3130 				if(bPorta && GetType() == MOD_TYPE_XM)
3131 					break;
3132 
3133 				ProcessSampleOffset(chn, nChn, m_PlayState);
3134 			}
3135 			break;
3136 
3137 		// Disorder Tracker 2 percentage offset
3138 		case CMD_OFFSETPERCENTAGE:
3139 			if(triggerNote)
3140 			{
3141 				SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, param, 256));
3142 			}
3143 			break;
3144 
3145 		// Arpeggio
3146 		case CMD_ARPEGGIO:
3147 			// IT compatibility 01. Don't ignore Arpeggio if no note is playing (also valid for ST3)
3148 			if(m_PlayState.m_nTickCount) break;
3149 			if((!chn.nPeriod || !chn.nNote)
3150 				&& (chn.pModInstrument == nullptr || !chn.pModInstrument->HasValidMIDIChannel())	// Plugin arpeggio
3151 				&& !m_playBehaviour[kITArpeggio] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) break;
3152 			if (!param && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD))) break;	// Only important when editing MOD/XM files (000 effects are removed when loading files where this means "no effect")
3153 			chn.nCommand = CMD_ARPEGGIO;
3154 			if (param) chn.nArpeggio = static_cast<ModCommand::PARAM>(param);
3155 			break;
3156 
3157 		// Retrig
3158 		case CMD_RETRIG:
3159 			if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))
3160 			{
3161 				if (!(param & 0xF0)) param |= chn.nRetrigParam & 0xF0;
3162 				if (!(param & 0x0F)) param |= chn.nRetrigParam & 0x0F;
3163 				param |= 0x100; // increment retrig count on first row
3164 			}
3165 			// IT compatibility 15. Retrigger
3166 			if(m_playBehaviour[kITRetrigger])
3167 			{
3168 				if (param) chn.nRetrigParam = static_cast<uint8>(param & 0xFF);
3169 				RetrigNote(nChn, chn.nRetrigParam, (volcmd == VOLCMD_OFFSET) ? vol + 1 : 0);
3170 			} else
3171 			{
3172 				// XM Retrig
3173 				if (param) chn.nRetrigParam = static_cast<uint8>(param & 0xFF); else param = chn.nRetrigParam;
3174 				RetrigNote(nChn, param, (volcmd == VOLCMD_OFFSET) ? vol + 1 : 0);
3175 			}
3176 			break;
3177 
3178 		// Tremor
3179 		case CMD_TREMOR:
3180 			if(!m_SongFlags[SONG_FIRSTTICK])
3181 			{
3182 				break;
3183 			}
3184 
3185 			// IT compatibility 12. / 13. Tremor (using modified DUMB's Tremor logic here because of old effects - http://dumb.sf.net/)
3186 			if(m_playBehaviour[kITTremor])
3187 			{
3188 				if(param && !m_SongFlags[SONG_ITOLDEFFECTS])
3189 				{
3190 					// Old effects have different length interpretation (+1 for both on and off)
3191 					if(param & 0xF0)
3192 						param -= 0x10;
3193 					if(param & 0x0F)
3194 						param -= 0x01;
3195 					chn.nTremorParam = static_cast<ModCommand::PARAM>(param);
3196 				}
3197 				chn.nTremorCount |= 0x80; // set on/off flag
3198 			} else if(m_playBehaviour[kFT2Tremor])
3199 			{
3200 				// XM Tremor. Logic is being processed in sndmix.cpp
3201 				chn.nTremorCount |= 0x80; // set on/off flag
3202 			}
3203 
3204 			chn.nCommand = CMD_TREMOR;
3205 			if(param)
3206 				chn.nTremorParam = static_cast<ModCommand::PARAM>(param);
3207 
3208 			break;
3209 
3210 		// Set Global Volume
3211 		case CMD_GLOBALVOLUME:
3212 			// IT compatibility: Only apply global volume on first tick (and multiples)
3213 			// Test case: GlobalVolFirstTick.it
3214 			if(!m_SongFlags[SONG_FIRSTTICK])
3215 				break;
3216 			// ST3 applies global volume on tick 1 and does other weird things, but we won't emulate this for now.
3217 // 			if(((GetType() & MOD_TYPE_S3M) && m_nTickCount != 1)
3218 // 				|| (!(GetType() & MOD_TYPE_S3M) && !m_SongFlags[SONG_FIRSTTICK]))
3219 // 			{
3220 // 				break;
3221 // 			}
3222 
3223 			// FT2 compatibility: On channels that are "left" of the global volume command, the new global volume is not applied
3224 			// until the second tick of the row. Since we apply global volume on the mix buffer rather than note volumes, this
3225 			// cannot be fixed for now.
3226 			// Test case: GlobalVolume.xm
3227 // 			if(IsCompatibleMode(TRK_FASTTRACKER2) && m_SongFlags[SONG_FIRSTTICK] && m_nMusicSpeed > 1)
3228 // 			{
3229 // 				break;
3230 // 			}
3231 
3232 			if (!(GetType() & GLOBALVOL_7BIT_FORMATS)) param *= 2;
3233 
3234 			// IT compatibility 16. ST3 and IT ignore out-of-range values.
3235 			// Test case: globalvol-invalid.it
3236 			if(param <= 128)
3237 			{
3238 				m_PlayState.m_nGlobalVolume = param * 2;
3239 			} else if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M)))
3240 			{
3241 				m_PlayState.m_nGlobalVolume = 256;
3242 			}
3243 			break;
3244 
3245 		// Global Volume Slide
3246 		case CMD_GLOBALVOLSLIDE:
3247 			//IT compatibility 16. Saving last global volume slide param per channel (FT2/IT)
3248 			if(m_playBehaviour[kPerChannelGlobalVolSlide])
3249 				GlobalVolSlide(static_cast<ModCommand::PARAM>(param), chn.nOldGlobalVolSlide);
3250 			else
3251 				GlobalVolSlide(static_cast<ModCommand::PARAM>(param), m_PlayState.Chn[0].nOldGlobalVolSlide);
3252 			break;
3253 
3254 		// Set 8-bit Panning
3255 		case CMD_PANNING8:
3256 			if(m_SongFlags[SONG_FIRSTTICK])
3257 			{
3258 				Panning(chn, param, Pan8bit);
3259 			}
3260 			break;
3261 
3262 		// Panning Slide
3263 		case CMD_PANNINGSLIDE:
3264 			PanningSlide(chn, static_cast<ModCommand::PARAM>(param));
3265 			break;
3266 
3267 		// Tremolo
3268 		case CMD_TREMOLO:
3269 			Tremolo(chn, param);
3270 			break;
3271 
3272 		// Fine Vibrato
3273 		case CMD_FINEVIBRATO:
3274 			FineVibrato(chn, param);
3275 			break;
3276 
3277 		// MOD/XM Exx Extended Commands
3278 		case CMD_MODCMDEX:
3279 			ExtendedMODCommands(nChn, static_cast<ModCommand::PARAM>(param));
3280 			break;
3281 
3282 		// S3M/IT Sxx Extended Commands
3283 		case CMD_S3MCMDEX:
3284 			if(m_playBehaviour[kST3EffectMemory] && param == 0)
3285 			{
3286 				param = chn.nArpeggio;	// S00 uses the last non-zero effect parameter as memory, like other effects including Arpeggio, so we "borrow" our memory there.
3287 			}
3288 			ExtendedS3MCommands(nChn, static_cast<ModCommand::PARAM>(param));
3289 			break;
3290 
3291 		// Key Off
3292 		case CMD_KEYOFF:
3293 			// This is how Key Off is supposed to sound... (in FT2 at least)
3294 			if(m_playBehaviour[kFT2KeyOff])
3295 			{
3296 				if (m_PlayState.m_nTickCount == param)
3297 				{
3298 					// XM: Key-Off + Sample == Note Cut
3299 					if(chn.pModInstrument == nullptr || !chn.pModInstrument->VolEnv.dwFlags[ENV_ENABLED])
3300 					{
3301 						if(param == 0 && (chn.rowCommand.instr || chn.rowCommand.volcmd != VOLCMD_NONE)) // FT2 is weird....
3302 						{
3303 							chn.dwFlags.set(CHN_NOTEFADE);
3304 						}
3305 						else
3306 						{
3307 							chn.dwFlags.set(CHN_FASTVOLRAMP);
3308 							chn.nVolume = 0;
3309 						}
3310 					}
3311 					KeyOff(chn);
3312 				}
3313 			}
3314 			// This is how it's NOT supposed to sound...
3315 			else
3316 			{
3317 				if(m_SongFlags[SONG_FIRSTTICK])
3318 					KeyOff(chn);
3319 			}
3320 			break;
3321 
3322 		// Extra-fine porta up/down
3323 		case CMD_XFINEPORTAUPDOWN:
3324 			switch(param & 0xF0)
3325 			{
3326 			case 0x10: ExtraFinePortamentoUp(chn, param & 0x0F); break;
3327 			case 0x20: ExtraFinePortamentoDown(chn, param & 0x0F); break;
3328 			// ModPlug XM Extensions (ignore in compatible mode)
3329 			case 0x50:
3330 			case 0x60:
3331 			case 0x70:
3332 			case 0x90:
3333 			case 0xA0:
3334 				if(!m_playBehaviour[kFT2RestrictXCommand]) ExtendedS3MCommands(nChn, static_cast<ModCommand::PARAM>(param));
3335 				break;
3336 			}
3337 			break;
3338 
3339 		case CMD_FINETUNE:
3340 		case CMD_FINETUNE_SMOOTH:
3341 			if(m_SongFlags[SONG_FIRSTTICK] || cmd == CMD_FINETUNE_SMOOTH)
3342 			{
3343 				SetFinetune(nChn, m_PlayState, cmd == CMD_FINETUNE_SMOOTH);
3344 #ifndef NO_PLUGINS
3345 				if(IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn); plugin != nullptr)
3346 					plugin->MidiPitchBendRaw(chn.GetMIDIPitchBend(), nChn);
3347 #endif  // NO_PLUGINS
3348 			}
3349 			break;
3350 
3351 		// Set Channel Global Volume
3352 		case CMD_CHANNELVOLUME:
3353 			if(!m_SongFlags[SONG_FIRSTTICK]) break;
3354 			if (param <= 64)
3355 			{
3356 				chn.nGlobalVol = param;
3357 				chn.dwFlags.set(CHN_FASTVOLRAMP);
3358 			}
3359 			break;
3360 
3361 		// Channel volume slide
3362 		case CMD_CHANNELVOLSLIDE:
3363 			ChannelVolSlide(chn, static_cast<ModCommand::PARAM>(param));
3364 			break;
3365 
3366 		// Panbrello (IT)
3367 		case CMD_PANBRELLO:
3368 			Panbrello(chn, param);
3369 			break;
3370 
3371 		// Set Envelope Position
3372 		case CMD_SETENVPOSITION:
3373 			if(m_SongFlags[SONG_FIRSTTICK])
3374 			{
3375 				chn.VolEnv.nEnvPosition = param;
3376 
3377 				// FT2 compatibility: FT2 only sets the position of the panning envelope if the volume envelope's sustain flag is set
3378 				// Test case: SetEnvPos.xm
3379 				if(!m_playBehaviour[kFT2SetPanEnvPos] || chn.VolEnv.flags[ENV_SUSTAIN])
3380 				{
3381 					chn.PanEnv.nEnvPosition = param;
3382 					chn.PitchEnv.nEnvPosition = param;
3383 				}
3384 
3385 			}
3386 			break;
3387 
3388 		// Position Jump
3389 		case CMD_POSITIONJUMP:
3390 			PositionJump(m_PlayState, nChn);
3391 			break;
3392 
3393 		// Pattern Break
3394 		case CMD_PATTERNBREAK:
3395 			if(ROWINDEX row = PatternBreak(m_PlayState, nChn, static_cast<ModCommand::PARAM>(param)); row != ROWINDEX_INVALID)
3396 			{
3397 				m_PlayState.m_breakRow = row;
3398 				if(m_SongFlags[SONG_PATTERNLOOP])
3399 				{
3400 					//If song is set to loop and a pattern break occurs we should stay on the same pattern.
3401 					//Use nPosJump to force playback to "jump to this pattern" rather than move to next, as by default.
3402 					m_PlayState.m_posJump = m_PlayState.m_nCurrentOrder;
3403 				}
3404 			}
3405 			break;
3406 
3407 		// IMF / PTM Note Slides
3408 		case CMD_NOTESLIDEUP:
3409 		case CMD_NOTESLIDEDOWN:
3410 		case CMD_NOTESLIDEUPRETRIG:
3411 		case CMD_NOTESLIDEDOWNRETRIG:
3412 			// Note that this command seems to be a bit buggy in Polytracker... Luckily, no tune seems to seriously use this
3413 			// (Vic uses it e.g. in Spaceman or Perfect Reason to slide effect samples, noone will notice the difference :)
3414 			NoteSlide(chn, param, cmd == CMD_NOTESLIDEUP || cmd == CMD_NOTESLIDEUPRETRIG, cmd == CMD_NOTESLIDEUPRETRIG || cmd == CMD_NOTESLIDEDOWNRETRIG);
3415 			break;
3416 
3417 		// PTM Reverse sample + offset (executed on every tick)
3418 		case CMD_REVERSEOFFSET:
3419 			ReverseSampleOffset(chn, static_cast<ModCommand::PARAM>(param));
3420 			break;
3421 
3422 #ifndef NO_PLUGINS
3423 		// DBM: Toggle DSP Echo
3424 		case CMD_DBMECHO:
3425 			if(m_PlayState.m_nTickCount == 0)
3426 			{
3427 				uint32 echoType = (param >> 4), enable = (param & 0x0F);
3428 				if(echoType > 2 || enable > 1)
3429 				{
3430 					break;
3431 				}
3432 				CHANNELINDEX firstChn = nChn, lastChn = nChn;
3433 				if(echoType == 1)
3434 				{
3435 					firstChn = 0;
3436 					lastChn = m_nChannels - 1;
3437 				}
3438 				for(CHANNELINDEX c = firstChn; c <= lastChn; c++)
3439 				{
3440 					ChnSettings[c].dwFlags.set(CHN_NOFX, enable == 1);
3441 					m_PlayState.Chn[c].dwFlags.set(CHN_NOFX, enable == 1);
3442 				}
3443 			}
3444 			break;
3445 #endif // NO_PLUGINS
3446 		}
3447 
3448 		if(m_playBehaviour[kST3EffectMemory] && param != 0)
3449 		{
3450 			UpdateS3MEffectMemory(chn, static_cast<ModCommand::PARAM>(param));
3451 		}
3452 
3453 		if(chn.rowCommand.instr)
3454 		{
3455 			// Not necessarily consistent with actually playing instrument for IT compatibility
3456 			chn.nOldIns = chn.rowCommand.instr;
3457 		}
3458 
3459 	} // for(...) end
3460 
3461 	// Navigation Effects
3462 	if(m_SongFlags[SONG_FIRSTTICK])
3463 	{
3464 		if(HandleNextRow(m_PlayState, Order(), true))
3465 			m_SongFlags.set(SONG_BREAKTOROW);
3466 	}
3467 	return true;
3468 }
3469 
3470 
HandleNextRow(PlayState & state,const ModSequence & order,bool honorPatternLoop) const3471 bool CSoundFile::HandleNextRow(PlayState &state, const ModSequence &order, bool honorPatternLoop) const
3472 {
3473 	const bool doPatternLoop = (state.m_patLoopRow != ROWINDEX_INVALID);
3474 	const bool doBreakRow = (state.m_breakRow != ROWINDEX_INVALID);
3475 	const bool doPosJump = (state.m_posJump != ORDERINDEX_INVALID);
3476 	bool breakToRow = false;
3477 
3478 	// Pattern Break / Position Jump only if no loop running
3479 	// Exception: FastTracker 2 in all cases, Impulse Tracker in case of position jump
3480 	// Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm
3481 	// Test case for IT: exception: LoopBreak.it, sbx-priority.it
3482 	if((doBreakRow || doPosJump)
3483 	   && (!doPatternLoop
3484 	       || m_playBehaviour[kFT2PatternLoopWithJumps]
3485 	       || (m_playBehaviour[kITPatternLoopWithJumps] && doPosJump)
3486 	       || (m_playBehaviour[kITPatternLoopWithJumpsOld] && doPosJump)))
3487 	{
3488 		if(!doPosJump)
3489 			state.m_posJump = state.m_nCurrentOrder + 1;
3490 		if(!doBreakRow)
3491 			state.m_breakRow = 0;
3492 		breakToRow = true;
3493 
3494 		if(state.m_posJump >= order.size())
3495 			state.m_posJump = order.GetRestartPos();
3496 
3497 		// IT / FT2 compatibility: don't reset loop count on pattern break.
3498 		// Test case: gm-trippy01.it, PatLoop-Break.xm, PatLoop-Weird.xm, PatLoop-Break.mod
3499 		if(state.m_posJump != state.m_nCurrentOrder
3500 		   && !m_playBehaviour[kITPatternLoopBreak] && !m_playBehaviour[kFT2PatternLoopWithJumps] && GetType() != MOD_TYPE_MOD)
3501 		{
3502 			for(CHANNELINDEX i = 0; i < GetNumChannels(); i++)
3503 			{
3504 				state.Chn[i].nPatternLoopCount = 0;
3505 			}
3506 		}
3507 
3508 		state.m_nNextRow = state.m_breakRow;
3509 		if(!honorPatternLoop || !m_SongFlags[SONG_PATTERNLOOP])
3510 			state.m_nNextOrder = state.m_posJump;
3511 	} else if(doPatternLoop)
3512 	{
3513 		// Pattern Loop
3514 		state.m_nNextOrder = state.m_nCurrentOrder;
3515 		state.m_nNextRow = state.m_patLoopRow;
3516 		// FT2 skips the first row of the pattern loop if there's a pattern delay, ProTracker sometimes does it too (didn't quite figure it out yet).
3517 		// But IT and ST3 don't do this.
3518 		// Test cases: PatLoopWithDelay.it, PatLoopWithDelay.s3m
3519 		if(state.m_nPatternDelay
3520 		   && (GetType() != MOD_TYPE_IT || !m_playBehaviour[kITPatternLoopWithJumps])
3521 		   && GetType() != MOD_TYPE_S3M)
3522 		{
3523 			state.m_nNextRow++;
3524 		}
3525 
3526 		// IT Compatibility: If the restart row is past the end of the current pattern
3527 		// (e.g. when continued from a previous pattern without explicit SB0 effect), continue the next pattern.
3528 		// Test case: LoopStartAfterPatternEnd.it
3529 		if(state.m_patLoopRow >= Patterns[state.m_nPattern].GetNumRows())
3530 		{
3531 			state.m_nNextOrder++;
3532 			state.m_nNextRow = 0;
3533 		}
3534 	}
3535 
3536 	return breakToRow;
3537 }
3538 
3539 
3540 ////////////////////////////////////////////////////////////
3541 // Channels effects
3542 
3543 
3544 // Update the effect memory of all S3M effects that use the last non-zero effect parameter as memory (Dxy, Exx, Fxx, Ixy, Jxy, Kxy, Lxy, Qxy, Rxy, Sxy)
3545 // Test case: ParamMemory.s3m
UpdateS3MEffectMemory(ModChannel & chn,ModCommand::PARAM param) const3546 void CSoundFile::UpdateS3MEffectMemory(ModChannel &chn, ModCommand::PARAM param) const
3547 {
3548 	chn.nOldVolumeSlide = param; // Dxy / Kxy / Lxy
3549 	chn.nOldPortaUp = param;     // Exx / Fxx
3550 	chn.nOldPortaDown = param;   // Exx / Fxx
3551 	chn.nTremorParam = param;    // Ixy
3552 	chn.nArpeggio = param;       // Jxy
3553 	chn.nRetrigParam = param;    // Qxy
3554 	chn.nTremoloDepth = (param & 0x0F) << 2; // Rxy
3555 	chn.nTremoloSpeed = (param >> 4) & 0x0F; // Rxy
3556 	// Sxy is not handled here.
3557 }
3558 
3559 
3560 // Calculate full parameter for effects that support parameter extension at the given pattern location.
3561 // maxCommands sets the maximum number of XParam commands to look at for this effect
3562 // extendedRows returns how many extended rows are used (i.e. a value of 0 means the command is not extended).
CalculateXParam(PATTERNINDEX pat,ROWINDEX row,CHANNELINDEX chn,uint32 * extendedRows) const3563 uint32 CSoundFile::CalculateXParam(PATTERNINDEX pat, ROWINDEX row, CHANNELINDEX chn, uint32 *extendedRows) const
3564 {
3565 	if(extendedRows != nullptr)
3566 		*extendedRows = 0;
3567 	if(!Patterns.IsValidPat(pat))
3568 	{
3569 #ifdef MPT_BUILD_FUZZER
3570 		// Ending up in this situation implies a logic error
3571 		std::abort();
3572 #else
3573 		return 0;
3574 #endif
3575 	}
3576 	ROWINDEX maxCommands = 4;
3577 	const ModCommand *m = Patterns[pat].GetpModCommand(row, chn);
3578 	const auto startCmd = m->command;
3579 	uint32 val = m->param;
3580 
3581 	switch(m->command)
3582 	{
3583 	case CMD_OFFSET:
3584 		// 24 bit command
3585 		maxCommands = 2;
3586 		break;
3587 	case CMD_TEMPO:
3588 	case CMD_PATTERNBREAK:
3589 	case CMD_POSITIONJUMP:
3590 	case CMD_FINETUNE:
3591 	case CMD_FINETUNE_SMOOTH:
3592 		// 16 bit command
3593 		maxCommands = 1;
3594 		break;
3595 	default:
3596 		return val;
3597 	}
3598 
3599 	const bool xmTempoFix = m->command == CMD_TEMPO && GetType() == MOD_TYPE_XM;
3600 	ROWINDEX numRows = std::min(Patterns[pat].GetNumRows() - row - 1, maxCommands);
3601 	uint32 extRows = 0;
3602 	while(numRows > 0)
3603 	{
3604 		m += Patterns[pat].GetNumChannels();
3605 		if(m->command != CMD_XPARAM)
3606 			break;
3607 
3608 		if(xmTempoFix && val < 256)
3609 		{
3610 			// With XM, 0x20 is the lowest tempo. Anything below changes ticks per row.
3611 			val -= 0x20;
3612 		}
3613 		val = (val << 8) | m->param;
3614 		numRows--;
3615 		extRows++;
3616 	}
3617 
3618 	// Always return a full-precision value for finetune
3619 	if((startCmd == CMD_FINETUNE || startCmd == CMD_FINETUNE_SMOOTH) && !extRows)
3620 		val <<= 8;
3621 
3622 	if(extendedRows != nullptr)
3623 		*extendedRows = extRows;
3624 
3625 	return val;
3626 }
3627 
3628 
PositionJump(PlayState & state,CHANNELINDEX chn) const3629 void CSoundFile::PositionJump(PlayState &state, CHANNELINDEX chn) const
3630 {
3631 	state.m_nextPatStartRow = 0;  // FT2 E60 bug
3632 	state.m_posJump = static_cast<ORDERINDEX>(CalculateXParam(state.m_nPattern, state.m_nRow, chn));
3633 
3634 	// see https://forum.openmpt.org/index.php?topic=2769.0 - FastTracker resets Dxx if Bxx is called _after_ Dxx
3635 	// Test case: PatternJump.mod
3636 	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM)) && state.m_breakRow != ROWINDEX_INVALID)
3637 	{
3638 		state.m_breakRow = 0;
3639 	}
3640 }
3641 
3642 
PatternBreak(PlayState & state,CHANNELINDEX chn,uint8 param) const3643 ROWINDEX CSoundFile::PatternBreak(PlayState &state, CHANNELINDEX chn, uint8 param) const
3644 {
3645 	if(param >= 64 && (GetType() & MOD_TYPE_S3M))
3646 	{
3647 		// ST3 ignores invalid pattern breaks.
3648 		return ROWINDEX_INVALID;
3649 	}
3650 
3651 	state.m_nextPatStartRow = 0; // FT2 E60 bug
3652 
3653 	return static_cast<ROWINDEX>(CalculateXParam(state.m_nPattern, state.m_nRow, chn));
3654 }
3655 
3656 
PortamentoUp(CHANNELINDEX nChn,ModCommand::PARAM param,const bool doFinePortamentoAsRegular)3657 void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular)
3658 {
3659 	ModChannel &chn = m_PlayState.Chn[nChn];
3660 
3661 	if(param)
3662 	{
3663 		// FT2 compatibility: Separate effect memory for all portamento commands
3664 		// Test case: Porta-LinkMem.xm
3665 		if(!m_playBehaviour[kFT2PortaUpDownMemory])
3666 			chn.nOldPortaDown = param;
3667 		chn.nOldPortaUp = param;
3668 	} else
3669 	{
3670 		param = chn.nOldPortaUp;
3671 	}
3672 
3673 	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM));
3674 
3675 	// Process MIDI pitch bend for instrument plugins
3676 	MidiPortamento(nChn, param, doFineSlides);
3677 
3678 	if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning)
3679 	{
3680 		// Portamento for instruments with custom tuning
3681 		if(param >= 0xF0 && !doFinePortamentoAsRegular)
3682 			PortamentoFineMPT(chn, param - 0xF0);
3683 		else if(param >= 0xE0 && !doFinePortamentoAsRegular)
3684 			PortamentoExtraFineMPT(chn, param - 0xE0);
3685 		else
3686 			PortamentoMPT(chn, param);
3687 		return;
3688 	} else if(GetType() == MOD_TYPE_PLM)
3689 	{
3690 		// A normal portamento up or down makes a follow-up tone portamento go the same direction.
3691 		chn.nPortamentoDest = 1;
3692 	}
3693 
3694 	if (doFineSlides && param >= 0xE0)
3695 	{
3696 		if (param & 0x0F)
3697 		{
3698 			if ((param & 0xF0) == 0xF0)
3699 			{
3700 				FinePortamentoUp(chn, param & 0x0F);
3701 				return;
3702 			} else if ((param & 0xF0) == 0xE0 && GetType() != MOD_TYPE_DBM)
3703 			{
3704 				ExtraFinePortamentoUp(chn, param & 0x0F);
3705 				return;
3706 			}
3707 		}
3708 		if(GetType() != MOD_TYPE_DBM)
3709 		{
3710 			// DBM only has fine slides, no extra-fine slides.
3711 			return;
3712 		}
3713 	}
3714 	// Regular Slide
3715 	if(!chn.isFirstTick
3716 	   || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1])
3717 	   || (GetType() & (MOD_TYPE_669 | MOD_TYPE_OKT))
3718 	   || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]))
3719 	{
3720 		DoFreqSlide(chn, chn.nPeriod, param * 4);
3721 	}
3722 }
3723 
3724 
PortamentoDown(CHANNELINDEX nChn,ModCommand::PARAM param,const bool doFinePortamentoAsRegular)3725 void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, const bool doFinePortamentoAsRegular)
3726 {
3727 	ModChannel &chn = m_PlayState.Chn[nChn];
3728 
3729 	if(param)
3730 	{
3731 		// FT2 compatibility: Separate effect memory for all portamento commands
3732 		// Test case: Porta-LinkMem.xm
3733 		if(!m_playBehaviour[kFT2PortaUpDownMemory])
3734 			chn.nOldPortaUp = param;
3735 		chn.nOldPortaDown = param;
3736 	} else
3737 	{
3738 		param = chn.nOldPortaDown;
3739 	}
3740 
3741 	const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM));
3742 
3743 	// Process MIDI pitch bend for instrument plugins
3744 	MidiPortamento(nChn, -static_cast<int>(param), doFineSlides);
3745 
3746 	if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning)
3747 	{
3748 		// Portamento for instruments with custom tuning
3749 		if(param >= 0xF0 && !doFinePortamentoAsRegular)
3750 			PortamentoFineMPT(chn, -static_cast<int>(param - 0xF0));
3751 		else if(param >= 0xE0 && !doFinePortamentoAsRegular)
3752 			PortamentoExtraFineMPT(chn, -static_cast<int>(param - 0xE0));
3753 		else
3754 			PortamentoMPT(chn, -static_cast<int>(param));
3755 		return;
3756 	} else if(GetType() == MOD_TYPE_PLM)
3757 	{
3758 		// A normal portamento up or down makes a follow-up tone portamento go the same direction.
3759 		chn.nPortamentoDest = 65535;
3760 	}
3761 
3762 	if(doFineSlides && param >= 0xE0)
3763 	{
3764 		if (param & 0x0F)
3765 		{
3766 			if ((param & 0xF0) == 0xF0)
3767 			{
3768 				FinePortamentoDown(chn, param & 0x0F);
3769 				return;
3770 			} else if ((param & 0xF0) == 0xE0 && GetType() != MOD_TYPE_DBM)
3771 			{
3772 				ExtraFinePortamentoDown(chn, param & 0x0F);
3773 				return;
3774 			}
3775 		}
3776 		if(GetType() != MOD_TYPE_DBM)
3777 		{
3778 			// DBM only has fine slides, no extra-fine slides.
3779 			return;
3780 		}
3781 	}
3782 
3783 	if(!chn.isFirstTick
3784 	   || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1])
3785 	   || (GetType() & (MOD_TYPE_669 | MOD_TYPE_OKT))
3786 	   || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]))
3787 	{
3788 		DoFreqSlide(chn, chn.nPeriod, param * -4);
3789 	}
3790 }
3791 
3792 
3793 // Send portamento commands to plugins
MidiPortamento(CHANNELINDEX nChn,int param,bool doFineSlides)3794 void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides)
3795 {
3796 	int actualParam = std::abs(param);
3797 	int pitchBend = 0;
3798 
3799 	// Old MIDI Pitch Bends:
3800 	// - Applied on every tick
3801 	// - No fine pitch slides (they are interpreted as normal slides)
3802 	// New MIDI Pitch Bends:
3803 	// - Behaviour identical to sample pitch bends if the instrument's PWD parameter corresponds to the actual VSTi setting.
3804 
3805 	if(doFineSlides && actualParam >= 0xE0 && !m_playBehaviour[kOldMIDIPitchBends])
3806 	{
3807 		if(m_PlayState.Chn[nChn].isFirstTick)
3808 		{
3809 			// Extra fine slide...
3810 			pitchBend = (actualParam & 0x0F) * mpt::signum(param);
3811 			if(actualParam >= 0xF0)
3812 			{
3813 				// ... or just a fine slide!
3814 				pitchBend *= 4;
3815 			}
3816 		}
3817 	} else if(!m_PlayState.Chn[nChn].isFirstTick || m_playBehaviour[kOldMIDIPitchBends])
3818 	{
3819 		// Regular slide
3820 		pitchBend = param * 4;
3821 	}
3822 
3823 	if(pitchBend)
3824 	{
3825 #ifndef NO_PLUGINS
3826 		IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn);
3827 		if(plugin != nullptr)
3828 		{
3829 			int8 pwd = 13;	// Early OpenMPT legacy... Actually it's not *exactly* 13, but close enough...
3830 			if(m_PlayState.Chn[nChn].pModInstrument != nullptr)
3831 			{
3832 				pwd = m_PlayState.Chn[nChn].pModInstrument->midiPWD;
3833 			}
3834 			plugin->MidiPitchBend(pitchBend, pwd, nChn);
3835 		}
3836 #endif // NO_PLUGINS
3837 	}
3838 }
3839 
3840 
FinePortamentoUp(ModChannel & chn,ModCommand::PARAM param) const3841 void CSoundFile::FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const
3842 {
3843 	MPT_ASSERT(!chn.HasCustomTuning());
3844 	if(GetType() == MOD_TYPE_XM)
3845 	{
3846 		// FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
3847 		// Test case: Porta-LinkMem.xm
3848 		if(param) chn.nOldFinePortaUpDown = (chn.nOldFinePortaUpDown & 0x0F) | (param << 4); else param = (chn.nOldFinePortaUpDown >> 4);
3849 	} else if(GetType() == MOD_TYPE_MT2)
3850 	{
3851 		if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown;
3852 	}
3853 
3854 	if(chn.isFirstTick && chn.nPeriod && param)
3855 		DoFreqSlide(chn, chn.nPeriod, param * 4);
3856 }
3857 
3858 
FinePortamentoDown(ModChannel & chn,ModCommand::PARAM param) const3859 void CSoundFile::FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const
3860 {
3861 	MPT_ASSERT(!chn.HasCustomTuning());
3862 	if(GetType() == MOD_TYPE_XM)
3863 	{
3864 		// FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
3865 		// Test case: Porta-LinkMem.xm
3866 		if(param) chn.nOldFinePortaUpDown = (chn.nOldFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (chn.nOldFinePortaUpDown & 0x0F);
3867 	} else if(GetType() == MOD_TYPE_MT2)
3868 	{
3869 		if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown;
3870 	}
3871 
3872 	if(chn.isFirstTick && chn.nPeriod && param)
3873 	{
3874 		DoFreqSlide(chn, chn.nPeriod, param * -4);
3875 		if(chn.nPeriod > 0xFFFF && !m_playBehaviour[kPeriodsAreHertz] && (!m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_XM))
3876 			chn.nPeriod = 0xFFFF;
3877 	}
3878 }
3879 
3880 
ExtraFinePortamentoUp(ModChannel & chn,ModCommand::PARAM param) const3881 void CSoundFile::ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const
3882 {
3883 	MPT_ASSERT(!chn.HasCustomTuning());
3884 	if(GetType() == MOD_TYPE_XM)
3885 	{
3886 		// FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
3887 		// Test case: Porta-LinkMem.xm
3888 		if(param) chn.nOldExtraFinePortaUpDown = (chn.nOldExtraFinePortaUpDown & 0x0F) | (param << 4); else param = (chn.nOldExtraFinePortaUpDown >> 4);
3889 	} else if(GetType() == MOD_TYPE_MT2)
3890 	{
3891 		if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown;
3892 	}
3893 
3894 	if(chn.isFirstTick && chn.nPeriod && param)
3895 		DoFreqSlide(chn, chn.nPeriod, param);
3896 }
3897 
3898 
ExtraFinePortamentoDown(ModChannel & chn,ModCommand::PARAM param) const3899 void CSoundFile::ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const
3900 {
3901 	MPT_ASSERT(!chn.HasCustomTuning());
3902 	if(GetType() == MOD_TYPE_XM)
3903 	{
3904 		// FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
3905 		// Test case: Porta-LinkMem.xm
3906 		if(param) chn.nOldExtraFinePortaUpDown = (chn.nOldExtraFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (chn.nOldExtraFinePortaUpDown & 0x0F);
3907 	} else if(GetType() == MOD_TYPE_MT2)
3908 	{
3909 		if(param) chn.nOldFinePortaUpDown = param; else param = chn.nOldFinePortaUpDown;
3910 	}
3911 
3912 	if(chn.isFirstTick && chn.nPeriod && param)
3913 	{
3914 		DoFreqSlide(chn, chn.nPeriod, -static_cast<int32>(param));
3915 		if(chn.nPeriod > 0xFFFF && !m_playBehaviour[kPeriodsAreHertz] && (!m_SongFlags[SONG_LINEARSLIDES] || GetType() == MOD_TYPE_XM))
3916 			chn.nPeriod = 0xFFFF;
3917 	}
3918 }
3919 
3920 
SetFinetune(CHANNELINDEX channel,PlayState & playState,bool isSmooth) const3921 void CSoundFile::SetFinetune(CHANNELINDEX channel, PlayState &playState, bool isSmooth) const
3922 {
3923 	ModChannel &chn = playState.Chn[channel];
3924 	int16 newTuning = mpt::saturate_cast<int16>(static_cast<int32>(CalculateXParam(playState.m_nPattern, playState.m_nRow, channel, nullptr)) - 0x8000);
3925 
3926 	if(isSmooth)
3927 	{
3928 		const int32 ticksLeft = playState.TicksOnRow() - playState.m_nTickCount;
3929 		if(ticksLeft > 1)
3930 		{
3931 			const int32 step = (newTuning - chn.microTuning) / ticksLeft;
3932 			newTuning = mpt::saturate_cast<int16>(chn.microTuning + step);
3933 		}
3934 	}
3935 	chn.microTuning = newTuning;
3936 }
3937 
3938 
3939 // Implemented for IMF / PTM / OKT compatibility, can't actually save this in any formats
3940 // Slide up / down every x ticks by y semitones
3941 // Oktalyzer: Slide down on first tick only, or on every tick
NoteSlide(ModChannel & chn,uint32 param,bool slideUp,bool retrig) const3942 void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const
3943 {
3944 	if(m_SongFlags[SONG_FIRSTTICK])
3945 	{
3946 		if(param & 0xF0)
3947 			chn.noteSlideParam = static_cast<uint8>(param & 0xF0) | (chn.noteSlideParam & 0x0F);
3948 		if(param & 0x0F)
3949 			chn.noteSlideParam = (chn.noteSlideParam & 0xF0) | static_cast<uint8>(param & 0x0F);
3950 		chn.noteSlideCounter = (chn.noteSlideParam >> 4);
3951 	}
3952 
3953 	bool doTrigger = false;
3954 	if(GetType() == MOD_TYPE_OKT)
3955 		doTrigger = ((chn.noteSlideParam & 0xF0) == 0x10) || m_SongFlags[SONG_FIRSTTICK];
3956 	else
3957 		doTrigger = !m_SongFlags[SONG_FIRSTTICK] && (--chn.noteSlideCounter == 0);
3958 
3959 	if(doTrigger)
3960 	{
3961 		const uint8 speed = (chn.noteSlideParam >> 4), steps = (chn.noteSlideParam & 0x0F);
3962 		chn.noteSlideCounter = speed;
3963 		// update it
3964 		const int32 delta = (slideUp ? steps : -steps);
3965 		if(chn.HasCustomTuning())
3966 			chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount();
3967 		else
3968 			chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod, chn.nFineTune, chn.nC5Speed), chn.nFineTune, chn.nC5Speed);
3969 
3970 		if(retrig)
3971 			chn.position.Set(0);
3972 	}
3973 }
3974 
3975 
GetVolCmdTonePorta(const ModCommand & m,uint32 startTick) const3976 std::pair<uint16, bool> CSoundFile::GetVolCmdTonePorta(const ModCommand &m, uint32 startTick) const
3977 {
3978 	if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL))
3979 	{
3980 		return {ImpulseTrackerPortaVolCmd[m.vol & 0x0F], false};
3981 	} else
3982 	{
3983 		bool clearEffectColumn = false;
3984 		uint16 vol = m.vol;
3985 		if(m.command == CMD_TONEPORTAMENTO && GetType() == MOD_TYPE_XM)
3986 		{
3987 			// Yes, FT2 is *that* weird. If there is a Mx command in the volume column
3988 			// and a normal 3xx command, the 3xx command is ignored but the Mx command's
3989 			// effectiveness is doubled.
3990 			// Test case: TonePortamentoMemory.xm
3991 			clearEffectColumn = true;
3992 			vol *= 2;
3993 		}
3994 
3995 		// FT2 compatibility: If there's a portamento and a note delay, execute the portamento, but don't update the parameter
3996 		// Test case: PortaDelay.xm
3997 		if(m_playBehaviour[kFT2PortaDelay] && startTick != 0)
3998 			return {uint16(0), clearEffectColumn};
3999 		else
4000 			return {static_cast<uint16>(vol * 16), clearEffectColumn};
4001 	}
4002 }
4003 
4004 
4005 // Portamento Slide
TonePortamento(ModChannel & chn,uint16 param) const4006 void CSoundFile::TonePortamento(ModChannel &chn, uint16 param) const
4007 {
4008 	chn.dwFlags.set(CHN_PORTAMENTO);
4009 
4010 	//IT compatibility 03: Share effect memory with portamento up/down
4011 	if((!m_SongFlags[SONG_ITCOMPATGXX] && m_playBehaviour[kITPortaMemoryShare]) || GetType() == MOD_TYPE_PLM)
4012 	{
4013 		if(param == 0) param = chn.nOldPortaUp;
4014 		chn.nOldPortaUp = chn.nOldPortaDown = static_cast<uint8>(param);
4015 	}
4016 
4017 	if(param)
4018 		chn.portamentoSlide = param;
4019 
4020 	if(chn.HasCustomTuning())
4021 	{
4022 		//Behavior: Param tells number of finesteps(or 'fullsteps'(notes) with glissando)
4023 		//to slide per row(not per tick).
4024 		if(chn.portamentoSlide == 0)
4025 			return;
4026 
4027 		const int32 oldPortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0;
4028 
4029 		int32 delta = chn.portamentoSlide;
4030 		if(chn.nPortamentoDest < 0)
4031 			delta = -delta;
4032 
4033 		chn.m_PortamentoTickSlide = static_cast<int32>((m_PlayState.m_nTickCount + 1.0) * delta / m_PlayState.m_nMusicSpeed);
4034 
4035 		if(chn.dwFlags[CHN_GLISSANDO])
4036 		{
4037 			chn.m_PortamentoTickSlide *= chn.pModInstrument->pTuning->GetFineStepCount() + 1;
4038 			//With glissando interpreting param as notes instead of finesteps.
4039 		}
4040 
4041 		const int32 slide = chn.m_PortamentoTickSlide - oldPortamentoTickSlide;
4042 
4043 		if(std::abs(chn.nPortamentoDest) <= std::abs(slide))
4044 		{
4045 			if(chn.nPortamentoDest != 0)
4046 			{
4047 				chn.m_PortamentoFineSteps += chn.nPortamentoDest;
4048 				chn.nPortamentoDest = 0;
4049 				chn.m_CalculateFreq = true;
4050 			}
4051 		} else
4052 		{
4053 			chn.m_PortamentoFineSteps += slide;
4054 			chn.nPortamentoDest -= slide;
4055 			chn.m_CalculateFreq = true;
4056 		}
4057 
4058 		return;
4059 	}
4060 
4061 	bool doPorta = !chn.isFirstTick
4062 	               || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669))
4063 	               || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1])
4064 	               || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]);
4065 
4066 	int32 delta = chn.portamentoSlide;
4067 	if(GetType() == MOD_TYPE_PLM && delta >= 0xF0)
4068 	{
4069 		delta -= 0xF0;
4070 		doPorta = chn.isFirstTick;
4071 	}
4072 
4073 	if(chn.nPeriod && chn.nPortamentoDest && doPorta)
4074 	{
4075 		delta *= (GetType() == MOD_TYPE_669) ? 2 : 4;
4076 		if(!PeriodsAreFrequencies())
4077 			delta = -delta;
4078 		if(chn.nPeriod < chn.nPortamentoDest || chn.portaTargetReached)
4079 		{
4080 			DoFreqSlide(chn, chn.nPeriod, delta, true);
4081 			if(chn.nPeriod > chn.nPortamentoDest)
4082 				chn.nPeriod = chn.nPortamentoDest;
4083 		} else if(chn.nPeriod > chn.nPortamentoDest)
4084 		{
4085 			DoFreqSlide(chn, chn.nPeriod, -delta, true);
4086 			if(chn.nPeriod < chn.nPortamentoDest)
4087 				chn.nPeriod = chn.nPortamentoDest;
4088 			// FT2 compatibility: Reaching portamento target from below forces subsequent portamentos on the same note to use the logic for reaching the note from above instead.
4089 			// Test case: PortaResetDirection.xm
4090 			if(chn.nPeriod == chn.nPortamentoDest && m_playBehaviour[kFT2PortaResetDirection])
4091 				chn.portaTargetReached = true;
4092 		}
4093 	}
4094 
4095 	// IT compatibility 23. Portamento with no note
4096 	// ProTracker also disables portamento once the target is reached.
4097 	// Test case: PortaTarget.mod
4098 	if(chn.nPeriod == chn.nPortamentoDest && (m_playBehaviour[kITPortaTargetReached] || GetType() == MOD_TYPE_MOD))
4099 		chn.nPortamentoDest = 0;
4100 
4101 }
4102 
4103 
Vibrato(ModChannel & chn,uint32 param) const4104 void CSoundFile::Vibrato(ModChannel &chn, uint32 param) const
4105 {
4106 	if (param & 0x0F) chn.nVibratoDepth = (param & 0x0F) * 4;
4107 	if (param & 0xF0) chn.nVibratoSpeed = (param >> 4) & 0x0F;
4108 	chn.dwFlags.set(CHN_VIBRATO);
4109 }
4110 
4111 
FineVibrato(ModChannel & chn,uint32 param) const4112 void CSoundFile::FineVibrato(ModChannel &chn, uint32 param) const
4113 {
4114 	if (param & 0x0F) chn.nVibratoDepth = param & 0x0F;
4115 	if (param & 0xF0) chn.nVibratoSpeed = (param >> 4) & 0x0F;
4116 	chn.dwFlags.set(CHN_VIBRATO);
4117 	// ST3 compatibility: Do not distinguish between vibrato types in effect memory
4118 	// Test case: VibratoTypeChange.s3m
4119 	if(m_playBehaviour[kST3VibratoMemory] && (param & 0x0F))
4120 	{
4121 		chn.nVibratoDepth *= 4u;
4122 	}
4123 }
4124 
4125 
Panbrello(ModChannel & chn,uint32 param) const4126 void CSoundFile::Panbrello(ModChannel &chn, uint32 param) const
4127 {
4128 	if (param & 0x0F) chn.nPanbrelloDepth = param & 0x0F;
4129 	if (param & 0xF0) chn.nPanbrelloSpeed = (param >> 4) & 0x0F;
4130 }
4131 
4132 
Panning(ModChannel & chn,uint32 param,PanningType panBits) const4133 void CSoundFile::Panning(ModChannel &chn, uint32 param, PanningType panBits) const
4134 {
4135 	// No panning in ProTracker mode
4136 	if(m_playBehaviour[kMODIgnorePanning])
4137 	{
4138 		return;
4139 	}
4140 	// IT Compatibility (and other trackers as well): panning disables surround (unless panning in rear channels is enabled, which is not supported by the original trackers anyway)
4141 	if (!m_SongFlags[SONG_SURROUNDPAN] && (panBits == Pan8bit || m_playBehaviour[kPanOverride]))
4142 	{
4143 		chn.dwFlags.reset(CHN_SURROUND);
4144 	}
4145 	if(panBits == Pan4bit)
4146 	{
4147 		// 0...15 panning
4148 		chn.nPan = (param * 256 + 8) / 15;
4149 	} else if(panBits == Pan6bit)
4150 	{
4151 		// 0...64 panning
4152 		if(param > 64) param = 64;
4153 		chn.nPan = param * 4;
4154 	} else
4155 	{
4156 		if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_DSM | MOD_TYPE_AMF0 | MOD_TYPE_AMF | MOD_TYPE_MTM)))
4157 		{
4158 			// Real 8-bit panning
4159 			chn.nPan = param;
4160 		} else
4161 		{
4162 			// 7-bit panning + surround
4163 			if(param <= 0x80)
4164 			{
4165 				chn.nPan = param << 1;
4166 			} else if(param == 0xA4)
4167 			{
4168 				chn.dwFlags.set(CHN_SURROUND);
4169 				chn.nPan = 0x80;
4170 			}
4171 		}
4172 	}
4173 
4174 	chn.dwFlags.set(CHN_FASTVOLRAMP);
4175 	chn.nRestorePanOnNewNote = 0;
4176 	//IT compatibility 20. Set pan overrides random pan
4177 	if(m_playBehaviour[kPanOverride])
4178 	{
4179 		chn.nPanSwing = 0;
4180 		chn.nPanbrelloOffset = 0;
4181 	}
4182 }
4183 
4184 
VolumeSlide(ModChannel & chn,ModCommand::PARAM param) const4185 void CSoundFile::VolumeSlide(ModChannel &chn, ModCommand::PARAM param) const
4186 {
4187 	if (param)
4188 		chn.nOldVolumeSlide = param;
4189 	else
4190 		param = chn.nOldVolumeSlide;
4191 
4192 	if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM)))
4193 	{
4194 		// MOD / XM nibble priority
4195 		if((param & 0xF0) != 0)
4196 		{
4197 			param &= 0xF0;
4198 		} else
4199 		{
4200 			param &= 0x0F;
4201 		}
4202 	}
4203 
4204 	int newVolume = chn.nVolume;
4205 	if(!(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_AMF0 | MOD_TYPE_MED | MOD_TYPE_DIGI)))
4206 	{
4207 		if ((param & 0x0F) == 0x0F) //Fine upslide or slide -15
4208 		{
4209 			if (param & 0xF0) //Fine upslide
4210 			{
4211 				FineVolumeUp(chn, (param >> 4), false);
4212 				return;
4213 			} else //Slide -15
4214 			{
4215 				if(chn.isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES])
4216 				{
4217 					newVolume -= 0x0F * 4;
4218 				}
4219 			}
4220 		} else
4221 		if ((param & 0xF0) == 0xF0) //Fine downslide or slide +15
4222 		{
4223 			if (param & 0x0F) //Fine downslide
4224 			{
4225 				FineVolumeDown(chn, (param & 0x0F), false);
4226 				return;
4227 			} else //Slide +15
4228 			{
4229 				if(chn.isFirstTick && !m_SongFlags[SONG_FASTVOLSLIDES])
4230 				{
4231 					newVolume += 0x0F * 4;
4232 				}
4233 			}
4234 		}
4235 	}
4236 	if(!chn.isFirstTick || m_SongFlags[SONG_FASTVOLSLIDES] || (m_PlayState.m_nMusicSpeed == 1 && GetType() == MOD_TYPE_DBM))
4237 	{
4238 		// IT compatibility: Ignore slide commands with both nibbles set.
4239 		if (param & 0x0F)
4240 		{
4241 			if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0)
4242 				newVolume -= (int)((param & 0x0F) * 4);
4243 		}
4244 		else
4245 		{
4246 			newVolume += (int)((param & 0xF0) >> 2);
4247 		}
4248 		if (GetType() == MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP);
4249 	}
4250 	newVolume = Clamp(newVolume, 0, 256);
4251 
4252 	chn.nVolume = newVolume;
4253 }
4254 
4255 
PanningSlide(ModChannel & chn,ModCommand::PARAM param,bool memory) const4256 void CSoundFile::PanningSlide(ModChannel &chn, ModCommand::PARAM param, bool memory) const
4257 {
4258 	if(memory)
4259 	{
4260 		// FT2 compatibility: Use effect memory (lxx and rxx in XM shouldn't use effect memory).
4261 		// Test case: PanSlideMem.xm
4262 		if(param)
4263 			chn.nOldPanSlide = param;
4264 		else
4265 			param = chn.nOldPanSlide;
4266 	}
4267 
4268 	if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)))
4269 	{
4270 		// XM nibble priority
4271 		if((param & 0xF0) != 0)
4272 		{
4273 			param &= 0xF0;
4274 		} else
4275 		{
4276 			param &= 0x0F;
4277 		}
4278 	}
4279 
4280 	int32 nPanSlide = 0;
4281 
4282 	if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)))
4283 	{
4284 		if (((param & 0x0F) == 0x0F) && (param & 0xF0))
4285 		{
4286 			if(m_SongFlags[SONG_FIRSTTICK])
4287 			{
4288 				param = (param & 0xF0) / 4u;
4289 				nPanSlide = - (int)param;
4290 			}
4291 		} else if (((param & 0xF0) == 0xF0) && (param & 0x0F))
4292 		{
4293 			if(m_SongFlags[SONG_FIRSTTICK])
4294 			{
4295 				nPanSlide = (param & 0x0F) * 4u;
4296 			}
4297 		} else if(!m_SongFlags[SONG_FIRSTTICK])
4298 		{
4299 			if (param & 0x0F)
4300 			{
4301 				// IT compatibility: Ignore slide commands with both nibbles set.
4302 				if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0)
4303 					nPanSlide = (int)((param & 0x0F) * 4u);
4304 			} else
4305 			{
4306 				nPanSlide = -(int)((param & 0xF0) / 4u);
4307 			}
4308 		}
4309 	} else
4310 	{
4311 		if(!m_SongFlags[SONG_FIRSTTICK])
4312 		{
4313 			if (param & 0xF0)
4314 			{
4315 				nPanSlide = (int)((param & 0xF0) / 4u);
4316 			} else
4317 			{
4318 				nPanSlide = -(int)((param & 0x0F) * 4u);
4319 			}
4320 			// FT2 compatibility: FT2's panning slide is like IT's fine panning slide (not as deep)
4321 			if(m_playBehaviour[kFT2PanSlide])
4322 				nPanSlide /= 4;
4323 		}
4324 	}
4325 	if (nPanSlide)
4326 	{
4327 		nPanSlide += chn.nPan;
4328 		nPanSlide = Clamp(nPanSlide, 0, 256);
4329 		chn.nPan = nPanSlide;
4330 		chn.nRestorePanOnNewNote = 0;
4331 	}
4332 }
4333 
4334 
FineVolumeUp(ModChannel & chn,ModCommand::PARAM param,bool volCol) const4335 void CSoundFile::FineVolumeUp(ModChannel &chn, ModCommand::PARAM param, bool volCol) const
4336 {
4337 	if(GetType() == MOD_TYPE_XM)
4338 	{
4339 		// FT2 compatibility: EAx / EBx memory is not linked
4340 		// Test case: FineVol-LinkMem.xm
4341 		if(param) chn.nOldFineVolUpDown = (param << 4) | (chn.nOldFineVolUpDown & 0x0F); else param = (chn.nOldFineVolUpDown >> 4);
4342 	} else if(volCol)
4343 	{
4344 		if(param) chn.nOldVolParam = param; else param = chn.nOldVolParam;
4345 	} else
4346 	{
4347 		if(param) chn.nOldFineVolUpDown = param; else param = chn.nOldFineVolUpDown;
4348 	}
4349 
4350 	if(chn.isFirstTick)
4351 	{
4352 		chn.nVolume += param * 4;
4353 		if(chn.nVolume > 256) chn.nVolume = 256;
4354 		if(GetType() & MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP);
4355 	}
4356 }
4357 
4358 
FineVolumeDown(ModChannel & chn,ModCommand::PARAM param,bool volCol) const4359 void CSoundFile::FineVolumeDown(ModChannel &chn, ModCommand::PARAM param, bool volCol) const
4360 {
4361 	if(GetType() == MOD_TYPE_XM)
4362 	{
4363 		// FT2 compatibility: EAx / EBx memory is not linked
4364 		// Test case: FineVol-LinkMem.xm
4365 		if(param) chn.nOldFineVolUpDown = param | (chn.nOldFineVolUpDown & 0xF0); else param = (chn.nOldFineVolUpDown & 0x0F);
4366 	} else if(volCol)
4367 	{
4368 		if(param) chn.nOldVolParam = param; else param = chn.nOldVolParam;
4369 	} else
4370 	{
4371 		if(param) chn.nOldFineVolUpDown = param; else param = chn.nOldFineVolUpDown;
4372 	}
4373 
4374 	if(chn.isFirstTick)
4375 	{
4376 		chn.nVolume -= param * 4;
4377 		if(chn.nVolume < 0) chn.nVolume = 0;
4378 		if(GetType() & MOD_TYPE_MOD) chn.dwFlags.set(CHN_FASTVOLRAMP);
4379 	}
4380 }
4381 
4382 
Tremolo(ModChannel & chn,uint32 param) const4383 void CSoundFile::Tremolo(ModChannel &chn, uint32 param) const
4384 {
4385 	if (param & 0x0F) chn.nTremoloDepth = (param & 0x0F) << 2;
4386 	if (param & 0xF0) chn.nTremoloSpeed = (param >> 4) & 0x0F;
4387 	chn.dwFlags.set(CHN_TREMOLO);
4388 }
4389 
4390 
ChannelVolSlide(ModChannel & chn,ModCommand::PARAM param) const4391 void CSoundFile::ChannelVolSlide(ModChannel &chn, ModCommand::PARAM param) const
4392 {
4393 	int32 nChnSlide = 0;
4394 	if (param) chn.nOldChnVolSlide = param; else param = chn.nOldChnVolSlide;
4395 
4396 	if (((param & 0x0F) == 0x0F) && (param & 0xF0))
4397 	{
4398 		if(m_SongFlags[SONG_FIRSTTICK]) nChnSlide = param >> 4;
4399 	} else if (((param & 0xF0) == 0xF0) && (param & 0x0F))
4400 	{
4401 		if(m_SongFlags[SONG_FIRSTTICK]) nChnSlide = - (int)(param & 0x0F);
4402 	} else
4403 	{
4404 		if(!m_SongFlags[SONG_FIRSTTICK])
4405 		{
4406 			if (param & 0x0F)
4407 			{
4408 				if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_J2B | MOD_TYPE_DBM)) || (param & 0xF0) == 0)
4409 					nChnSlide = -(int)(param & 0x0F);
4410 			} else
4411 			{
4412 				nChnSlide = (int)((param & 0xF0) >> 4);
4413 			}
4414 		}
4415 	}
4416 	if (nChnSlide)
4417 	{
4418 		nChnSlide += chn.nGlobalVol;
4419 		nChnSlide = Clamp(nChnSlide, 0, 64);
4420 		chn.nGlobalVol = nChnSlide;
4421 	}
4422 }
4423 
4424 
ExtendedMODCommands(CHANNELINDEX nChn,ModCommand::PARAM param)4425 void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param)
4426 {
4427 	ModChannel &chn = m_PlayState.Chn[nChn];
4428 	uint8 command = param & 0xF0;
4429 	param &= 0x0F;
4430 	switch(command)
4431 	{
4432 	// E0x: Set Filter
4433 	case 0x00:
4434 		for(CHANNELINDEX channel = 0; channel < GetNumChannels(); channel++)
4435 		{
4436 			m_PlayState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1));
4437 		}
4438 		break;
4439 	// E1x: Fine Portamento Up
4440 	case 0x10:	if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoUp(chn, param); break;
4441 	// E2x: Fine Portamento Down
4442 	case 0x20:	if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoDown(chn, param); break;
4443 	// E3x: Set Glissando Control
4444 	case 0x30:	chn.dwFlags.set(CHN_GLISSANDO, param != 0); break;
4445 	// E4x: Set Vibrato WaveForm
4446 	case 0x40:	chn.nVibratoType = param & 0x07; break;
4447 	// E5x: Set FineTune
4448 	case 0x50:	if(!m_SongFlags[SONG_FIRSTTICK])
4449 					break;
4450 				if(GetType() & (MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_AMF0 | MOD_TYPE_MED))
4451 				{
4452 					chn.nFineTune = MOD2XMFineTune(param);
4453 					if(chn.nPeriod && chn.rowCommand.IsNote()) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed);
4454 				} else if(GetType() == MOD_TYPE_MTM)
4455 				{
4456 					if(chn.rowCommand.IsNote() && chn.pModSample != nullptr)
4457 					{
4458 						// Effect is permanent in MultiTracker
4459 						const_cast<ModSample *>(chn.pModSample)->nFineTune = param;
4460 						chn.nFineTune = param;
4461 						if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed);
4462 					}
4463 				} else if(chn.rowCommand.IsNote())
4464 				{
4465 					chn.nFineTune = MOD2XMFineTune(param - 8);
4466 					if(chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed);
4467 				}
4468 				break;
4469 	// E6x: Pattern Loop
4470 	case 0x60:
4471 		if(m_SongFlags[SONG_FIRSTTICK])
4472 			PatternLoop(m_PlayState, chn, param & 0x0F);
4473 		break;
4474 	// E7x: Set Tremolo WaveForm
4475 	case 0x70:	chn.nTremoloType = param & 0x07; break;
4476 	// E8x: Set 4-bit Panning
4477 	case 0x80:
4478 		if(m_SongFlags[SONG_FIRSTTICK])
4479 		{
4480 			Panning(chn, param, Pan4bit);
4481 		}
4482 		break;
4483 	// E9x: Retrig
4484 	case 0x90:	RetrigNote(nChn, param); break;
4485 	// EAx: Fine Volume Up
4486 	case 0xA0:	if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeUp(chn, param, false); break;
4487 	// EBx: Fine Volume Down
4488 	case 0xB0:	if ((param) || (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeDown(chn, param, false); break;
4489 	// ECx: Note Cut
4490 	case 0xC0:	NoteCut(nChn, param, false); break;
4491 	// EDx: Note Delay
4492 	// EEx: Pattern Delay
4493 	case 0xF0:
4494 		if(GetType() == MOD_TYPE_MOD) // MOD: Invert Loop
4495 		{
4496 			chn.nEFxSpeed = param;
4497 			if(m_SongFlags[SONG_FIRSTTICK]) InvertLoop(chn);
4498 		} else // XM: Set Active Midi Macro
4499 		{
4500 			chn.nActiveMacro = param;
4501 		}
4502 		break;
4503 	}
4504 }
4505 
4506 
ExtendedS3MCommands(CHANNELINDEX nChn,ModCommand::PARAM param)4507 void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param)
4508 {
4509 	ModChannel &chn = m_PlayState.Chn[nChn];
4510 	uint8 command = param & 0xF0;
4511 	param &= 0x0F;
4512 	switch(command)
4513 	{
4514 	// S0x: Set Filter
4515 	// S1x: Set Glissando Control
4516 	case 0x10:	chn.dwFlags.set(CHN_GLISSANDO, param != 0); break;
4517 	// S2x: Set FineTune
4518 	case 0x20:	if(!m_SongFlags[SONG_FIRSTTICK])
4519 					break;
4520 				if(chn.HasCustomTuning())
4521 				{
4522 					chn.nFineTune = param - 8;
4523 					chn.m_CalculateFreq = true;
4524 				} else if(GetType() != MOD_TYPE_669)
4525 				{
4526 					chn.nC5Speed = S3MFineTuneTable[param];
4527 					chn.nFineTune = MOD2XMFineTune(param);
4528 					if(chn.nPeriod)
4529 						chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed);
4530 				} else if(chn.pModSample != nullptr)
4531 				{
4532 					chn.nC5Speed = chn.pModSample->nC5Speed + param * 80;
4533 				}
4534 				break;
4535 	// S3x: Set Vibrato Waveform
4536 	case 0x30:	if(GetType() == MOD_TYPE_S3M)
4537 				{
4538 					chn.nVibratoType = param & 0x03;
4539 				} else
4540 				{
4541 					// IT compatibility: Ignore waveform types > 3
4542 					if(m_playBehaviour[kITVibratoTremoloPanbrello])
4543 						chn.nVibratoType = (param < 0x04) ? param : 0;
4544 					else
4545 						chn.nVibratoType = param & 0x07;
4546 				}
4547 				break;
4548 	// S4x: Set Tremolo Waveform
4549 	case 0x40:	if(GetType() == MOD_TYPE_S3M)
4550 				{
4551 					chn.nTremoloType = param & 0x03;
4552 				} else
4553 				{
4554 					// IT compatibility: Ignore waveform types > 3
4555 					if(m_playBehaviour[kITVibratoTremoloPanbrello])
4556 						chn.nTremoloType = (param < 0x04) ? param : 0;
4557 					else
4558 						chn.nTremoloType = param & 0x07;
4559 				}
4560 				break;
4561 	// S5x: Set Panbrello Waveform
4562 	case 0x50:
4563 		// IT compatibility: Ignore waveform types > 3
4564 				if(m_playBehaviour[kITVibratoTremoloPanbrello])
4565 				{
4566 					chn.nPanbrelloType = (param < 0x04) ? param : 0;
4567 					chn.nPanbrelloPos = 0;
4568 				} else
4569 				{
4570 					chn.nPanbrelloType = param & 0x07;
4571 				}
4572 				break;
4573 	// S6x: Pattern Delay for x frames
4574 	case 0x60:
4575 				if(m_SongFlags[SONG_FIRSTTICK] && m_PlayState.m_nTickCount == 0)
4576 				{
4577 					// Tick delays are added up.
4578 					// Scream Tracker 3 does actually not support this command.
4579 					// We'll use the same behaviour as for Impulse Tracker, as we can assume that
4580 					// most S3Ms that make use of this command were made with Impulse Tracker.
4581 					// MPT added this command to the XM format through the X6x effect, so we will use
4582 					// the same behaviour here as well.
4583 					// Test cases: PatternDelays.it, PatternDelays.s3m, PatternDelays.xm
4584 					m_PlayState.m_nFrameDelay += param;
4585 				}
4586 				break;
4587 	// S7x: Envelope Control / Instrument Control
4588 	case 0x70:	if(!m_SongFlags[SONG_FIRSTTICK]) break;
4589 				switch(param)
4590 				{
4591 				case 0:
4592 				case 1:
4593 				case 2:
4594 					{
4595 						for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++)
4596 						{
4597 							ModChannel &bkChn = m_PlayState.Chn[i];
4598 							if (bkChn.nMasterChn == nChn + 1)
4599 							{
4600 								if (param == 1)
4601 								{
4602 									KeyOff(bkChn);
4603 									if(bkChn.dwFlags[CHN_ADLIB] && m_opl)
4604 										m_opl->NoteOff(i);
4605 								} else if (param == 2)
4606 								{
4607 									bkChn.dwFlags.set(CHN_NOTEFADE);
4608 									if(bkChn.dwFlags[CHN_ADLIB] && m_opl)
4609 										m_opl->NoteOff(i);
4610 								} else
4611 								{
4612 									bkChn.dwFlags.set(CHN_NOTEFADE);
4613 									bkChn.nFadeOutVol = 0;
4614 									if(bkChn.dwFlags[CHN_ADLIB] && m_opl)
4615 										m_opl->NoteCut(i);
4616 								}
4617 #ifndef NO_PLUGINS
4618 								const ModInstrument *pIns = bkChn.pModInstrument;
4619 								IMixPlugin *pPlugin;
4620 								if(pIns != nullptr && pIns->nMixPlug && (pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin) != nullptr)
4621 								{
4622 									pPlugin->MidiCommand(*pIns, bkChn.nNote + NOTE_MAX_SPECIAL, 0, nChn);
4623 								}
4624 #endif // NO_PLUGINS
4625 							}
4626 						}
4627 					}
4628 					break;
4629 				default:  // S73-S7E
4630 					chn.InstrumentControl(param, *this);
4631 					break;
4632 				}
4633 				break;
4634 	// S8x: Set 4-bit Panning
4635 	case 0x80:
4636 		if(m_SongFlags[SONG_FIRSTTICK])
4637 		{
4638 			Panning(chn, param, Pan4bit);
4639 		}
4640 		break;
4641 	// S9x: Sound Control
4642 	case 0x90:	ExtendedChannelEffect(chn, param); break;
4643 	// SAx: Set 64k Offset
4644 	case 0xA0:	if(m_SongFlags[SONG_FIRSTTICK])
4645 				{
4646 					chn.nOldHiOffset = static_cast<uint8>(param);
4647 					if (!m_playBehaviour[kITHighOffsetNoRetrig] && chn.rowCommand.IsNote())
4648 					{
4649 						SmpLength pos = param << 16;
4650 						if (pos < chn.nLength) chn.position.SetInt(pos);
4651 					}
4652 				}
4653 				break;
4654 	// SBx: Pattern Loop
4655 	case 0xB0:
4656 		if(m_SongFlags[SONG_FIRSTTICK])
4657 			PatternLoop(m_PlayState, chn, param & 0x0F);
4658 		break;
4659 	// SCx: Note Cut
4660 	case 0xC0:
4661 		if(param == 0)
4662 		{
4663 			//IT compatibility 22. SC0 == SC1
4664 			if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
4665 				param = 1;
4666 			// ST3 doesn't cut notes with SC0
4667 			else if(GetType() == MOD_TYPE_S3M)
4668 				return;
4669 		}
4670 		// S3M/IT compatibility: Note Cut really cuts notes and does not just mute them (so that following volume commands could restore the sample)
4671 		// Test case: scx.it
4672 		NoteCut(nChn, param, m_playBehaviour[kITSCxStopsSample] || GetType() == MOD_TYPE_S3M);
4673 		break;
4674 	// SDx: Note Delay
4675 	// SEx: Pattern Delay for x rows
4676 	// SFx: S3M: Not used, IT: Set Active Midi Macro
4677 	case 0xF0:
4678 		if(GetType() != MOD_TYPE_S3M)
4679 		{
4680 			chn.nActiveMacro = static_cast<uint8>(param);
4681 		}
4682 		break;
4683 	}
4684 }
4685 
4686 
ExtendedChannelEffect(ModChannel & chn,uint32 param)4687 void CSoundFile::ExtendedChannelEffect(ModChannel &chn, uint32 param)
4688 {
4689 	// S9x and X9x commands (S3M/XM/IT only)
4690 	if(!m_SongFlags[SONG_FIRSTTICK]) return;
4691 	switch(param & 0x0F)
4692 	{
4693 	// S90: Surround Off
4694 	case 0x00:	chn.dwFlags.reset(CHN_SURROUND);	break;
4695 	// S91: Surround On
4696 	case 0x01:	chn.dwFlags.set(CHN_SURROUND); chn.nPan = 128; break;
4697 
4698 	////////////////////////////////////////////////////////////
4699 	// ModPlug Extensions
4700 	// S98: Reverb Off
4701 	case 0x08:
4702 		chn.dwFlags.reset(CHN_REVERB);
4703 		chn.dwFlags.set(CHN_NOREVERB);
4704 		break;
4705 	// S99: Reverb On
4706 	case 0x09:
4707 		chn.dwFlags.reset(CHN_NOREVERB);
4708 		chn.dwFlags.set(CHN_REVERB);
4709 		break;
4710 	// S9A: 2-Channels surround mode
4711 	case 0x0A:
4712 		m_SongFlags.reset(SONG_SURROUNDPAN);
4713 		break;
4714 	// S9B: 4-Channels surround mode
4715 	case 0x0B:
4716 		m_SongFlags.set(SONG_SURROUNDPAN);
4717 		break;
4718 	// S9C: IT Filter Mode
4719 	case 0x0C:
4720 		m_SongFlags.reset(SONG_MPTFILTERMODE);
4721 		break;
4722 	// S9D: MPT Filter Mode
4723 	case 0x0D:
4724 		m_SongFlags.set(SONG_MPTFILTERMODE);
4725 		break;
4726 	// S9E: Go forward
4727 	case 0x0E:
4728 		chn.dwFlags.reset(CHN_PINGPONGFLAG);
4729 		break;
4730 	// S9F: Go backward (and set playback position to the end if sample just started)
4731 	case 0x0F:
4732 		if(chn.position.IsZero() && chn.nLength && (chn.rowCommand.IsNote() || !chn.dwFlags[CHN_LOOP]))
4733 		{
4734 			chn.position.Set(chn.nLength - 1, SamplePosition::fractMax);
4735 		}
4736 		chn.dwFlags.set(CHN_PINGPONGFLAG);
4737 		break;
4738 	}
4739 }
4740 
4741 
InvertLoop(ModChannel & chn)4742 void CSoundFile::InvertLoop(ModChannel &chn)
4743 {
4744 	// EFx implementation for MOD files (PT 1.1A and up: Invert Loop)
4745 	// This effect trashes samples. Thanks to 8bitbubsy for making this work. :)
4746 	if(GetType() != MOD_TYPE_MOD || chn.nEFxSpeed == 0)
4747 		return;
4748 
4749 	ModSample *pModSample = const_cast<ModSample *>(chn.pModSample);
4750 	if(pModSample == nullptr || !pModSample->HasSampleData() || !pModSample->uFlags[CHN_LOOP | CHN_SUSTAINLOOP])
4751 		return;
4752 
4753 	chn.nEFxDelay += ModEFxTable[chn.nEFxSpeed & 0x0F];
4754 	if(chn.nEFxDelay < 128)
4755 		return;
4756 	chn.nEFxDelay = 0;
4757 
4758 	const SmpLength loopStart = pModSample->uFlags[CHN_LOOP] ? pModSample->nLoopStart : pModSample->nSustainStart;
4759 	const SmpLength loopEnd = pModSample->uFlags[CHN_LOOP] ? pModSample->nLoopEnd : pModSample->nSustainEnd;
4760 
4761 	if(++chn.nEFxOffset >= loopEnd - loopStart)
4762 		chn.nEFxOffset = 0;
4763 
4764 	// TRASH IT!!! (Yes, the sample!)
4765 	const uint8 bps = pModSample->GetBytesPerSample();
4766 	uint8 *begin = mpt::byte_cast<uint8 *>(pModSample->sampleb()) + (loopStart + chn.nEFxOffset) * bps;
4767 	for(auto &sample : mpt::as_span(begin, bps))
4768 	{
4769 		sample = ~sample;
4770 	}
4771 	pModSample->PrecomputeLoops(*this, false);
4772 }
4773 
4774 
4775 // Process a MIDI Macro.
4776 // Parameters:
4777 // nChn: Mod channel to apply macro on
4778 // isSmooth: If true, internal macros are interpolated between two rows
4779 // macro: Actual MIDI Macro string
4780 // param: Parameter for parametric macros (Z00 - Z7F)
4781 // plugin: Plugin to send MIDI message to (if not specified but needed, it is autodetected)
ProcessMIDIMacro(CHANNELINDEX nChn,bool isSmooth,const char * macro,uint8 param,PLUGINDEX plugin)4782 void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char *macro, uint8 param, PLUGINDEX plugin)
4783 {
4784 	ModChannel &chn = m_PlayState.Chn[nChn];
4785 	const ModInstrument *pIns = GetNumInstruments() ? chn.pModInstrument : nullptr;
4786 
4787 	uint8 out[MACRO_LENGTH];
4788 	uint32 outPos = 0;  // output buffer position, which also equals the number of complete bytes
4789 	const uint8 lastZxxParam = chn.lastZxxParam;  // always interpolate based on original value in case z appears multiple times in macro string
4790 	uint8 updateZxxParam = 0xFF;                  // avoid updating lastZxxParam immediately if macro contains both internal and external MIDI message
4791 	bool firstNibble = true;
4792 
4793 	for(uint32 pos = 0; pos < (MACRO_LENGTH - 1) && macro[pos]; pos++)
4794 	{
4795 		bool isNibble = false;  // did we parse a nibble or a byte value?
4796 		uint8 data = 0;         // data that has just been parsed
4797 
4798 		// Parse next macro byte... See Impulse Tracker's MIDI.TXT for detailed information on each possible character.
4799 		if(macro[pos] >= '0' && macro[pos] <= '9')
4800 		{
4801 			isNibble = true;
4802 			data = static_cast<uint8>(macro[pos] - '0');
4803 		}
4804 		else if(macro[pos] >= 'A' && macro[pos] <= 'F')
4805 		{
4806 			isNibble = true;
4807 			data = static_cast<uint8>(macro[pos] - 'A' + 0x0A);
4808 		} else if(macro[pos] == 'c')
4809 		{
4810 			// MIDI channel
4811 			isNibble = true;
4812 			data = 0xFF;
4813 #ifndef NO_PLUGINS
4814 			const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
4815 			if(plug > 0 && plug <= MAX_MIXPLUGINS)
4816 			{
4817 				auto midiPlug = dynamic_cast<const IMidiPlugin *>(m_MixPlugins[plug - 1u].pMixPlugin);
4818 				if(midiPlug)
4819 					data = midiPlug->GetMidiChannel(nChn);
4820 			}
4821 #endif // NO_PLUGINS
4822 			if(data == 0xFF)
4823 			{
4824 				// Fallback if no plugin was found
4825 				if(pIns)
4826 					data = pIns->GetMIDIChannel(*this, nChn);
4827 				else
4828 					data = 0;
4829 			}
4830 		} else if(macro[pos] == 'n')
4831 		{
4832 			// Last triggered note
4833 			if(ModCommand::IsNote(chn.nLastNote))
4834 			{
4835 				data = chn.nLastNote - NOTE_MIN;
4836 			}
4837 		} else if(macro[pos] == 'v')
4838 		{
4839 			// Velocity
4840 			// This is "almost" how IT does it - apparently, IT seems to lag one row behind on global volume or channel volume changes.
4841 			const int swing = (m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) ? chn.nVolSwing : 0;
4842 			const int vol = Util::muldiv((chn.nVolume + swing) * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 20);
4843 			data = static_cast<uint8>(Clamp(vol / 2, 1, 127));
4844 			//data = (unsigned char)std::min((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127);
4845 		} else if(macro[pos] == 'u')
4846 		{
4847 			// Calculated volume
4848 			// Same note as with velocity applies here, but apparently also for instrument / sample volumes?
4849 			const int vol = Util::muldiv(chn.nCalcVolume * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 26);
4850 			data = static_cast<uint8>(Clamp(vol / 2, 1, 127));
4851 			//data = (unsigned char)std::min((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127);
4852 		} else if(macro[pos] == 'x')
4853 		{
4854 			// Pan set
4855 			data = static_cast<uint8>(std::min(static_cast<int>(chn.nPan / 2), 127));
4856 		} else if(macro[pos] == 'y')
4857 		{
4858 			// Calculated pan
4859 			data = static_cast<uint8>(std::min(static_cast<int>(chn.nRealPan / 2), 127));
4860 		} else if(macro[pos] == 'a')
4861 		{
4862 			// High byte of bank select
4863 			if(pIns && pIns->wMidiBank)
4864 			{
4865 				data = static_cast<uint8>(((pIns->wMidiBank - 1) >> 7) & 0x7F);
4866 			}
4867 		} else if(macro[pos] == 'b')
4868 		{
4869 			// Low byte of bank select
4870 			if(pIns && pIns->wMidiBank)
4871 			{
4872 				data = static_cast<uint8>((pIns->wMidiBank - 1) & 0x7F);
4873 			}
4874 		} else if(macro[pos] == 'o')
4875 		{
4876 			// Offset (ignoring high offset)
4877 			data = static_cast<uint8>((chn.oldOffset >> 8) & 0xFF);
4878 		} else if(macro[pos] == 'h')
4879 		{
4880 			// Host channel number
4881 			data = static_cast<uint8>((nChn >= GetNumChannels() ? (chn.nMasterChn - 1) : nChn) & 0x7F);
4882 		} else if(macro[pos] == 'm')
4883 		{
4884 			// Loop direction (judging from the character, it was supposed to be loop type, though)
4885 			data = chn.dwFlags[CHN_PINGPONGFLAG] ? 1 : 0;
4886 		} else if(macro[pos] == 'p')
4887 		{
4888 			// Program select
4889 			if(pIns && pIns->nMidiProgram)
4890 			{
4891 				data = static_cast<uint8>((pIns->nMidiProgram - 1) & 0x7F);
4892 			}
4893 		} else if(macro[pos] == 'z')
4894 		{
4895 			// Zxx parameter
4896 			data = param & 0x7F;
4897 			if(isSmooth && chn.lastZxxParam < 0x80
4898 				&& (outPos < 3 || out[outPos - 3] != 0xF0 || out[outPos - 2] < 0xF0))
4899 			{
4900 				// Interpolation for external MIDI messages - interpolation for internal messages
4901 				// is handled separately to allow for more than 7-bit granularity where it's possible
4902 				data = static_cast<uint8>(CalculateSmoothParamChange(lastZxxParam, data));
4903 				chn.lastZxxParam = data;
4904 				updateZxxParam = 0x80;
4905 			} else if(updateZxxParam == 0xFF)
4906 			{
4907 				updateZxxParam = data;
4908 			}
4909 		} else if(macro[pos] == 's')
4910 		{
4911 			// SysEx Checksum (not an original Impulse Tracker macro variable, but added for convenience)
4912 			uint32 startPos = outPos;
4913 			while(startPos > 0 && out[--startPos] != 0xF0);
4914 			if(outPos - startPos < 5 || out[startPos] != 0xF0)
4915 			{
4916 				continue;
4917 			}
4918 			for(uint32 p = startPos + 5; p != outPos; p++)
4919 			{
4920 				data += out[p];
4921 			}
4922 			data = (~data + 1) & 0x7F;
4923 		} else
4924 		{
4925 			// Unrecognized byte (e.g. space char)
4926 			continue;
4927 		}
4928 
4929 		// Append parsed data
4930 		if(isNibble)  // parsed a nibble (constant or 'c' variable)
4931 		{
4932 			if(firstNibble)
4933 			{
4934 				out[outPos] = data;
4935 			} else
4936 			{
4937 				out[outPos] = (out[outPos] << 4) | data;
4938 				outPos++;
4939 			}
4940 			firstNibble = !firstNibble;
4941 		} else  // parsed a byte (variable)
4942 		{
4943 			if(!firstNibble)	// From MIDI.TXT: '9n' is exactly the same as '09 n' or '9 n' -- so finish current byte first
4944 			{
4945 				outPos++;
4946 			}
4947 			out[outPos++] = data;
4948 			firstNibble = true;
4949 		}
4950 	}
4951 	if(!firstNibble)
4952 	{
4953 		// Finish current byte
4954 		outPos++;
4955 	}
4956 	if(updateZxxParam < 0x80)
4957 		chn.lastZxxParam = updateZxxParam;
4958 
4959 	// Macro string has been parsed and translated, now send the message(s)...
4960 	uint32 sendPos = 0;
4961 	uint8 runningStatus = 0;
4962 	while(sendPos < outPos)
4963 	{
4964 		uint32 sendLen = 0;
4965 		if(out[sendPos] == 0xF0)
4966 		{
4967 			// SysEx start
4968 			if((outPos - sendPos >= 4) && (out[sendPos + 1] == 0xF0 || out[sendPos + 1] == 0xF1))
4969 			{
4970 				// Internal macro (normal (F0F0) or extended (F0F1)), 4 bytes long
4971 				sendLen = 4;
4972 			} else
4973 			{
4974 				// SysEx message, find end of message
4975 				for(uint32 i = sendPos + 1; i < outPos; i++)
4976 				{
4977 					if(out[i] == 0xF7)
4978 					{
4979 						// Found end of SysEx message
4980 						sendLen = i - sendPos + 1;
4981 						break;
4982 					}
4983 				}
4984 				if(sendLen == 0)
4985 				{
4986 					// Didn't find end, so "invent" end of SysEx message
4987 					out[outPos++] = 0xF7;
4988 					sendLen = outPos - sendPos;
4989 				}
4990 			}
4991 		} else if(!(out[sendPos] & 0x80))
4992 		{
4993 			// Missing status byte? Try inserting running status
4994 			if(runningStatus != 0)
4995 			{
4996 				sendPos--;
4997 				out[sendPos] = runningStatus;
4998 			} else
4999 			{
5000 				// No running status to re-use; skip this byte
5001 				sendPos++;
5002 			}
5003 			continue;
5004 		} else
5005 		{
5006 			// Other MIDI messages
5007 			sendLen = std::min(static_cast<uint32>(MIDIEvents::GetEventLength(out[sendPos])), outPos - sendPos);
5008 		}
5009 
5010 		if(sendLen == 0)
5011 			break;
5012 
5013 		if(out[sendPos] < 0xF0)
5014 		{
5015 			runningStatus = out[sendPos];
5016 		}
5017 		uint32 bytesSent = SendMIDIData(nChn, isSmooth, out + sendPos, sendLen, plugin);
5018 		// If there's no error in the macro data (e.g. unrecognized internal MIDI macro), we have sendLen == bytesSent.
5019 		if(bytesSent > 0)
5020 			sendPos += bytesSent;
5021 		else
5022 			sendPos += sendLen;
5023 	}
5024 }
5025 
5026 
5027 // Calculate smooth MIDI macro slide parameter for current tick.
CalculateSmoothParamChange(float currentValue,float param) const5028 float CSoundFile::CalculateSmoothParamChange(float currentValue, float param) const
5029 {
5030 	MPT_ASSERT(m_PlayState.TicksOnRow() > m_PlayState.m_nTickCount);
5031 	const uint32 ticksLeft = m_PlayState.TicksOnRow() - m_PlayState.m_nTickCount;
5032 	if(ticksLeft > 1)
5033 	{
5034 		// Slide param
5035 		const float step = (param - currentValue) / (float)ticksLeft;
5036 		return (currentValue + step);
5037 	} else
5038 	{
5039 		// On last tick, set exact value.
5040 		return param;
5041 	}
5042 }
5043 
5044 
5045 // Process exactly one MIDI message parsed by ProcessMIDIMacro. Returns bytes sent on success, 0 on (parse) failure.
SendMIDIData(CHANNELINDEX nChn,bool isSmooth,const unsigned char * macro,uint32 macroLen,PLUGINDEX plugin)5046 uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned char *macro, uint32 macroLen, PLUGINDEX plugin)
5047 {
5048 	if(macroLen < 1)
5049 	{
5050 		return 0;
5051 	}
5052 
5053 	if(macro[0] == 0xFA || macro[0] == 0xFC || macro[0] == 0xFF)
5054 	{
5055 		// Start Song, Stop Song, MIDI Reset - both interpreted internally and sent to plugins
5056 		for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
5057 		{
5058 			m_PlayState.Chn[chn].nCutOff = 0x7F;
5059 			m_PlayState.Chn[chn].nResonance = 0x00;
5060 		}
5061 	}
5062 
5063 	ModChannel &chn = m_PlayState.Chn[nChn];
5064 	if(macro[0] == 0xF0 && (macro[1] == 0xF0 || macro[1] == 0xF1))
5065 	{
5066 		// Internal device.
5067 		if(macroLen < 4)
5068 		{
5069 			return 0;
5070 		}
5071 		const bool isExtended = (macro[1] == 0xF1);
5072 		const uint8 macroCode = macro[2];
5073 		const uint8 param = macro[3];
5074 
5075 		if(macroCode == 0x00 && !isExtended && param < 0x80)
5076 		{
5077 			// F0.F0.00.xx: Set CutOff
5078 			if(!isSmooth)
5079 			{
5080 				chn.nCutOff = param;
5081 			} else
5082 			{
5083 				chn.nCutOff = mpt::saturate_round<uint8>(CalculateSmoothParamChange(chn.nCutOff, param));
5084 			}
5085 			chn.nRestoreCutoffOnNewNote = 0;
5086 			int cutoff = SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]);
5087 
5088 			if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl)
5089 			{
5090 				// Cutoff doubles as modulator intensity for FM instruments
5091 				m_opl->Volume(nChn, static_cast<uint8>(cutoff / 4), true);
5092 			}
5093 
5094 			return 4;
5095 		} else if(macroCode == 0x01 && !isExtended && param < 0x80)
5096 		{
5097 			// F0.F0.01.xx: Set Resonance
5098 			if(!isSmooth)
5099 			{
5100 				chn.nResonance = param;
5101 			} else
5102 			{
5103 				chn.nResonance = (uint8)CalculateSmoothParamChange((float)chn.nResonance, (float)param);
5104 			}
5105 			chn.nRestoreResonanceOnNewNote = 0;
5106 			SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]);
5107 
5108 			return 4;
5109 		} else if(macroCode == 0x02 && !isExtended)
5110 		{
5111 			// F0.F0.02.xx: Set filter mode (high nibble determines filter mode)
5112 			if(param < 0x20)
5113 			{
5114 				chn.nFilterMode = static_cast<FilterMode>(param >> 4);
5115 				SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]);
5116 			}
5117 
5118 			return 4;
5119 #ifndef NO_PLUGINS
5120 		} else if(macroCode == 0x03 && !isExtended)
5121 		{
5122 			// F0.F0.03.xx: Set plug dry/wet
5123 			const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
5124 			if(plug > 0 && plug <= MAX_MIXPLUGINS && param < 0x80)
5125 			{
5126 				const float newRatio = (0x7F - (param & 0x7F)) / 127.0f;
5127 				if(!isSmooth)
5128 				{
5129 					m_MixPlugins[plug - 1].fDryRatio = newRatio;
5130 				} else
5131 				{
5132 					m_MixPlugins[plug - 1].fDryRatio = CalculateSmoothParamChange(m_MixPlugins[plug - 1].fDryRatio, newRatio);
5133 				}
5134 			}
5135 
5136 			return 4;
5137 		} else if((macroCode & 0x80) || isExtended)
5138 		{
5139 			// F0.F0.{80|n}.xx / F0.F1.n.xx: Set VST effect parameter n to xx
5140 			const PLUGINDEX plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
5141 			const uint32 plugParam = isExtended ? (0x80 + macroCode) : (macroCode & 0x7F);
5142 			if(plug > 0 && plug <= MAX_MIXPLUGINS)
5143 			{
5144 				IMixPlugin *pPlugin = m_MixPlugins[plug - 1].pMixPlugin;
5145 				if(pPlugin && param < 0x80)
5146 				{
5147 					const float fParam = param / 127.0f;
5148 					if(!isSmooth)
5149 					{
5150 						pPlugin->SetParameter(plugParam, fParam);
5151 					} else
5152 					{
5153 						pPlugin->SetParameter(plugParam, CalculateSmoothParamChange(pPlugin->GetParameter(plugParam), fParam));
5154 					}
5155 				}
5156 			}
5157 
5158 			return 4;
5159 #endif // NO_PLUGINS
5160 		}
5161 
5162 		// If we reach this point, the internal macro was invalid.
5163 
5164 	} else
5165 	{
5166 #ifndef NO_PLUGINS
5167 		// Not an internal device. Pass on to appropriate plugin.
5168 		const CHANNELINDEX plugChannel = (nChn < GetNumChannels()) ? nChn + 1 : chn.nMasterChn;
5169 		if(plugChannel > 0 && plugChannel <= GetNumChannels())	// XXX do we need this? I guess it might be relevant for previewing notes in the pattern... Or when using this mechanism for volume/panning!
5170 		{
5171 			PLUGINDEX plug = 0;
5172 			if(!chn.dwFlags[CHN_NOFX])
5173 			{
5174 				plug = (plugin != 0) ? plugin : GetBestPlugin(nChn, PrioritiseChannel, EvenIfMuted);
5175 			}
5176 
5177 			if(plug > 0 && plug <= MAX_MIXPLUGINS)
5178 			{
5179 				IMixPlugin *pPlugin = m_MixPlugins[plug - 1].pMixPlugin;
5180 				if (pPlugin != nullptr)
5181 				{
5182 					if(macro[0] == 0xF0)
5183 					{
5184 						pPlugin->MidiSysexSend(mpt::as_span(mpt::byte_cast<const std::byte*>(macro), macroLen));
5185 					} else
5186 					{
5187 						uint32 len = std::min(static_cast<uint32>(MIDIEvents::GetEventLength(macro[0])), macroLen);
5188 						uint32 curData = 0;
5189 						memcpy(&curData, macro, len);
5190 						pPlugin->MidiSend(curData);
5191 					}
5192 				}
5193 			}
5194 		}
5195 #else
5196 		MPT_UNREFERENCED_PARAMETER(plugin);
5197 #endif // NO_PLUGINS
5198 
5199 		return macroLen;
5200 	}
5201 
5202 	return 0;
5203 }
5204 
5205 
SendMIDINote(CHANNELINDEX chn,uint16 note,uint16 volume)5206 void CSoundFile::SendMIDINote(CHANNELINDEX chn, uint16 note, uint16 volume)
5207 {
5208 #ifndef NO_PLUGINS
5209 	auto &channel = m_PlayState.Chn[chn];
5210 	const ModInstrument *pIns = channel.pModInstrument;
5211 	// instro sends to a midi chan
5212 	if (pIns && pIns->HasValidMIDIChannel())
5213 	{
5214 		PLUGINDEX plug = pIns->nMixPlug;
5215 		if(plug > 0 && plug <= MAX_MIXPLUGINS)
5216 		{
5217 			IMixPlugin *pPlug = m_MixPlugins[plug - 1].pMixPlugin;
5218 			if (pPlug != nullptr)
5219 			{
5220 				pPlug->MidiCommand(*pIns, note, volume, chn);
5221 				if(note < NOTE_MIN_SPECIAL)
5222 					channel.nLeftVU = channel.nRightVU = 0xFF;
5223 			}
5224 		}
5225 	}
5226 #endif // NO_PLUGINS
5227 }
5228 
5229 
ProcessSampleOffset(ModChannel & chn,CHANNELINDEX nChn,const PlayState & playState) const5230 void CSoundFile::ProcessSampleOffset(ModChannel& chn, CHANNELINDEX nChn, const PlayState& playState) const
5231 {
5232 	const ModCommand &m = chn.rowCommand;
5233 	uint32 extendedRows = 0;
5234 	SmpLength offset = CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn, &extendedRows), highOffset = 0;
5235 	if(!extendedRows)
5236 	{
5237 		// No X-param (normal behaviour)
5238 		const bool isPercentageOffset = (m.volcmd == VOLCMD_OFFSET && m.vol == 0);
5239 		offset <<= 8;
5240 		if(offset)
5241 			chn.oldOffset = offset;
5242 		else if(m.volcmd != VOLCMD_OFFSET)
5243 			offset = chn.oldOffset;
5244 
5245 		if(!isPercentageOffset)
5246 			highOffset = static_cast<SmpLength>(chn.nOldHiOffset) << 16;
5247 	}
5248 	if(m.volcmd == VOLCMD_OFFSET)
5249 	{
5250 		if(m.vol == 0)
5251 			offset = Util::muldivr_unsigned(chn.nLength, offset, 256u << (8u * std::max(uint32(1), extendedRows)));  // o00 + Oxx = Percentage Offset
5252 		else if(m.vol <= std::size(ModSample().cues) && chn.pModSample != nullptr)
5253 			offset += chn.pModSample->cues[m.vol - 1];  // Offset relative to cue point
5254 		chn.oldOffset = offset;
5255 	}
5256 	SampleOffset(chn, offset + highOffset);
5257 }
5258 
5259 
SampleOffset(ModChannel & chn,SmpLength param) const5260 void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const
5261 {
5262 	// ST3 compatibility: Instrument-less note recalls previous note's offset
5263 	// Test case: OxxMemory.s3m
5264 	if(m_playBehaviour[kST3OffsetWithoutInstrument])
5265 		chn.prevNoteOffset = 0;
5266 
5267 	chn.prevNoteOffset += param;
5268 
5269 	if(param >= chn.nLoopEnd && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_MTM)) && chn.dwFlags[CHN_LOOP] && chn.nLoopEnd > 0)
5270 	{
5271 		// Offset wrap-around
5272 		// Note that ST3 only does this in GUS mode. SoundBlaster stops the sample entirely instead.
5273 		// Test case: OffsetLoopWraparound.s3m
5274 		param = (param - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart) + chn.nLoopStart;
5275 	}
5276 
5277 	if(GetType() == MOD_TYPE_MDL && chn.dwFlags[CHN_16BIT])
5278 	{
5279 		// Digitrakker really uses byte offsets, not sample offsets. WTF!
5280 		param /= 2u;
5281 	}
5282 
5283 	if(chn.rowCommand.IsNote() || m_playBehaviour[kApplyOffsetWithoutNote])
5284 	{
5285 		// IT compatibility: If this note is not mapped to a sample, ignore it.
5286 		// Test case: empty_sample_offset.it
5287 		if(chn.pModInstrument != nullptr && chn.rowCommand.IsNote())
5288 		{
5289 			SAMPLEINDEX smp = chn.pModInstrument->Keyboard[chn.rowCommand.note - NOTE_MIN];
5290 			if(smp == 0 || smp > GetNumSamples())
5291 				return;
5292 		}
5293 
5294 		if(m_SongFlags[SONG_PT_MODE])
5295 		{
5296 			// ProTracker compatbility: PT1/2-style funky 9xx offset command
5297 			// Test case: ptoffset.mod
5298 			chn.position.Set(chn.prevNoteOffset);
5299 			chn.prevNoteOffset += param;
5300 		} else
5301 		{
5302 			chn.position.Set(param);
5303 		}
5304 
5305 		if (chn.position.GetUInt() >= chn.nLength || (chn.dwFlags[CHN_LOOP] && chn.position.GetUInt() >= chn.nLoopEnd))
5306 		{
5307 			// Offset beyond sample size
5308 			if(m_playBehaviour[kFT2ST3OffsetOutOfRange] || GetType() == MOD_TYPE_MTM)
5309 			{
5310 				// FT2 Compatibility: Don't play note if offset is beyond sample length
5311 				// ST3 Compatibility: Don't play note if offset is beyond sample length (non-looped samples only)
5312 				// Test cases: 3xx-no-old-samp.xm, OffsetPastSampleEnd.s3m
5313 				chn.dwFlags.set(CHN_FASTVOLRAMP);
5314 				chn.nPeriod = 0;
5315 			} else if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MOD)))
5316 			{
5317 				// IT Compatibility: Offset
5318 				if(m_playBehaviour[kITOffset])
5319 				{
5320 					if(m_SongFlags[SONG_ITOLDEFFECTS])
5321 						chn.position.Set(chn.nLength); // Old FX: Clip to end of sample
5322 					else
5323 						chn.position.Set(0); // Reset to beginning of sample
5324 				} else
5325 				{
5326 					chn.position.Set(chn.nLoopStart);
5327 					if(m_SongFlags[SONG_ITOLDEFFECTS] && chn.nLength > 4)
5328 					{
5329 						chn.position.Set(chn.nLength - 2);
5330 					}
5331 				}
5332 			} else if(GetType() == MOD_TYPE_MOD && chn.dwFlags[CHN_LOOP])
5333 			{
5334 				chn.position.Set(chn.nLoopStart);
5335 			}
5336 		}
5337 	} else if ((param < chn.nLength) && (GetType() & (MOD_TYPE_MTM | MOD_TYPE_DMF | MOD_TYPE_MDL | MOD_TYPE_PLM)))
5338 	{
5339 		// Some trackers can also call offset effects without notes next to them...
5340 		chn.position.Set(param);
5341 	}
5342 }
5343 
5344 
ReverseSampleOffset(ModChannel & chn,ModCommand::PARAM param) const5345 void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) const
5346 {
5347 	if(chn.pModSample != nullptr && chn.pModSample->nLength > 0)
5348 	{
5349 		chn.dwFlags.set(CHN_PINGPONGFLAG);
5350 		chn.dwFlags.reset(CHN_LOOP);
5351 		chn.nLength = chn.pModSample->nLength;	// If there was a loop, extend sample to whole length.
5352 		chn.position.Set((chn.nLength - 1) - std::min(SmpLength(param) << 8, chn.nLength - SmpLength(1)), 0);
5353 	}
5354 }
5355 
5356 
RetrigNote(CHANNELINDEX nChn,int param,int offset)5357 void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset)
5358 {
5359 	// Retrig: bit 8 is set if it's the new XM retrig
5360 	ModChannel &chn = m_PlayState.Chn[nChn];
5361 	int retrigSpeed = param & 0x0F;
5362 	uint8 retrigCount = chn.nRetrigCount;
5363 	bool doRetrig = false;
5364 
5365 	// IT compatibility 15. Retrigger
5366 	if(m_playBehaviour[kITRetrigger])
5367 	{
5368 		if(m_PlayState.m_nTickCount == 0 && chn.rowCommand.note)
5369 		{
5370 			chn.nRetrigCount = param & 0x0F;
5371 		} else if(!chn.nRetrigCount || !--chn.nRetrigCount)
5372 		{
5373 			chn.nRetrigCount = param & 0x0F;
5374 			doRetrig = true;
5375 		}
5376 	} else if(m_playBehaviour[kFT2Retrigger] && (param & 0x100))
5377 	{
5378 		// Buggy-like-hell FT2 Rxy retrig!
5379 		// Test case: retrig.xm
5380 		if(m_SongFlags[SONG_FIRSTTICK])
5381 		{
5382 			// Here are some really stupid things FT2 does on the first tick.
5383 			// Test case: RetrigTick0.xm
5384 			if(chn.rowCommand.instr > 0 && chn.rowCommand.IsNoteOrEmpty())
5385 				retrigCount = 1;
5386 			if(chn.rowCommand.volcmd == VOLCMD_VOLUME && chn.rowCommand.vol != 0)
5387 			{
5388 				// I guess this condition simply checked if the volume byte was != 0 in FT2.
5389 				chn.nRetrigCount = retrigCount;
5390 				return;
5391 			}
5392 		}
5393 		if(retrigCount >= retrigSpeed)
5394 		{
5395 			if(!m_SongFlags[SONG_FIRSTTICK] || !chn.rowCommand.IsNote())
5396 			{
5397 				doRetrig = true;
5398 				retrigCount = 0;
5399 			}
5400 		}
5401 	} else
5402 	{
5403 		// old routines
5404 		if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))
5405 		{
5406 			if(!retrigSpeed)
5407 				retrigSpeed = 1;
5408 			if(retrigCount && !(retrigCount % retrigSpeed))
5409 				doRetrig = true;
5410 			retrigCount++;
5411 		} else if(GetType() == MOD_TYPE_MOD)
5412 		{
5413 			// ProTracker-style retrigger
5414 			// Test case: PTRetrigger.mod
5415 			const auto tick = m_PlayState.m_nTickCount % m_PlayState.m_nMusicSpeed;
5416 			if(!tick && chn.rowCommand.IsNote())
5417 				return;
5418 			if(retrigSpeed && !(tick % retrigSpeed))
5419 				doRetrig = true;
5420 		} else if(GetType() == MOD_TYPE_MTM)
5421 		{
5422 			// In MultiTracker, E9x retriggers the last note at exactly the x-th tick of the row
5423 			doRetrig = m_PlayState.m_nTickCount == static_cast<uint32>(param & 0x0F) && retrigSpeed != 0;
5424 		} else
5425 		{
5426 			int realspeed = retrigSpeed;
5427 			// FT2 bug: if a retrig (Rxy) occurs together with a volume command, the first retrig interval is increased by one tick
5428 			if((param & 0x100) && (chn.rowCommand.volcmd == VOLCMD_VOLUME) && (chn.rowCommand.param & 0xF0))
5429 				realspeed++;
5430 			if(!m_SongFlags[SONG_FIRSTTICK] || (param & 0x100))
5431 			{
5432 				if(!realspeed)
5433 					realspeed = 1;
5434 				if(!(param & 0x100) && m_PlayState.m_nMusicSpeed && !(m_PlayState.m_nTickCount % realspeed))
5435 					doRetrig = true;
5436 				retrigCount++;
5437 			} else if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
5438 				retrigCount = 0;
5439 			if (retrigCount >= realspeed)
5440 			{
5441 				if(m_PlayState.m_nTickCount || ((param & 0x100) && !chn.rowCommand.note))
5442 					doRetrig = true;
5443 			}
5444 			if(m_playBehaviour[kFT2Retrigger] && param == 0)
5445 			{
5446 				// E90 = Retrig instantly, and only once
5447 				doRetrig = (m_PlayState.m_nTickCount == 0);
5448 			}
5449 		}
5450 	}
5451 
5452 	// IT compatibility: If a sample is shorter than the retrig time (i.e. it stops before the retrig counter hits zero), it is not retriggered.
5453 	// Test case: retrig-short.it
5454 	if(chn.nLength == 0 && m_playBehaviour[kITShortSampleRetrig] && !chn.HasMIDIOutput())
5455 		return;
5456 	// ST3 compatibility: No retrig after Note Cut
5457 	// Test case: RetrigAfterNoteCut.s3m
5458 	if(m_playBehaviour[kST3RetrigAfterNoteCut] && !chn.nFadeOutVol)
5459 		return;
5460 
5461 	if(doRetrig)
5462 	{
5463 		uint32 dv = (param >> 4) & 0x0F;
5464 		int vol = chn.nVolume;
5465 		if(dv)
5466 		{
5467 
5468 			// FT2 compatibility: Retrig + volume will not change volume of retrigged notes
5469 			if(!m_playBehaviour[kFT2Retrigger] || !(chn.rowCommand.volcmd == VOLCMD_VOLUME))
5470 			{
5471 				if(retrigTable1[dv])
5472 					vol = (vol * retrigTable1[dv]) / 16;
5473 				else
5474 					vol += ((int)retrigTable2[dv]) * 4;
5475 			}
5476 			Limit(vol, 0, 256);
5477 
5478 			chn.dwFlags.set(CHN_FASTVOLRAMP);
5479 		}
5480 		uint32 note = chn.nNewNote;
5481 		int32 oldPeriod = chn.nPeriod;
5482 		// ST3 doesn't retrigger OPL notes
5483 		if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && (!chn.dwFlags[CHN_ADLIB] || GetType() != MOD_TYPE_S3M || m_playBehaviour[kOPLRealRetrig]))
5484 			CheckNNA(nChn, 0, note, true);
5485 		bool resetEnv = false;
5486 		if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
5487 		{
5488 			if(chn.rowCommand.instr && param < 0x100)
5489 			{
5490 				InstrumentChange(chn, chn.rowCommand.instr, false, false);
5491 				resetEnv = true;
5492 			}
5493 			if(param < 0x100)
5494 				resetEnv = true;
5495 		}
5496 
5497 		const bool fading = chn.dwFlags[CHN_NOTEFADE];
5498 		const auto oldPrevNoteOffset = chn.prevNoteOffset;
5499 		chn.prevNoteOffset = 0;  // Retriggered notes should not use previous offset (test case: OxxMemoryWithRetrig.s3m)
5500 		// IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase)
5501 		// Test case: retrig.it
5502 		NoteChange(chn, note, m_playBehaviour[kITRetrigger], resetEnv, false, nChn);
5503 		if(!chn.rowCommand.instr)
5504 			chn.prevNoteOffset = oldPrevNoteOffset;
5505 		// XM compatibility: Prevent NoteChange from resetting the fade flag in case an instrument number + note-off is present.
5506 		// Test case: RetrigFade.xm
5507 		if(fading && GetType() == MOD_TYPE_XM)
5508 			chn.dwFlags.set(CHN_NOTEFADE);
5509 		chn.nVolume = vol;
5510 		if(m_nInstruments)
5511 		{
5512 			chn.rowCommand.note = static_cast<ModCommand::NOTE>(note);	// No retrig without note...
5513 #ifndef NO_PLUGINS
5514 			ProcessMidiOut(nChn);	//Send retrig to Midi
5515 #endif // NO_PLUGINS
5516 		}
5517 		if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && chn.rowCommand.note == NOTE_NONE && oldPeriod != 0)
5518 			chn.nPeriod = oldPeriod;
5519 		if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)))
5520 			retrigCount = 0;
5521 		// IT compatibility: see previous IT compatibility comment =)
5522 		if(m_playBehaviour[kITRetrigger]) chn.position.Set(0);
5523 
5524 		offset--;
5525 		if(chn.pModSample != nullptr && offset >= 0 && offset <= static_cast<int>(std::size(chn.pModSample->cues)))
5526 		{
5527 			if(offset == 0)
5528 				offset = chn.oldOffset;
5529 			else
5530 				offset = chn.oldOffset = chn.pModSample->cues[offset - 1];
5531 			SampleOffset(chn, offset);
5532 		}
5533 	}
5534 
5535 	// buggy-like-hell FT2 Rxy retrig!
5536 	if(m_playBehaviour[kFT2Retrigger] && (param & 0x100))
5537 		retrigCount++;
5538 
5539 	// Now we can also store the retrig value for IT...
5540 	if(!m_playBehaviour[kITRetrigger])
5541 		chn.nRetrigCount = retrigCount;
5542 }
5543 
5544 
5545 // Execute a frequency slide on given channel.
5546 // Positive amounts increase the frequency, negative amounts decrease it.
5547 // The period or frequency that is read and written is in the period variable, chn.nPeriod is not touched.
DoFreqSlide(ModChannel & chn,int32 & period,int32 amount,bool isTonePorta) const5548 void CSoundFile::DoFreqSlide(ModChannel &chn, int32 &period, int32 amount, bool isTonePorta) const
5549 {
5550 	if(!period || !amount)
5551 		return;
5552 	MPT_ASSERT(!chn.HasCustomTuning());
5553 
5554 	if(GetType() == MOD_TYPE_669)
5555 	{
5556 		// Like other oldskool trackers, Composer 669 doesn't have linear slides...
5557 		// But the slides are done in Hertz rather than periods, meaning that they
5558 		// are more effective in the lower notes (rather than the higher notes).
5559 		period += amount * 20;
5560 	} else if(GetType() == MOD_TYPE_FAR)
5561 	{
5562 		period += (amount * 36318 / 1024);
5563 	} else if(m_SongFlags[SONG_LINEARSLIDES] && GetType() != MOD_TYPE_XM)
5564 	{
5565 		// IT Linear slides
5566 		const auto oldPeriod = period;
5567 		uint32 n = std::abs(amount);
5568 		LimitMax(n, 255u * 4u);
5569 
5570 		// Note: IT ignores the lower 2 bits when abs(mount) > 16 (it either uses the fine *or* the regular table, not both)
5571 		// This means that vibratos are slightly less accurate in this range than they could be.
5572 		// Other code paths will *either* have an amount that's a multiple of 4 *or* it's less than 16.
5573 		if(amount > 0)
5574 		{
5575 			if(n < 16)
5576 				period = Util::muldivr(period, GetFineLinearSlideUpTable(this, n), 65536);
5577 			else
5578 				period = Util::muldivr(period, GetLinearSlideUpTable(this, n / 4u), 65536);
5579 		} else
5580 		{
5581 			if(n < 16)
5582 				period = Util::muldivr(period, GetFineLinearSlideDownTable(this, n), 65536);
5583 			else
5584 				period = Util::muldivr(period, GetLinearSlideDownTable(this, n / 4u), 65536);
5585 		}
5586 
5587 		if(period == oldPeriod)
5588 		{
5589 			const bool incPeriod = m_playBehaviour[kPeriodsAreHertz] == (amount > 0);
5590 			if(incPeriod && period < Util::MaxValueOfType(period))
5591 				period++;
5592 			else if(!incPeriod && period > 1)
5593 				period--;
5594 		}
5595 	} else if(!m_SongFlags[SONG_LINEARSLIDES] && m_playBehaviour[kPeriodsAreHertz])
5596 	{
5597 		// IT Amiga slides
5598 		if(amount < 0)
5599 		{
5600 			// Go down
5601 			period = mpt::saturate_cast<int32>(Util::mul32to64_unsigned(1712 * 8363, period) / (Util::mul32to64_unsigned(period, -amount) + 1712 * 8363));
5602 		} else if(amount > 0)
5603 		{
5604 			// Go up
5605 			const auto periodDiv = 1712 * 8363 - Util::mul32to64(period, amount);
5606 			if(periodDiv <= 0)
5607 			{
5608 				if(isTonePorta)
5609 				{
5610 					period = int32_max;
5611 					return;
5612 				} else
5613 				{
5614 					period = 0;
5615 					chn.nFadeOutVol = 0;
5616 					chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP);
5617 				}
5618 				return;
5619 			}
5620 			period = mpt::saturate_cast<int32>(Util::mul32to64_unsigned(1712 * 8363, period) / periodDiv);
5621 		}
5622 	} else
5623 	{
5624 		period -= amount;
5625 	}
5626 	if(period < 1)
5627 	{
5628 		period = 1;
5629 		if(GetType() == MOD_TYPE_S3M && !isTonePorta)
5630 		{
5631 			chn.nFadeOutVol = 0;
5632 			chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP);
5633 		}
5634 	}
5635 }
5636 
5637 
NoteCut(CHANNELINDEX nChn,uint32 nTick,bool cutSample)5638 void CSoundFile::NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample)
5639 {
5640 	if (m_PlayState.m_nTickCount == nTick)
5641 	{
5642 		ModChannel &chn = m_PlayState.Chn[nChn];
5643 		if(cutSample)
5644 		{
5645 			chn.increment.Set(0);
5646 			chn.nFadeOutVol = 0;
5647 			chn.dwFlags.set(CHN_NOTEFADE);
5648 		} else
5649 		{
5650 			chn.nVolume = 0;
5651 		}
5652 		chn.dwFlags.set(CHN_FASTVOLRAMP);
5653 
5654 		// instro sends to a midi chan
5655 		SendMIDINote(nChn, /*chn.nNote+*/NOTE_MAX_SPECIAL, 0);
5656 
5657 		if(chn.dwFlags[CHN_ADLIB] && m_opl)
5658 		{
5659 			m_opl->NoteCut(nChn, false);
5660 		}
5661 	}
5662 }
5663 
5664 
KeyOff(ModChannel & chn) const5665 void CSoundFile::KeyOff(ModChannel &chn) const
5666 {
5667 	const bool keyIsOn = !chn.dwFlags[CHN_KEYOFF];
5668 	chn.dwFlags.set(CHN_KEYOFF);
5669 	if(chn.pModInstrument != nullptr && !chn.VolEnv.flags[ENV_ENABLED])
5670 	{
5671 		chn.dwFlags.set(CHN_NOTEFADE);
5672 	}
5673 	if (!chn.nLength) return;
5674 	if (chn.dwFlags[CHN_SUSTAINLOOP] && chn.pModSample && keyIsOn)
5675 	{
5676 		const ModSample *pSmp = chn.pModSample;
5677 		if(pSmp->uFlags[CHN_LOOP])
5678 		{
5679 			if (pSmp->uFlags[CHN_PINGPONGLOOP])
5680 				chn.dwFlags.set(CHN_PINGPONGLOOP);
5681 			else
5682 				chn.dwFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGFLAG);
5683 			chn.dwFlags.set(CHN_LOOP);
5684 			chn.nLength = pSmp->nLength;
5685 			chn.nLoopStart = pSmp->nLoopStart;
5686 			chn.nLoopEnd = pSmp->nLoopEnd;
5687 			if (chn.nLength > chn.nLoopEnd) chn.nLength = chn.nLoopEnd;
5688 			if(chn.position.GetUInt() > chn.nLength)
5689 			{
5690 				// Test case: SusAfterLoop.it
5691 				chn.position.Set(chn.nLoopStart + ((chn.position.GetInt() - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart)));
5692 			}
5693 		} else
5694 		{
5695 			chn.dwFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_PINGPONGFLAG);
5696 			chn.nLength = pSmp->nLength;
5697 		}
5698 	}
5699 
5700 	if (chn.pModInstrument)
5701 	{
5702 		const ModInstrument *pIns = chn.pModInstrument;
5703 		if((pIns->VolEnv.dwFlags[ENV_LOOP] || (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MDL))) && pIns->nFadeOut != 0)
5704 		{
5705 			chn.dwFlags.set(CHN_NOTEFADE);
5706 		}
5707 
5708 		if (pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET && chn.VolEnv.nEnvValueAtReleaseJump == NOT_YET_RELEASED)
5709 		{
5710 			chn.VolEnv.nEnvValueAtReleaseJump = mpt::saturate_cast<int16>(pIns->VolEnv.GetValueFromPosition(chn.VolEnv.nEnvPosition, 256));
5711 			chn.VolEnv.nEnvPosition = pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick;
5712 		}
5713 	}
5714 }
5715 
5716 
5717 //////////////////////////////////////////////////////////
5718 // CSoundFile: Global Effects
5719 
5720 
SetSpeed(PlayState & playState,uint32 param) const5721 void CSoundFile::SetSpeed(PlayState &playState, uint32 param) const
5722 {
5723 #ifdef MODPLUG_TRACKER
5724 	// FT2 appears to be decrementing the tick count before checking for zero,
5725 	// so it effectively counts down 65536 ticks with speed = 0 (song speed is a 16-bit variable in FT2)
5726 	if(GetType() == MOD_TYPE_XM && !param)
5727 	{
5728 		playState.m_nMusicSpeed = uint16_max;
5729 	}
5730 #endif	// MODPLUG_TRACKER
5731 	if(param > 0) playState.m_nMusicSpeed = param;
5732 	if(GetType() == MOD_TYPE_STM && param > 0)
5733 	{
5734 		playState.m_nMusicSpeed = std::max(param >> 4, uint32(1));
5735 		playState.m_nMusicTempo = ConvertST2Tempo(static_cast<uint8>(param));
5736 	}
5737 }
5738 
5739 
5740 // Convert a ST2 tempo byte to classic tempo and speed combination
ConvertST2Tempo(uint8 tempo)5741 TEMPO CSoundFile::ConvertST2Tempo(uint8 tempo)
5742 {
5743 	static constexpr uint8 ST2TempoFactor[] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 };
5744 	static constexpr uint32 st2MixingRate = 23863; // Highest possible setting in ST2
5745 
5746 	// This underflows at tempo 06...0F, and the resulting tick lengths depend on the mixing rate.
5747 	// Note: ST2.3 uses the constant 50 below, earlier versions use 49 but they also play samples at a different speed.
5748 	int32 samplesPerTick = st2MixingRate / (50 - ((ST2TempoFactor[tempo >> 4u] * (tempo & 0x0F)) >> 4u));
5749 	if(samplesPerTick <= 0)
5750 		samplesPerTick += 65536;
5751 	return TEMPO().SetRaw(Util::muldivrfloor(st2MixingRate, 5 * TEMPO::fractFact, samplesPerTick * 2));
5752 }
5753 
5754 
SetTempo(TEMPO param,bool setFromUI)5755 void CSoundFile::SetTempo(TEMPO param, bool setFromUI)
5756 {
5757 	const CModSpecifications &specs = GetModSpecifications();
5758 
5759 	// Anything lower than the minimum tempo is considered to be a tempo slide
5760 	const TEMPO minTempo = (GetType() & (MOD_TYPE_MDL | MOD_TYPE_MED)) ? TEMPO(1, 0) : TEMPO(32, 0);
5761 
5762 	if(setFromUI)
5763 	{
5764 		// Set tempo from UI - ignore slide commands and such.
5765 		m_PlayState.m_nMusicTempo = Clamp(param, specs.GetTempoMin(), specs.GetTempoMax());
5766 	} else if(param >= minTempo && m_SongFlags[SONG_FIRSTTICK] == !m_playBehaviour[kMODTempoOnSecondTick])
5767 	{
5768 		// ProTracker sets the tempo after the first tick.
5769 		// Note: The case of one tick per row is handled in ProcessRow() instead.
5770 		// Test case: TempoChange.mod
5771 		m_PlayState.m_nMusicTempo = std::min(param, specs.GetTempoMax());
5772 	} else if(param < minTempo && !m_SongFlags[SONG_FIRSTTICK])
5773 	{
5774 		// Tempo Slide
5775 		TEMPO tempDiff(param.GetInt() & 0x0F, 0);
5776 		if((param.GetInt() & 0xF0) == 0x10)
5777 			m_PlayState.m_nMusicTempo += tempDiff;
5778 		else
5779 			m_PlayState.m_nMusicTempo -= tempDiff;
5780 
5781 		TEMPO tempoMin = specs.GetTempoMin(), tempoMax = specs.GetTempoMax();
5782 		if(m_playBehaviour[kTempoClamp])	// clamp tempo correctly in compatible mode
5783 		{
5784 			tempoMax.Set(255);
5785 		}
5786 		Limit(m_PlayState.m_nMusicTempo, tempoMin, tempoMax);
5787 	}
5788 }
5789 
5790 
PatternLoop(PlayState & state,ModChannel & chn,ModCommand::PARAM param) const5791 void CSoundFile::PatternLoop(PlayState &state, ModChannel &chn, ModCommand::PARAM param) const
5792 {
5793 	if(m_playBehaviour[kST3NoMutedChannels] && chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE])
5794 		return;  // not even effects are processed on muted S3M channels
5795 
5796 	if(!param)
5797 	{
5798 		// Loop Start
5799 		chn.nPatternLoop = state.m_nRow;
5800 		return;
5801 	}
5802 
5803 	// Loop Repeat
5804 	if(chn.nPatternLoopCount)
5805 	{
5806 		// There's a loop left
5807 		chn.nPatternLoopCount--;
5808 		if(!chn.nPatternLoopCount)
5809 		{
5810 			// IT compatibility 10. Pattern loops (+ same fix for S3M files)
5811 			// When finishing a pattern loop, the next loop without a dedicated SB0 starts on the first row after the previous loop.
5812 			if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M))
5813 				chn.nPatternLoop = state.m_nRow + 1;
5814 
5815 			return;
5816 		}
5817 	} else
5818 	{
5819 		// First time we get into the loop => Set loop count.
5820 
5821 		// IT compatibility 10. Pattern loops (+ same fix for XM / MOD / S3M files)
5822 		if(!m_playBehaviour[kITFT2PatternLoop] && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_S3M)))
5823 		{
5824 			auto p = std::cbegin(state.Chn);
5825 			for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, p++)
5826 			{
5827 				// Loop on other channel
5828 				if(p != &chn && p->nPatternLoopCount)
5829 					return;
5830 			}
5831 		}
5832 		chn.nPatternLoopCount = param;
5833 	}
5834 	state.m_nextPatStartRow = chn.nPatternLoop;  // Nasty FT2 E60 bug emulation!
5835 
5836 	const auto loopTarget = chn.nPatternLoop;
5837 	if(loopTarget != ROWINDEX_INVALID)
5838 	{
5839 		// FT2 compatibility: E6x overwrites jump targets of Dxx effects that are located left of the E6x effect.
5840 		// Test cases: PatLoop-Jumps.xm, PatLoop-Various.xm
5841 		if(state.m_breakRow != ROWINDEX_INVALID && m_playBehaviour[kFT2PatternLoopWithJumps])
5842 			state.m_breakRow = loopTarget;
5843 
5844 		state.m_patLoopRow = loopTarget;
5845 		// IT compatibility: SBx is prioritized over Position Jump (Bxx) effects that are located left of the SBx effect.
5846 		// Test case: sbx-priority.it, LoopBreak.it
5847 		if(m_playBehaviour[kITPatternLoopWithJumps])
5848 			state.m_posJump = ORDERINDEX_INVALID;
5849 	}
5850 
5851 	if(GetType() == MOD_TYPE_S3M)
5852 	{
5853 		// ST3 doesn't have per-channel pattern loop memory, so spam all changes to other channels as well.
5854 		for(CHANNELINDEX i = 0; i < GetNumChannels(); i++)
5855 		{
5856 			state.Chn[i].nPatternLoop = chn.nPatternLoop;
5857 			state.Chn[i].nPatternLoopCount = chn.nPatternLoopCount;
5858 		}
5859 	}
5860 
5861 }
5862 
5863 
GlobalVolSlide(ModCommand::PARAM param,uint8 & nOldGlobalVolSlide)5864 void CSoundFile::GlobalVolSlide(ModCommand::PARAM param, uint8 &nOldGlobalVolSlide)
5865 {
5866 	int32 nGlbSlide = 0;
5867 	if (param) nOldGlobalVolSlide = param; else param = nOldGlobalVolSlide;
5868 
5869 	if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)))
5870 	{
5871 		// XM nibble priority
5872 		if((param & 0xF0) != 0)
5873 		{
5874 			param &= 0xF0;
5875 		} else
5876 		{
5877 			param &= 0x0F;
5878 		}
5879 	}
5880 
5881 	if (((param & 0x0F) == 0x0F) && (param & 0xF0))
5882 	{
5883 		if(m_SongFlags[SONG_FIRSTTICK]) nGlbSlide = (param >> 4) * 2;
5884 	} else
5885 	if (((param & 0xF0) == 0xF0) && (param & 0x0F))
5886 	{
5887 		if(m_SongFlags[SONG_FIRSTTICK]) nGlbSlide = - (int)((param & 0x0F) * 2);
5888 	} else
5889 	{
5890 		if(!m_SongFlags[SONG_FIRSTTICK])
5891 		{
5892 			if (param & 0xF0)
5893 			{
5894 				// IT compatibility: Ignore slide commands with both nibbles set.
5895 				if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM)) || (param & 0x0F) == 0)
5896 					nGlbSlide = (int)((param & 0xF0) >> 4) * 2;
5897 			} else
5898 			{
5899 				nGlbSlide = -(int)((param & 0x0F) * 2);
5900 			}
5901 		}
5902 	}
5903 	if (nGlbSlide)
5904 	{
5905 		if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM))) nGlbSlide *= 2;
5906 		nGlbSlide += m_PlayState.m_nGlobalVolume;
5907 		Limit(nGlbSlide, 0, 256);
5908 		m_PlayState.m_nGlobalVolume = nGlbSlide;
5909 	}
5910 }
5911 
5912 
5913 //////////////////////////////////////////////////////
5914 // Note/Period/Frequency functions
5915 
5916 // Find lowest note which has same or lower period as a given period (i.e. the note has the same or higher frequency)
GetNoteFromPeriod(uint32 period,int32 nFineTune,uint32 nC5Speed) const5917 uint32 CSoundFile::GetNoteFromPeriod(uint32 period, int32 nFineTune, uint32 nC5Speed) const
5918 {
5919 	if(!period) return 0;
5920 	if(m_playBehaviour[kFT2Periods])
5921 	{
5922 		// FT2's "RelocateTon" function actually rounds up and down, while GetNoteFromPeriod normally just truncates.
5923 		nFineTune += 64;
5924 	}
5925 	// This essentially implements std::lower_bound, with the difference that we don't need an iterable container.
5926 	uint32 minNote = NOTE_MIN, maxNote = NOTE_MAX, count = maxNote - minNote + 1;
5927 	const bool periodIsFreq = PeriodsAreFrequencies();
5928 	while(count > 0)
5929 	{
5930 		const uint32 step = count / 2, midNote = minNote + step;
5931 		uint32 n = GetPeriodFromNote(midNote, nFineTune, nC5Speed);
5932 		if((n > period && !periodIsFreq) || (n < period && periodIsFreq) || !n)
5933 		{
5934 			minNote = midNote + 1;
5935 			count -= step + 1;
5936 		} else
5937 		{
5938 			count = step;
5939 		}
5940 	}
5941 	return minNote;
5942 }
5943 
5944 
GetPeriodFromNote(uint32 note,int32 nFineTune,uint32 nC5Speed) const5945 uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Speed) const
5946 {
5947 	if (note == NOTE_NONE || (note >= NOTE_MIN_SPECIAL)) return 0;
5948 	note -= NOTE_MIN;
5949 	if(!UseFinetuneAndTranspose())
5950 	{
5951 		if(GetType() & (MOD_TYPE_MDL | MOD_TYPE_DTM))
5952 		{
5953 			// MDL uses non-linear slides, but their effectiveness does not depend on the middle-C frequency.
5954 			return (FreqS3MTable[note % 12u] << 4) >> (note / 12);
5955 		}
5956 		if(!nC5Speed)
5957 			nC5Speed = 8363;
5958 		if(PeriodsAreFrequencies())
5959 		{
5960 			// Compute everything in Hertz rather than periods.
5961 			uint32 freq = Util::muldiv_unsigned(nC5Speed, LinearSlideUpTable[(note % 12u) * 16u] << (note / 12u), 65536 << 5);
5962 			LimitMax(freq, static_cast<uint32>(int32_max));
5963 			return freq;
5964 		} else if(m_SongFlags[SONG_LINEARSLIDES])
5965 		{
5966 			return (FreqS3MTable[note % 12u] << 5) >> (note / 12);
5967 		} else
5968 		{
5969 			LimitMax(nC5Speed, uint32_max >> (note / 12u));
5970 			//(a*b)/c
5971 			return Util::muldiv_unsigned(8363, (FreqS3MTable[note % 12u] << 5), nC5Speed << (note / 12u));
5972 			//8363 * freq[note%12] / nC5Speed * 2^(5-note/12)
5973 		}
5974 	} else if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM))
5975 	{
5976 		if (note < 12) note = 12;
5977 		note -= 12;
5978 
5979 		if(GetType() == MOD_TYPE_MTM)
5980 		{
5981 			nFineTune *= 16;
5982 		} else if(m_playBehaviour[kFT2FinetunePrecision])
5983 		{
5984 			// FT2 Compatibility: The lower three bits of the finetune are truncated.
5985 			// Test case: Finetune-Precision.xm
5986 			nFineTune &= ~7;
5987 		}
5988 
5989 		if(m_SongFlags[SONG_LINEARSLIDES])
5990 		{
5991 			int l = ((NOTE_MAX - note) << 6) - (nFineTune / 2);
5992 			if (l < 1) l = 1;
5993 			return static_cast<uint32>(l);
5994 		} else
5995 		{
5996 			int finetune = nFineTune;
5997 			uint32 rnote = (note % 12) << 3;
5998 			uint32 roct = note / 12;
5999 			int rfine = finetune / 16;
6000 			int i = rnote + rfine + 8;
6001 			Limit(i , 0, 103);
6002 			uint32 per1 = XMPeriodTable[i];
6003 			if(finetune < 0)
6004 			{
6005 				rfine--;
6006 				finetune = -finetune;
6007 			} else rfine++;
6008 			i = rnote+rfine+8;
6009 			if (i < 0) i = 0;
6010 			if (i >= 104) i = 103;
6011 			uint32 per2 = XMPeriodTable[i];
6012 			rfine = finetune & 0x0F;
6013 			per1 *= 16-rfine;
6014 			per2 *= rfine;
6015 			return ((per1 + per2) << 1) >> roct;
6016 		}
6017 	} else
6018 	{
6019 		nFineTune = XM2MODFineTune(nFineTune);
6020 		if ((nFineTune) || (note < 24) || (note >= 24 + std::size(ProTrackerPeriodTable)))
6021 			return (ProTrackerTunedPeriods[nFineTune * 12u + note % 12u] << 5) >> (note / 12u);
6022 		else
6023 			return (ProTrackerPeriodTable[note - 24] << 2);
6024 	}
6025 }
6026 
6027 
6028 // Converts period value to sample frequency. Return value is fixed point, with FREQ_FRACBITS fractional bits.
GetFreqFromPeriod(uint32 period,uint32 c5speed,int32 nPeriodFrac) const6029 uint32 CSoundFile::GetFreqFromPeriod(uint32 period, uint32 c5speed, int32 nPeriodFrac) const
6030 {
6031 	if (!period) return 0;
6032 	if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MTM))
6033 	{
6034 		if(m_playBehaviour[kFT2Periods])
6035 		{
6036 			// FT2 compatibility: Period is a 16-bit value in FT2, and it overflows happily.
6037 			// Test case: FreqWraparound.xm
6038 			period &= 0xFFFF;
6039 		}
6040 		if(m_SongFlags[SONG_LINEARSLIDES])
6041 		{
6042 			uint32 octave;
6043 			if(m_playBehaviour[kFT2Periods])
6044 			{
6045 				// Under normal circumstances, this calculation returns the same values as the non-compatible one.
6046 				// However, once the 12 octaves are exceeded (through portamento slides), the octave shift goes
6047 				// crazy in FT2, meaning that the frequency wraps around randomly...
6048 				// The entries in FT2's conversion table are four times as big, hence we have to do an additional shift by two bits.
6049 				// Test case: FreqWraparound.xm
6050 				// 12 octaves * (12 * 64) LUT entries = 9216, add 767 for rounding
6051 				uint32 div = ((9216u + 767u - period) / 768);
6052 				octave = ((14 - div) & 0x1F);
6053 			} else
6054 			{
6055 				octave = (period / 768) + 2;
6056 			}
6057 			return (XMLinearTable[period % 768] << (FREQ_FRACBITS + 2)) >> octave;
6058 		} else
6059 		{
6060 			if(!period) period = 1;
6061 			return ((8363 * 1712L) << FREQ_FRACBITS) / period;
6062 		}
6063 	} else if(UseFinetuneAndTranspose())
6064 	{
6065 		return ((3546895L * 4) << FREQ_FRACBITS) / period;
6066 	} else if(GetType() == MOD_TYPE_669)
6067 	{
6068 		// We only really use c5speed for the finetune pattern command. All samples in 669 files have the same middle-C speed (imported as 8363 Hz).
6069 		return (period + c5speed - 8363) << FREQ_FRACBITS;
6070 	} else if(GetType() & (MOD_TYPE_MDL | MOD_TYPE_DTM))
6071 	{
6072 		LimitMax(period, Util::MaxValueOfType(period) >> 8);
6073 		if (!c5speed) c5speed = 8363;
6074 		return Util::muldiv_unsigned(c5speed, (1712L << 7) << FREQ_FRACBITS, (period << 8) + nPeriodFrac);
6075 	} else
6076 	{
6077 		LimitMax(period, Util::MaxValueOfType(period) >> 8);
6078 		if(PeriodsAreFrequencies())
6079 		{
6080 			// Input is already a frequency in Hertz, not a period.
6081 			static_assert(FREQ_FRACBITS <= 8, "Check this shift operator");
6082 			return uint32(((uint64(period) << 8) + nPeriodFrac) >> (8 - FREQ_FRACBITS));
6083 		} else if(m_SongFlags[SONG_LINEARSLIDES])
6084 		{
6085 			if(!c5speed)
6086 				c5speed = 8363;
6087 			return Util::muldiv_unsigned(c5speed, (1712L << 8) << FREQ_FRACBITS, (period << 8) + nPeriodFrac);
6088 		} else
6089 		{
6090 			return Util::muldiv_unsigned(8363, (1712L << 8) << FREQ_FRACBITS, (period << 8) + nPeriodFrac);
6091 		}
6092 	}
6093 }
6094 
6095 
GetBestPlugin(CHANNELINDEX nChn,PluginPriority priority,PluginMutePriority respectMutes) const6096 PLUGINDEX CSoundFile::GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const
6097 {
6098 	if (nChn >= MAX_CHANNELS)		//Check valid channel number
6099 	{
6100 		return 0;
6101 	}
6102 
6103 	//Define search source order
6104 	PLUGINDEX plugin = 0;
6105 	switch (priority)
6106 	{
6107 		case ChannelOnly:
6108 			plugin = GetChannelPlugin(nChn, respectMutes);
6109 			break;
6110 		case InstrumentOnly:
6111 			plugin  = GetActiveInstrumentPlugin(nChn, respectMutes);
6112 			break;
6113 		case PrioritiseInstrument:
6114 			plugin  = GetActiveInstrumentPlugin(nChn, respectMutes);
6115 			if(!plugin || plugin > MAX_MIXPLUGINS)
6116 			{
6117 				plugin = GetChannelPlugin(nChn, respectMutes);
6118 			}
6119 			break;
6120 		case PrioritiseChannel:
6121 			plugin  = GetChannelPlugin(nChn, respectMutes);
6122 			if(!plugin || plugin > MAX_MIXPLUGINS)
6123 			{
6124 				plugin = GetActiveInstrumentPlugin(nChn, respectMutes);
6125 			}
6126 			break;
6127 	}
6128 
6129 	return plugin; // 0 Means no plugin found.
6130 }
6131 
6132 
GetChannelPlugin(CHANNELINDEX nChn,PluginMutePriority respectMutes) const6133 PLUGINDEX CSoundFile::GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority respectMutes) const
6134 {
6135 	const ModChannel &channel = m_PlayState.Chn[nChn];
6136 
6137 	PLUGINDEX plugin;
6138 	if((respectMutes == RespectMutes && channel.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) || channel.dwFlags[CHN_NOFX])
6139 	{
6140 		plugin = 0;
6141 	} else
6142 	{
6143 		// If it looks like this is an NNA channel, we need to find the master channel.
6144 		// This ensures we pick up the right ChnSettings.
6145 		// NB: nMasterChn == 0 means no master channel, so we need to -1 to get correct index.
6146 		if (nChn >= m_nChannels && channel.nMasterChn > 0)
6147 		{
6148 			nChn = channel.nMasterChn - 1;
6149 		}
6150 
6151 		if(nChn < MAX_BASECHANNELS)
6152 		{
6153 			plugin = ChnSettings[nChn].nMixPlugin;
6154 		} else
6155 		{
6156 			plugin = 0;
6157 		}
6158 	}
6159 	return plugin;
6160 }
6161 
6162 
GetActiveInstrumentPlugin(CHANNELINDEX nChn,PluginMutePriority respectMutes) const6163 PLUGINDEX CSoundFile::GetActiveInstrumentPlugin(CHANNELINDEX nChn, PluginMutePriority respectMutes) const
6164 {
6165 	// Unlike channel settings, pModInstrument is copied from the original chan to the NNA chan,
6166 	// so we don't need to worry about finding the master chan.
6167 
6168 	PLUGINDEX plug = 0;
6169 	if(m_PlayState.Chn[nChn].pModInstrument != nullptr)
6170 	{
6171 		if(respectMutes == RespectMutes && m_PlayState.Chn[nChn].pModSample && m_PlayState.Chn[nChn].pModSample->uFlags[CHN_MUTE])
6172 		{
6173 			plug = 0;
6174 		} else
6175 		{
6176 			plug = m_PlayState.Chn[nChn].pModInstrument->nMixPlug;
6177 		}
6178 	}
6179 	return plug;
6180 }
6181 
6182 
6183 // Retrieve the plugin that is associated with the channel's current instrument.
6184 // No plugin is returned if the channel is muted or if the instrument doesn't have a MIDI channel set up,
6185 // As this is meant to be used with instrument plugins.
GetChannelInstrumentPlugin(CHANNELINDEX chn) const6186 IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(CHANNELINDEX chn) const
6187 {
6188 #ifndef NO_PLUGINS
6189 	if(m_PlayState.Chn[chn].dwFlags[CHN_MUTE | CHN_SYNCMUTE])
6190 	{
6191 		// Don't process portamento on muted channels. Note that this might have a side-effect
6192 		// on other channels which trigger notes on the same MIDI channel of the same plugin,
6193 		// as those won't be pitch-bent anymore.
6194 		return nullptr;
6195 	}
6196 
6197 	if(m_PlayState.Chn[chn].HasMIDIOutput())
6198 	{
6199 		const ModInstrument *pIns = m_PlayState.Chn[chn].pModInstrument;
6200 		// Instrument sends to a MIDI channel
6201 		if(pIns->nMixPlug != 0 && pIns->nMixPlug <= MAX_MIXPLUGINS)
6202 		{
6203 			return m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin;
6204 		}
6205 	}
6206 #else
6207 	MPT_UNREFERENCED_PARAMETER(chn);
6208 #endif // NO_PLUGINS
6209 	return nullptr;
6210 }
6211 
6212 
6213 #ifdef MODPLUG_TRACKER
HandlePatternTransitionEvents()6214 void CSoundFile::HandlePatternTransitionEvents()
6215 {
6216 	// MPT sequence override
6217 	if(m_PlayState.m_nSeqOverride != ORDERINDEX_INVALID && m_PlayState.m_nSeqOverride < Order().size())
6218 	{
6219 		if(m_SongFlags[SONG_PATTERNLOOP])
6220 		{
6221 			m_PlayState.m_nPattern = Order()[m_PlayState.m_nSeqOverride];
6222 		}
6223 		m_PlayState.m_nCurrentOrder = m_PlayState.m_nSeqOverride;
6224 		m_PlayState.m_nSeqOverride = ORDERINDEX_INVALID;
6225 	}
6226 
6227 	// Channel mutes
6228 	for (CHANNELINDEX chan = 0; chan < GetNumChannels(); chan++)
6229 	{
6230 		if (m_bChannelMuteTogglePending[chan])
6231 		{
6232 			if(GetpModDoc())
6233 			{
6234 				GetpModDoc()->MuteChannel(chan, !GetpModDoc()->IsChannelMuted(chan));
6235 			}
6236 			m_bChannelMuteTogglePending[chan] = false;
6237 		}
6238 	}
6239 }
6240 #endif // MODPLUG_TRACKER
6241 
6242 
6243 // Update time signatures (global or pattern-specific). Don't forget to call this when changing the RPB/RPM settings anywhere!
UpdateTimeSignature()6244 void CSoundFile::UpdateTimeSignature()
6245 {
6246 	if(!Patterns.IsValidIndex(m_PlayState.m_nPattern) || !Patterns[m_PlayState.m_nPattern].GetOverrideSignature())
6247 	{
6248 		m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat;
6249 		m_PlayState.m_nCurrentRowsPerMeasure = m_nDefaultRowsPerMeasure;
6250 	} else
6251 	{
6252 		m_PlayState.m_nCurrentRowsPerBeat = Patterns[m_PlayState.m_nPattern].GetRowsPerBeat();
6253 		m_PlayState.m_nCurrentRowsPerMeasure = Patterns[m_PlayState.m_nPattern].GetRowsPerMeasure();
6254 	}
6255 }
6256 
6257 
PortamentoMPT(ModChannel & chn,int param)6258 void CSoundFile::PortamentoMPT(ModChannel &chn, int param)
6259 {
6260 	//Behavior: Modifies portamento by param-steps on every tick.
6261 	//Note that step meaning depends on tuning.
6262 
6263 	chn.m_PortamentoFineSteps += param;
6264 	chn.m_CalculateFreq = true;
6265 }
6266 
6267 
PortamentoFineMPT(ModChannel & chn,int param)6268 void CSoundFile::PortamentoFineMPT(ModChannel &chn, int param)
6269 {
6270 	//Behavior: Divides portamento change between ticks/row. For example
6271 	//if Ticks/row == 6, and param == +-6, portamento goes up/down by one tuning-dependent
6272 	//fine step every tick.
6273 
6274 	if(m_PlayState.m_nTickCount == 0)
6275 		chn.nOldFinePortaUpDown = 0;
6276 
6277 	const int tickParam = static_cast<int>((m_PlayState.m_nTickCount + 1.0) * param / m_PlayState.m_nMusicSpeed);
6278 	chn.m_PortamentoFineSteps += (param >= 0) ? tickParam - chn.nOldFinePortaUpDown : tickParam + chn.nOldFinePortaUpDown;
6279 	if(m_PlayState.m_nTickCount + 1 == m_PlayState.m_nMusicSpeed)
6280 		chn.nOldFinePortaUpDown = static_cast<int8>(std::abs(param));
6281 	else
6282 		chn.nOldFinePortaUpDown = static_cast<int8>(std::abs(tickParam));
6283 
6284 	chn.m_CalculateFreq = true;
6285 }
6286 
6287 
PortamentoExtraFineMPT(ModChannel & chn,int param)6288 void CSoundFile::PortamentoExtraFineMPT(ModChannel &chn, int param)
6289 {
6290 	// This kinda behaves like regular fine portamento.
6291 	// It changes the pitch by n finetune steps on the first tick.
6292 
6293 	if(chn.isFirstTick)
6294 	{
6295 		chn.m_PortamentoFineSteps += param;
6296 		chn.m_CalculateFreq = true;
6297 	}
6298 }
6299 
6300 
6301 OPENMPT_NAMESPACE_END
6302