1 // C port of ProTracker 2.3D's replayer (with some modifications, but still accurate)
2 
3 // for finding memory leaks in debug mode with Visual Studio
4 #if defined _DEBUG && defined _MSC_VER
5 #include <crtdbg.h>
6 #endif
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <stdbool.h>
12 #include <math.h>
13 #include "pt2_header.h"
14 #include "pt2_audio.h"
15 #include "pt2_helpers.h"
16 #include "pt2_tables.h"
17 #include "pt2_module_loader.h"
18 #include "pt2_config.h"
19 #include "pt2_sampler.h"
20 #include "pt2_visuals.h"
21 #include "pt2_textout.h"
22 #include "pt2_scopes.h"
23 #include "pt2_sync.h"
24 
25 static bool posJumpAssert, pBreakFlag, modRenderDone;
26 static bool doStopSong; // from F00 (Set Speed)
27 static int8_t pBreakPosition, oldRow, modPattern;
28 static uint8_t pattDelTime, lowMask = 0xFF, pattDelTime2;
29 static int16_t modOrder, oldPattern, oldOrder;
30 static int32_t modBPM, oldBPM, oldSpeed, ciaSetBPM;
31 
32 static const uint8_t funkTable[16] = // EFx (FunkRepeat/InvertLoop)
33 {
34 	0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D,
35 	0x10, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80
36 };
37 
allocMemForAllSamples(void)38 int8_t *allocMemForAllSamples(void)
39 {
40 	/* Allocate memory for all sample data blocks.
41 	**
42 	** We need three extra sample slots:
43 	** The 1st is extra safety padding since setting a Paula length of 0
44 	** results in reading (1+65535)*2 bytes. The 2nd and 3rd (64K*2 = 1x 128K)
45 	** are reserved for NULL pointers. This is needed for emulating a PT quirk.
46 	**
47 	** We have a padding of 4 bytes at the end for length=0 quirk safety.
48 	**
49 	** PS: I don't really know if it's possible for ProTracker to set a Paula
50 	** length of 0, but I fully support this Paula behavior just in case.
51 	*/
52 	const size_t allocLen = ((MOD_SAMPLES + 3) * MAX_SAMPLE_LEN) + 4;
53 
54 	return (int8_t *)calloc(1, allocLen);
55 }
56 
modSetSpeed(int32_t speed)57 void modSetSpeed(int32_t speed)
58 {
59 	song->currSpeed = song->speed = speed;
60 	song->tick = 0;
61 }
62 
doStopIt(bool resetPlayMode)63 void doStopIt(bool resetPlayMode)
64 {
65 	editor.songPlaying = false;
66 
67 	resetCachedMixerPeriod();
68 	resetCachedScopePeriod();
69 
70 	pattDelTime = 0;
71 	pattDelTime2 = 0;
72 
73 	if (resetPlayMode)
74 	{
75 		editor.playMode = PLAY_MODE_NORMAL;
76 		editor.currMode = MODE_IDLE;
77 
78 		pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
79 	}
80 
81 	if (song != NULL)
82 	{
83 		moduleChannel_t *c = song->channels;
84 		for (int32_t i = 0; i < AMIGA_VOICES; i++, c++)
85 		{
86 			c->n_wavecontrol = 0;
87 			c->n_glissfunk = 0;
88 			c->n_finetune = 0;
89 			c->n_loopcount = 0;
90 		}
91 	}
92 
93 	doStopSong = false; // just in case this flag was stuck from command F00 (stop song)
94 }
95 
setPattern(int16_t pattern)96 void setPattern(int16_t pattern)
97 {
98 	if (pattern > MAX_PATTERNS-1)
99 		pattern = MAX_PATTERNS-1;
100 
101 	song->currPattern = modPattern = (int8_t)pattern;
102 }
103 
storeTempVariables(void)104 void storeTempVariables(void) // this one is accessed in other files, so non-static
105 {
106 	oldBPM = song->currBPM;
107 	oldRow = song->currRow;
108 	oldOrder = song->currOrder;
109 	oldSpeed = song->currSpeed;
110 	oldPattern = song->currPattern;
111 }
112 
setVUMeterHeight(moduleChannel_t * ch)113 static void setVUMeterHeight(moduleChannel_t *ch)
114 {
115 	uint8_t vol;
116 
117 	if (editor.muted[ch->n_chanindex])
118 		return;
119 
120 	vol = ch->n_volume;
121 	if ((ch->n_cmd & 0xF00) == 0xC00) // handle Cxx effect
122 		vol = ch->n_cmd & 0xFF;
123 
124 	if (vol > 64)
125 		vol = 64;
126 
127 	if (!editor.songPlaying)
128 	{
129 		editor.vuMeterVolumes[ch->n_chanindex] = vuMeterHeights[vol];
130 	}
131 	else
132 	{
133 		ch->syncVuVolume = vol;
134 		ch->syncFlags |= UPDATE_VUMETER;
135 	}
136 }
137 
updateFunk(moduleChannel_t * ch)138 static void updateFunk(moduleChannel_t *ch)
139 {
140 	const int8_t funkSpeed = ch->n_glissfunk >> 4;
141 	if (funkSpeed == 0)
142 		return;
143 
144 	ch->n_funkoffset += funkTable[funkSpeed];
145 	if (ch->n_funkoffset >= 128)
146 	{
147 		ch->n_funkoffset = 0;
148 
149 		if (ch->n_loopstart != NULL && ch->n_wavestart != NULL) // ProTracker bugfix
150 		{
151 			if (++ch->n_wavestart >= ch->n_loopstart + (ch->n_replen << 1))
152 				ch->n_wavestart = ch->n_loopstart;
153 
154 			*ch->n_wavestart = -1 - *ch->n_wavestart;
155 		}
156 	}
157 }
158 
setGlissControl(moduleChannel_t * ch)159 static void setGlissControl(moduleChannel_t *ch)
160 {
161 	ch->n_glissfunk = (ch->n_glissfunk & 0xF0) | (ch->n_cmd & 0x0F);
162 }
163 
setVibratoControl(moduleChannel_t * ch)164 static void setVibratoControl(moduleChannel_t *ch)
165 {
166 	ch->n_wavecontrol = (ch->n_wavecontrol & 0xF0) | (ch->n_cmd & 0x0F);
167 }
168 
setFineTune(moduleChannel_t * ch)169 static void setFineTune(moduleChannel_t *ch)
170 {
171 	ch->n_finetune = ch->n_cmd & 0xF;
172 }
173 
jumpLoop(moduleChannel_t * ch)174 static void jumpLoop(moduleChannel_t *ch)
175 {
176 	uint8_t tempParam;
177 
178 	if (song->tick != 0)
179 		return;
180 
181 	if ((ch->n_cmd & 0xF) == 0)
182 	{
183 		ch->n_pattpos = song->row;
184 	}
185 	else
186 	{
187 		if (ch->n_loopcount == 0)
188 			ch->n_loopcount = ch->n_cmd & 0xF;
189 		else if (--ch->n_loopcount == 0)
190 			return;
191 
192 		pBreakPosition = ch->n_pattpos;
193 		pBreakFlag = true;
194 
195 		// stuff used for MOD2WAV to determine if the song has reached its end
196 		if (editor.isWAVRendering)
197 		{
198 			for (tempParam = pBreakPosition; tempParam <= song->row; tempParam++)
199 				editor.rowVisitTable[(modOrder * MOD_ROWS) + tempParam] = false;
200 		}
201 	}
202 }
203 
setTremoloControl(moduleChannel_t * ch)204 static void setTremoloControl(moduleChannel_t *ch)
205 {
206 	ch->n_wavecontrol = ((ch->n_cmd & 0xF) << 4) | (ch->n_wavecontrol & 0xF);
207 }
208 
209 /* This is a little used effect, despite being present in original ProTracker.
210 ** E8x was sometimes entirely replaced with code used for demo fx syncing in
211 ** demo mod players, so it can be turned off by looking at DISABLE_E8X in
212 ** protracker.ini if you so desire.
213 */
karplusStrong(moduleChannel_t * ch)214 static void karplusStrong(moduleChannel_t *ch)
215 {
216 	int8_t a, b;
217 
218 	if (config.disableE8xEffect)
219 		return;
220 
221 	if (ch->n_loopstart == NULL)
222 		return; // ProTracker bugfix
223 
224 	int8_t *ptr8 = ch->n_loopstart;
225 	int16_t end = ((ch->n_replen * 2) & 0xFFFF) - 2;
226 	do
227 	{
228 		a = ptr8[0];
229 		b = ptr8[1];
230 		*ptr8++ = (a + b) >> 1;
231 	}
232 	while (--end >= 0);
233 
234 	a = ptr8[0];
235 	b = ch->n_loopstart[0];
236 	*ptr8 = (a + b) >> 1;
237 }
238 
doRetrg(moduleChannel_t * ch)239 static void doRetrg(moduleChannel_t *ch)
240 {
241 	paulaSetData(ch->n_chanindex, ch->n_start); // n_start is increased on 9xx
242 	paulaSetLength(ch->n_chanindex, ch->n_length);
243 	paulaSetPeriod(ch->n_chanindex, ch->n_period);
244 	paulaStartDMA(ch->n_chanindex);
245 
246 	// these take effect after the current DMA cycle is done
247 	paulaSetData(ch->n_chanindex, ch->n_loopstart);
248 	paulaSetLength(ch->n_chanindex, ch->n_replen);
249 
250 	ch->syncAnalyzerVolume = ch->n_volume;
251 	ch->syncAnalyzerPeriod = ch->n_period;
252 	ch->syncFlags |= UPDATE_ANALYZER;
253 
254 	setVUMeterHeight(ch);
255 }
256 
retrigNote(moduleChannel_t * ch)257 static void retrigNote(moduleChannel_t *ch)
258 {
259 	if ((ch->n_cmd & 0xF) > 0)
260 	{
261 		if (song->tick == 0 && (ch->n_note & 0xFFF) > 0)
262 			return;
263 
264 		if (song->tick % (ch->n_cmd & 0xF) == 0)
265 			doRetrg(ch);
266 	}
267 }
268 
volumeSlide(moduleChannel_t * ch)269 static void volumeSlide(moduleChannel_t *ch)
270 {
271 	uint8_t param = ch->n_cmd & 0xFF;
272 	if ((param & 0xF0) == 0)
273 	{
274 		ch->n_volume -= param & 0x0F;
275 		if (ch->n_volume < 0)
276 			ch->n_volume = 0;
277 	}
278 	else
279 	{
280 		ch->n_volume += param >> 4;
281 		if (ch->n_volume > 64)
282 			ch->n_volume = 64;
283 	}
284 }
285 
volumeFineUp(moduleChannel_t * ch)286 static void volumeFineUp(moduleChannel_t *ch)
287 {
288 	if (song->tick == 0)
289 	{
290 		ch->n_volume += ch->n_cmd & 0xF;
291 		if (ch->n_volume > 64)
292 			ch->n_volume = 64;
293 	}
294 }
295 
volumeFineDown(moduleChannel_t * ch)296 static void volumeFineDown(moduleChannel_t *ch)
297 {
298 	if (song->tick == 0)
299 	{
300 		ch->n_volume -= ch->n_cmd & 0xF;
301 		if (ch->n_volume < 0)
302 			ch->n_volume = 0;
303 	}
304 }
305 
noteCut(moduleChannel_t * ch)306 static void noteCut(moduleChannel_t *ch)
307 {
308 	if (song->tick == (ch->n_cmd & 0xF))
309 		ch->n_volume = 0;
310 }
311 
noteDelay(moduleChannel_t * ch)312 static void noteDelay(moduleChannel_t *ch)
313 {
314 	if (song->tick == (ch->n_cmd & 0xF) && (ch->n_note & 0xFFF) > 0)
315 		doRetrg(ch);
316 }
317 
patternDelay(moduleChannel_t * ch)318 static void patternDelay(moduleChannel_t *ch)
319 {
320 	if (song->tick == 0 && pattDelTime2 == 0)
321 		pattDelTime = (ch->n_cmd & 0xF) + 1;
322 }
323 
funkIt(moduleChannel_t * ch)324 static void funkIt(moduleChannel_t *ch)
325 {
326 	if (song->tick == 0)
327 	{
328 		ch->n_glissfunk = ((ch->n_cmd & 0xF) << 4) | (ch->n_glissfunk & 0xF);
329 
330 		if ((ch->n_glissfunk & 0xF0) > 0)
331 			updateFunk(ch);
332 	}
333 }
334 
positionJump(moduleChannel_t * ch)335 static void positionJump(moduleChannel_t *ch)
336 {
337 	modOrder = (ch->n_cmd & 0xFF) - 1; // B00 results in -1, but it safely jumps to order 0
338 	pBreakPosition = 0;
339 	posJumpAssert = true;
340 }
341 
volumeChange(moduleChannel_t * ch)342 static void volumeChange(moduleChannel_t *ch)
343 {
344 	ch->n_volume = ch->n_cmd & 0xFF;
345 	if ((uint8_t)ch->n_volume > 64)
346 		ch->n_volume = 64;
347 }
348 
patternBreak(moduleChannel_t * ch)349 static void patternBreak(moduleChannel_t *ch)
350 {
351 	pBreakPosition = (((ch->n_cmd & 0xF0) >> 4) * 10) + (ch->n_cmd & 0x0F);
352 	if ((uint8_t)pBreakPosition > 63)
353 		pBreakPosition = 0;
354 
355 	posJumpAssert = true;
356 }
357 
setSpeed(moduleChannel_t * ch)358 static void setSpeed(moduleChannel_t *ch)
359 {
360 	if ((ch->n_cmd & 0xFF) > 0)
361 	{
362 		if (editor.timingMode == TEMPO_MODE_VBLANK || (ch->n_cmd & 0xFF) < 32)
363 			modSetSpeed(ch->n_cmd & 0xFF);
364 		else
365 			ciaSetBPM = ch->n_cmd & 0xFF; // the CIA chip doesn't use its new timer value until the next interrupt, so change it later
366 	}
367 	else
368 	{
369 		// F00 - stop song
370 		doStopSong = true;
371 	}
372 }
373 
arpeggio(moduleChannel_t * ch)374 static void arpeggio(moduleChannel_t *ch)
375 {
376 	uint8_t arpTick, arpNote;
377 	const int16_t *periods;
378 
379 	arpTick = song->tick % 3; // 0, 1, 2
380 	if (arpTick == 1)
381 	{
382 		arpNote = (uint8_t)(ch->n_cmd >> 4);
383 	}
384 	else if (arpTick == 2)
385 	{
386 		arpNote = ch->n_cmd & 0xF;
387 	}
388 	else // arpTick 0
389 	{
390 		paulaSetPeriod(ch->n_chanindex, ch->n_period);
391 		return;
392 	}
393 
394 	/* 8bitbubsy: If the finetune is -1, this can overflow up to
395 	** 15 words outside of the table. The table is padded with
396 	** the correct overflow values to allow this to safely happen
397 	** and sound correct at the same time.
398 	*/
399 	periods = &periodTable[ch->n_finetune * 37];
400 	for (int32_t baseNote = 0; baseNote < 37; baseNote++)
401 	{
402 		if (ch->n_period >= periods[baseNote])
403 		{
404 			paulaSetPeriod(ch->n_chanindex, periods[baseNote+arpNote]);
405 			break;
406 		}
407 	}
408 }
409 
portaUp(moduleChannel_t * ch)410 static void portaUp(moduleChannel_t *ch)
411 {
412 	ch->n_period -= (ch->n_cmd & 0xFF) & lowMask;
413 	lowMask = 0xFF;
414 
415 	if ((ch->n_period & 0xFFF) < 113) // PT BUG: sign removed before comparison, underflow not clamped!
416 		ch->n_period = (ch->n_period & 0xF000) | 113;
417 
418 	paulaSetPeriod(ch->n_chanindex, ch->n_period & 0xFFF);
419 }
420 
portaDown(moduleChannel_t * ch)421 static void portaDown(moduleChannel_t *ch)
422 {
423 	ch->n_period += (ch->n_cmd & 0xFF) & lowMask;
424 	lowMask = 0xFF;
425 
426 	if ((ch->n_period & 0xFFF) > 856)
427 		ch->n_period = (ch->n_period & 0xF000) | 856;
428 
429 	paulaSetPeriod(ch->n_chanindex, ch->n_period & 0xFFF);
430 }
431 
filterOnOff(moduleChannel_t * ch)432 static void filterOnOff(moduleChannel_t *ch)
433 {
434 	if (song->tick == 0) // added this (just pointless to call this during all ticks!)
435 	{
436 		const bool filterOn = (ch->n_cmd & 1) ^ 1;
437 		setLEDFilter(filterOn, false);
438 	}
439 }
440 
finePortaUp(moduleChannel_t * ch)441 static void finePortaUp(moduleChannel_t *ch)
442 {
443 	if (song->tick == 0)
444 	{
445 		lowMask = 0xF;
446 		portaUp(ch);
447 	}
448 }
449 
finePortaDown(moduleChannel_t * ch)450 static void finePortaDown(moduleChannel_t *ch)
451 {
452 	if (song->tick == 0)
453 	{
454 		lowMask = 0xF;
455 		portaDown(ch);
456 	}
457 }
458 
setTonePorta(moduleChannel_t * ch)459 static void setTonePorta(moduleChannel_t *ch)
460 {
461 	uint8_t i;
462 	const int16_t *portaPointer;
463 	uint16_t note;
464 
465 	note = ch->n_note & 0xFFF;
466 	portaPointer = &periodTable[ch->n_finetune * 37];
467 
468 	i = 0;
469 	while (true)
470 	{
471 		// portaPointer[36] = 0, so i=36 is safe
472 		if (note >= portaPointer[i])
473 			break;
474 
475 		if (++i >= 37)
476 		{
477 			i = 35;
478 			break;
479 		}
480 	}
481 
482 	if ((ch->n_finetune & 8) && i > 0)
483 		i--;
484 
485 	ch->n_wantedperiod = portaPointer[i];
486 	ch->n_toneportdirec = 0;
487 
488 	     if (ch->n_period == ch->n_wantedperiod) ch->n_wantedperiod = 0;
489 	else if (ch->n_period > ch->n_wantedperiod) ch->n_toneportdirec = 1;
490 }
491 
tonePortNoChange(moduleChannel_t * ch)492 static void tonePortNoChange(moduleChannel_t *ch)
493 {
494 	uint8_t i;
495 	const int16_t *portaPointer;
496 
497 	if (ch->n_wantedperiod <= 0)
498 		return;
499 
500 	if (ch->n_toneportdirec > 0)
501 	{
502 		ch->n_period -= ch->n_toneportspeed;
503 		if (ch->n_period <= ch->n_wantedperiod)
504 		{
505 			ch->n_period = ch->n_wantedperiod;
506 			ch->n_wantedperiod = 0;
507 		}
508 	}
509 	else
510 	{
511 		ch->n_period += ch->n_toneportspeed;
512 		if (ch->n_period >= ch->n_wantedperiod)
513 		{
514 			ch->n_period = ch->n_wantedperiod;
515 			ch->n_wantedperiod = 0;
516 		}
517 	}
518 
519 	if ((ch->n_glissfunk & 0xF) == 0)
520 	{
521 		paulaSetPeriod(ch->n_chanindex, ch->n_period);
522 	}
523 	else
524 	{
525 		portaPointer = &periodTable[ch->n_finetune * 37];
526 
527 		i = 0;
528 		while (true)
529 		{
530 			// portaPointer[36] = 0, so i=36 is safe
531 			if (ch->n_period >= portaPointer[i])
532 				break;
533 
534 			if (++i >= 37)
535 			{
536 				i = 35;
537 				break;
538 			}
539 		}
540 
541 		paulaSetPeriod(ch->n_chanindex, portaPointer[i]);
542 	}
543 }
544 
tonePortamento(moduleChannel_t * ch)545 static void tonePortamento(moduleChannel_t *ch)
546 {
547 	if ((ch->n_cmd & 0xFF) > 0)
548 	{
549 		ch->n_toneportspeed = ch->n_cmd & 0xFF;
550 		ch->n_cmd &= 0xFF00;
551 	}
552 
553 	tonePortNoChange(ch);
554 }
555 
vibrato2(moduleChannel_t * ch)556 static void vibrato2(moduleChannel_t *ch)
557 {
558 	uint16_t vibratoData;
559 
560 	const uint8_t vibratoPos = (ch->n_vibratopos >> 2) & 0x1F;
561 	const uint8_t vibratoType = ch->n_wavecontrol & 3;
562 
563 	if (vibratoType == 0) // sine
564 	{
565 		vibratoData = vibratoTable[vibratoPos];
566 	}
567 	else if (vibratoType == 1) // ramp
568 	{
569 		if (ch->n_vibratopos < 128)
570 			vibratoData = vibratoPos << 3;
571 		else
572 			vibratoData = 255 - (vibratoPos << 3);
573 	}
574 	else // square
575 	{
576 		vibratoData = 255;
577 	}
578 
579 	vibratoData = (vibratoData * (ch->n_vibratocmd & 0xF)) >> 7;
580 
581 	if (ch->n_vibratopos < 128)
582 		vibratoData = ch->n_period + vibratoData;
583 	else
584 		vibratoData = ch->n_period - vibratoData;
585 
586 	paulaSetPeriod(ch->n_chanindex, vibratoData);
587 
588 	ch->n_vibratopos += (ch->n_vibratocmd >> 2) & 0x3C;
589 }
590 
vibrato(moduleChannel_t * ch)591 static void vibrato(moduleChannel_t *ch)
592 {
593 	if ((ch->n_cmd & 0x0F) > 0)
594 		ch->n_vibratocmd = (ch->n_vibratocmd & 0xF0) | (ch->n_cmd & 0x0F);
595 
596 	if ((ch->n_cmd & 0xF0) > 0)
597 		ch->n_vibratocmd = (ch->n_cmd & 0xF0) | (ch->n_vibratocmd & 0x0F);
598 
599 	vibrato2(ch);
600 }
601 
tonePlusVolSlide(moduleChannel_t * ch)602 static void tonePlusVolSlide(moduleChannel_t *ch)
603 {
604 	tonePortNoChange(ch);
605 	volumeSlide(ch);
606 }
607 
vibratoPlusVolSlide(moduleChannel_t * ch)608 static void vibratoPlusVolSlide(moduleChannel_t *ch)
609 {
610 	vibrato2(ch);
611 	volumeSlide(ch);
612 }
613 
tremolo(moduleChannel_t * ch)614 static void tremolo(moduleChannel_t *ch)
615 {
616 	int16_t tremoloData;
617 
618 	if ((ch->n_cmd & 0x0F) > 0)
619 		ch->n_tremolocmd = (ch->n_tremolocmd & 0xF0) | (ch->n_cmd & 0x0F);
620 
621 	if ((ch->n_cmd & 0xF0) > 0)
622 		ch->n_tremolocmd = (ch->n_cmd & 0xF0) | (ch->n_tremolocmd & 0x0F);
623 
624 	const uint8_t tremoloPos = (ch->n_tremolopos >> 2) & 0x1F;
625 	const uint8_t tremoloType = (ch->n_wavecontrol >> 4) & 3;
626 
627 	if (tremoloType == 0) // sine
628 	{
629 		tremoloData = vibratoTable[tremoloPos];
630 	}
631 	else if (tremoloType == 1) // ramp
632 	{
633 		if (ch->n_vibratopos < 128) // PT bug, should've been ch->n_tremolopos
634 			tremoloData = tremoloPos << 3;
635 		else
636 			tremoloData = 255 - (tremoloPos << 3);
637 	}
638 	else // square
639 	{
640 		tremoloData = 255;
641 	}
642 
643 	tremoloData = ((uint16_t)tremoloData * (ch->n_tremolocmd & 0xF)) >> 6;
644 
645 	if (ch->n_tremolopos < 128)
646 	{
647 		tremoloData = ch->n_volume + tremoloData;
648 		if (tremoloData > 64)
649 			tremoloData = 64;
650 	}
651 	else
652 	{
653 		tremoloData = ch->n_volume - tremoloData;
654 		if (tremoloData < 0)
655 			tremoloData = 0;
656 	}
657 
658 	paulaSetVolume(ch->n_chanindex, tremoloData);
659 
660 	ch->n_tremolopos += (ch->n_tremolocmd >> 2) & 0x3C;
661 }
662 
sampleOffset(moduleChannel_t * ch)663 static void sampleOffset(moduleChannel_t *ch)
664 {
665 	if ((ch->n_cmd & 0xFF) > 0)
666 		ch->n_sampleoffset = ch->n_cmd & 0xFF;
667 
668 	uint16_t newOffset = ch->n_sampleoffset << 7;
669 
670 	// this signed test is the reason for the 9xx "sample >64kB = silence" bug
671 	if ((int16_t)newOffset < ch->n_length)
672 	{
673 		ch->n_length -= newOffset;
674 		ch->n_start += newOffset << 1;
675 	}
676 	else
677 	{
678 		ch->n_length = 1;
679 	}
680 }
681 
E_Commands(moduleChannel_t * ch)682 static void E_Commands(moduleChannel_t *ch)
683 {
684 	const uint8_t ecmd = (ch->n_cmd & 0x00F0) >> 4;
685 	switch (ecmd)
686 	{
687 		case 0x0: filterOnOff(ch);       return;
688 		case 0x1: finePortaUp(ch);       return;
689 		case 0x2: finePortaDown(ch);     return;
690 		case 0x3: setGlissControl(ch);   return;
691 		case 0x4: setVibratoControl(ch); return;
692 		case 0x5: setFineTune(ch);       return;
693 		case 0x6: jumpLoop(ch);          return;
694 		case 0x7: setTremoloControl(ch); return;
695 		case 0x8: karplusStrong(ch);     return;
696 		case 0xE: patternDelay(ch);      return;
697 		default: break;
698 	}
699 
700 	if (editor.muted[ch->n_chanindex])
701 		return;
702 
703 	switch (ecmd)
704 	{
705 		case 0x9: retrigNote(ch);     return;
706 		case 0xA: volumeFineUp(ch);   return;
707 		case 0xB: volumeFineDown(ch); return;
708 		case 0xC: noteCut(ch);        return;
709 		case 0xD: noteDelay(ch);      return;
710 		case 0xF: funkIt(ch);         return;
711 		default: break;
712 	}
713 }
714 
checkMoreEffects(moduleChannel_t * ch)715 static void checkMoreEffects(moduleChannel_t *ch)
716 {
717 	const uint8_t cmd = (ch->n_cmd & 0x0F00) >> 8;
718 	switch (cmd)
719 	{
720 		case 0x9: sampleOffset(ch); return; // note the returns here, not breaks!
721 		case 0xB: positionJump(ch); return;
722 		case 0xD: patternBreak(ch); return;
723 		case 0xE: E_Commands(ch);   return;
724 		case 0xF: setSpeed(ch);     return;
725 		default: break;
726 	}
727 
728 	if (editor.muted[ch->n_chanindex])
729 		return;
730 
731 	if (cmd == 0xC)
732 	{
733 		volumeChange(ch);
734 		return;
735 	}
736 
737 	paulaSetPeriod(ch->n_chanindex, ch->n_period);
738 }
739 
chkefx2(moduleChannel_t * ch)740 static void chkefx2(moduleChannel_t *ch)
741 {
742 	updateFunk(ch);
743 
744 	if ((ch->n_cmd & 0xFFF) == 0)
745 		return;
746 
747 	const uint8_t cmd = (ch->n_cmd & 0x0F00) >> 8;
748 	switch (cmd)
749 	{
750 		case 0x0: arpeggio(ch);            return; // note the returns here, not breaks!
751 		case 0x1: portaUp(ch);             return;
752 		case 0x2: portaDown(ch);           return;
753 		case 0x3: tonePortamento(ch);      return;
754 		case 0x4: vibrato(ch);             return;
755 		case 0x5: tonePlusVolSlide(ch);    return;
756 		case 0x6: vibratoPlusVolSlide(ch); return;
757 		case 0xE: E_Commands(ch);          return;
758 		default: break;
759 	}
760 
761 	paulaSetPeriod(ch->n_chanindex, ch->n_period);
762 
763 	if (cmd == 0x7)
764 		tremolo(ch);
765 	else if (cmd == 0xA)
766 		volumeSlide(ch);
767 }
768 
checkEffects(moduleChannel_t * ch)769 static void checkEffects(moduleChannel_t *ch)
770 {
771 	if (editor.muted[ch->n_chanindex])
772 		return;
773 
774 	chkefx2(ch);
775 
776 	/* This is not very clear in the original PT replayer code,
777 	** but the tremolo effect skips chkefx2()'s return address
778 	** in the stack so that it jumps to checkEffects()'s return
779 	** address instead of ending up here. In other words, volume
780 	** is not updated here after tremolo (it's done inside the
781 	** tremolo routine itself).
782 	*/
783 	const uint8_t cmd = (ch->n_cmd & 0x0F00) >> 8;
784 	if (cmd != 0x7)
785 		paulaSetVolume(ch->n_chanindex, ch->n_volume);
786 }
787 
setPeriod(moduleChannel_t * ch)788 static void setPeriod(moduleChannel_t *ch)
789 {
790 	int32_t i;
791 
792 	uint16_t note = ch->n_note & 0xFFF;
793 	for (i = 0; i < 37; i++)
794 	{
795 		// periodTable[36] = 0, so i=36 is safe
796 		if (note >= periodTable[i])
797 			break;
798 	}
799 
800 	// yes it's safe if i=37 because of zero-padding
801 	ch->n_period = periodTable[(ch->n_finetune * 37) + i];
802 
803 	if ((ch->n_cmd & 0xFF0) != 0xED0) // no note delay
804 	{
805 		if ((ch->n_wavecontrol & 0x04) == 0) ch->n_vibratopos = 0;
806 		if ((ch->n_wavecontrol & 0x40) == 0) ch->n_tremolopos = 0;
807 
808 		paulaSetLength(ch->n_chanindex, ch->n_length);
809 		paulaSetData(ch->n_chanindex, ch->n_start);
810 
811 		if (ch->n_start == NULL)
812 		{
813 			ch->n_loopstart = NULL;
814 			paulaSetLength(ch->n_chanindex, 1);
815 			ch->n_replen = 1;
816 		}
817 
818 		paulaSetPeriod(ch->n_chanindex, ch->n_period);
819 
820 		if (!editor.muted[ch->n_chanindex])
821 		{
822 			paulaStartDMA(ch->n_chanindex);
823 
824 			ch->syncAnalyzerVolume = ch->n_volume;
825 			ch->syncAnalyzerPeriod = ch->n_period;
826 			ch->syncFlags |= UPDATE_ANALYZER;
827 
828 			setVUMeterHeight(ch);
829 		}
830 		else
831 		{
832 			paulaStopDMA(ch->n_chanindex);
833 		}
834 	}
835 
836 	checkMoreEffects(ch);
837 }
838 
checkMetronome(moduleChannel_t * ch,note_t * note)839 static void checkMetronome(moduleChannel_t *ch, note_t *note)
840 {
841 	if (editor.metroFlag && editor.metroChannel > 0)
842 	{
843 		if (ch->n_chanindex == editor.metroChannel-1 && (song->row % editor.metroSpeed) == 0)
844 		{
845 			note->sample = 0x1F;
846 			note->period = (((song->row / editor.metroSpeed) % editor.metroSpeed) == 0) ? 160 : 214;
847 		}
848 	}
849 }
850 
playVoice(moduleChannel_t * ch)851 static void playVoice(moduleChannel_t *ch)
852 {
853 	uint8_t cmd;
854 	moduleSample_t *s;
855 	note_t note;
856 
857 	if (ch->n_note == 0 && ch->n_cmd == 0)
858 		paulaSetPeriod(ch->n_chanindex, ch->n_period);
859 
860 	note = song->patterns[modPattern][(song->row * AMIGA_VOICES) + ch->n_chanindex];
861 	checkMetronome(ch, &note);
862 
863 	ch->n_note = note.period;
864 	ch->n_cmd = (note.command << 8) | note.param;
865 
866 	if (note.sample >= 1 && note.sample <= 31) // SAFETY BUG FIX: don't handle sample-numbers >31
867 	{
868 		ch->n_samplenum = note.sample - 1;
869 		s = &song->samples[ch->n_samplenum];
870 
871 		ch->n_start = &song->sampleData[s->offset];
872 		ch->n_finetune = s->fineTune & 0xF;
873 		ch->n_volume = s->volume;
874 		ch->n_length = s->length >> 1;
875 		ch->n_replen = s->loopLength >> 1;
876 
877 		const uint16_t repeat = s->loopStart >> 1;
878 		if (repeat > 0)
879 		{
880 			ch->n_loopstart = ch->n_start + (repeat << 1);
881 			ch->n_wavestart = ch->n_loopstart;
882 			ch->n_length = repeat + ch->n_replen;
883 		}
884 		else
885 		{
886 			ch->n_loopstart = ch->n_start;
887 			ch->n_wavestart = ch->n_start;
888 		}
889 
890 		// non-PT2 requirement (set safe sample space for uninitialized voices - f.ex. "the ultimate beeper.mod")
891 		if (ch->n_length == 0)
892 			ch->n_loopstart = ch->n_wavestart = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample
893 	}
894 
895 	if ((ch->n_note & 0xFFF) > 0)
896 	{
897 		if ((ch->n_cmd & 0xFF0) == 0xE50) // set finetune
898 		{
899 			setFineTune(ch);
900 			setPeriod(ch);
901 		}
902 		else
903 		{
904 			cmd = (ch->n_cmd & 0x0F00) >> 8;
905 			if (cmd == 3 || cmd == 5)
906 			{
907 				setVUMeterHeight(ch);
908 				setTonePorta(ch);
909 				checkMoreEffects(ch);
910 			}
911 			else if (cmd == 9)
912 			{
913 				checkMoreEffects(ch);
914 				setPeriod(ch);
915 			}
916 			else
917 			{
918 				setPeriod(ch);
919 			}
920 		}
921 	}
922 	else
923 	{
924 		checkMoreEffects(ch);
925 	}
926 }
927 
updateUIPositions(void)928 static void updateUIPositions(void)
929 {
930 	// don't update UI under MOD2WAV/PAT2SMP rendering
931 	if (editor.isWAVRendering || editor.isSMPRendering)
932 		return;
933 
934 	song->currRow = song->row;
935 	song->currOrder = modOrder;
936 	song->currPattern = modPattern;
937 
938 	uint16_t *currPatPtr = &song->header.order[modOrder];
939 	editor.currPatternDisp = currPatPtr;
940 	editor.currPosEdPattDisp = currPatPtr;
941 	editor.currPatternDisp = currPatPtr;
942 	editor.currPosEdPattDisp = currPatPtr;
943 
944 	ui.updateSongPos = true;
945 	ui.updateSongPattern = true;
946 	ui.updateCurrPattText = true;
947 	ui.updatePatternData = true;
948 
949 	if (ui.posEdScreenShown)
950 		ui.updatePosEd = true;
951 }
952 
nextPosition(void)953 static void nextPosition(void)
954 {
955 	if (editor.isSMPRendering)
956 		modRenderDone = true;
957 
958 	song->row = pBreakPosition;
959 	pBreakPosition = 0;
960 	posJumpAssert = false;
961 
962 	if (editor.playMode != PLAY_MODE_PATTERN ||
963 		(editor.currMode == MODE_RECORD && editor.recordMode != RECORD_PATT))
964 	{
965 		if (editor.stepPlayEnabled)
966 		{
967 			doStopIt(true);
968 
969 			editor.stepPlayEnabled = false;
970 			editor.stepPlayBackwards = false;
971 
972 			song->currRow = song->row;
973 			return;
974 		}
975 
976 		modOrder = (modOrder + 1) & 0x7F;
977 		if (modOrder >= song->header.numOrders)
978 		{
979 			modOrder = 0;
980 
981 			if (config.compoMode) // stop song for music competitions playing
982 			{
983 				doStopIt(true);
984 				turnOffVoices();
985 
986 				modOrder = 0;
987 				modPattern = (int8_t)song->header.order[modOrder];
988 				song->row = 0;
989 
990 				updateUIPositions();
991 			}
992 
993 			if (editor.isWAVRendering)
994 				modRenderDone = true;
995 		}
996 
997 		modPattern = (int8_t)song->header.order[modOrder];
998 		if (modPattern > MAX_PATTERNS-1)
999 			modPattern = MAX_PATTERNS-1;
1000 	}
1001 }
1002 
increasePlaybackTimer(void)1003 static void increasePlaybackTimer(void)
1004 {
1005 	// the timer is not counting in "play pattern" mode
1006 	if (editor.playMode != PLAY_MODE_PATTERN && modBPM >= 32 && modBPM <= 255)
1007 		editor.musicTime64 += musicTimeTab64[modBPM-32];
1008 }
1009 
setCurrRowToVisited(void)1010 static void setCurrRowToVisited(void) // for MOD2WAV
1011 {
1012 	if (editor.isWAVRendering)
1013 		editor.rowVisitTable[(modOrder * MOD_ROWS) + song->row] = true;
1014 }
1015 
renderEndCheck(void)1016 static bool renderEndCheck(void) // for MOD2WAV/PAT2SMP
1017 {
1018 	if (!editor.isWAVRendering && !editor.isSMPRendering)
1019 		return true; // we're not doing MOD2WAV/PAT2SMP
1020 
1021 	bool noPatternDelay = pattDelTime2 == 0;
1022 	if (noPatternDelay && song->tick == song->speed-1)
1023 	{
1024 		if (editor.isSMPRendering)
1025 		{
1026 			if (modRenderDone)
1027 				return false; // we're done rendering
1028 		}
1029 
1030 		if (editor.isWAVRendering)
1031 		{
1032 			bool rowVisited = editor.rowVisitTable[(modOrder * MOD_ROWS) + song->row];
1033 			if (rowVisited || modRenderDone)
1034 				return false; // we're done rendering
1035 		}
1036 	}
1037 
1038 	return true;
1039 }
1040 
intMusic(void)1041 bool intMusic(void) // replayer ticker
1042 {
1043 	// quirk: CIA BPM changes are delayed by one tick in PT, so handle previous tick's BPM change now
1044 	if (ciaSetBPM != -1)
1045 	{
1046 		const int32_t newBPM = ciaSetBPM;
1047 		modSetTempo(newBPM, false);
1048 		ciaSetBPM = -1;
1049 	}
1050 
1051 	increasePlaybackTimer();
1052 
1053 	if (!editor.stepPlayEnabled)
1054 		song->tick++;
1055 
1056 	bool readNewNote = false;
1057 	if ((uint32_t)song->tick >= (uint32_t)song->speed)
1058 	{
1059 		song->tick = 0;
1060 		readNewNote = true;
1061 	}
1062 
1063 	if (readNewNote || editor.stepPlayEnabled) // tick 0
1064 	{
1065 		if (pattDelTime2 == 0) // no pattern delay, time to read note data
1066 		{
1067 			setCurrRowToVisited(); // for MOD2WAV/PAT2SMP
1068 			updateUIPositions(); // update current song positions in UI
1069 
1070 			// read note data and trigger voices
1071 			moduleChannel_t *c = song->channels;
1072 			for (int32_t i = 0; i < AMIGA_VOICES; i++, c++)
1073 			{
1074 				playVoice(c);
1075 				paulaSetVolume(i, c->n_volume);
1076 
1077 				// these take effect after the current DMA cycle is done
1078 				paulaSetData(i, c->n_loopstart);
1079 				paulaSetLength(i, c->n_replen);
1080 			}
1081 		}
1082 		else // pattern delay is on-going
1083 		{
1084 			moduleChannel_t *c = song->channels;
1085 			for (int32_t i = 0; i < AMIGA_VOICES; i++, c++)
1086 				checkEffects(c);
1087 		}
1088 
1089 		// increase row
1090 		if (!editor.stepPlayBackwards)
1091 		{
1092 			song->row++;
1093 			song->rowsCounter++; // for MOD2WAV's progress bar
1094 		}
1095 
1096 		if (pattDelTime > 0)
1097 		{
1098 			pattDelTime2 = pattDelTime;
1099 			pattDelTime = 0;
1100 		}
1101 
1102 		// undo row increase if pattern delay is on-going
1103 		if (pattDelTime2 > 0)
1104 		{
1105 			pattDelTime2--;
1106 			if (pattDelTime2 > 0)
1107 			{
1108 				song->row--;
1109 				song->rowsCounter--; // for MOD2WAV's progress bar
1110 			}
1111 		}
1112 
1113 		if (pBreakFlag)
1114 		{
1115 			song->row = pBreakPosition;
1116 			pBreakPosition = 0;
1117 			pBreakFlag = false;
1118 		}
1119 
1120 		// step-play handling
1121 		if (editor.stepPlayEnabled)
1122 		{
1123 			doStopIt(true);
1124 
1125 			song->currRow = song->row & 0x3F;
1126 			editor.stepPlayEnabled = false;
1127 			editor.stepPlayBackwards = false;
1128 
1129 			ui.updatePatternData = true;
1130 			return true;
1131 		}
1132 
1133 		if (song->row >= MOD_ROWS || posJumpAssert)
1134 			nextPosition();
1135 
1136 		// for pattern block mark feature
1137 		if (editor.blockMarkFlag)
1138 			ui.updateStatusText = true;
1139 	}
1140 	else // tick > 0 (handle effects)
1141 	{
1142 		moduleChannel_t *c = song->channels;
1143 		for (int32_t i = 0; i < AMIGA_VOICES; i++, c++)
1144 			checkEffects(c);
1145 
1146 		if (posJumpAssert)
1147 			nextPosition();
1148 	}
1149 
1150 	// command F00 = stop song, do it here (so that the scopes are updated properly)
1151 	if (doStopSong)
1152 	{
1153 		doStopSong = false;
1154 
1155 		editor.songPlaying = false;
1156 		editor.playMode = PLAY_MODE_NORMAL;
1157 		editor.currMode = MODE_IDLE;
1158 
1159 		pointerResetThreadSafe(); // set normal gray mouse pointer
1160 	}
1161 
1162 	return renderEndCheck(); // MOD2WAV/PAT2SMP listens to the return value (true = not done yet)
1163 }
1164 
modSetPattern(uint8_t pattern)1165 void modSetPattern(uint8_t pattern)
1166 {
1167 	modPattern = pattern;
1168 	song->currPattern = modPattern;
1169 	ui.updateCurrPattText = true;
1170 }
1171 
modSetPos(int16_t order,int16_t row)1172 void modSetPos(int16_t order, int16_t row)
1173 {
1174 	int16_t posEdPos;
1175 
1176 	if (row != -1)
1177 	{
1178 		row = CLAMP(row, 0, 63);
1179 
1180 		song->tick = 0;
1181 		song->row = (int8_t)row;
1182 		song->currRow = (int8_t)row;
1183 	}
1184 
1185 	if (order != -1)
1186 	{
1187 		if (order >= 0)
1188 		{
1189 			modOrder = order;
1190 			song->currOrder = order;
1191 			ui.updateSongPos = true;
1192 
1193 			if (editor.currMode == MODE_PLAY && editor.playMode == PLAY_MODE_NORMAL)
1194 			{
1195 				modPattern = (int8_t)song->header.order[order];
1196 				if (modPattern > MAX_PATTERNS-1)
1197 					modPattern = MAX_PATTERNS-1;
1198 
1199 				song->currPattern = modPattern;
1200 				ui.updateCurrPattText = true;
1201 			}
1202 
1203 			ui.updateSongPattern = true;
1204 			editor.currPatternDisp = &song->header.order[modOrder];
1205 
1206 			posEdPos = song->currOrder;
1207 			if (posEdPos > song->header.numOrders-1)
1208 				posEdPos = song->header.numOrders-1;
1209 
1210 			editor.currPosEdPattDisp = &song->header.order[posEdPos];
1211 
1212 			if (ui.posEdScreenShown)
1213 				ui.updatePosEd = true;
1214 		}
1215 	}
1216 
1217 	ui.updatePatternData = true;
1218 
1219 	if (editor.blockMarkFlag)
1220 		ui.updateStatusText = true;
1221 }
1222 
modSetTempo(int32_t bpm,bool doLockAudio)1223 void modSetTempo(int32_t bpm, bool doLockAudio)
1224 {
1225 	if (bpm < 32 || bpm > 255)
1226 		return;
1227 
1228 	const bool audioWasntLocked = !audio.locked;
1229 	if (doLockAudio && audioWasntLocked)
1230 		lockAudio();
1231 
1232 	modBPM = bpm;
1233 	if (!editor.isSMPRendering && !editor.isWAVRendering)
1234 	{
1235 		song->currBPM = bpm;
1236 		ui.updateSongBPM = true;
1237 	}
1238 
1239 	bpm -= 32; // 32..255 -> 0..223
1240 
1241 	int64_t samplesPerTick64;
1242 	if (editor.isSMPRendering)
1243 		samplesPerTick64 = editor.pat2SmpHQ ? audio.bpmTable28kHz[bpm] : audio.bpmTable20kHz[bpm];
1244 	else
1245 		samplesPerTick64 = audio.bpmTable[bpm];
1246 
1247 	audio.samplesPerTick64 = samplesPerTick64;
1248 
1249 	// calculate tick time length for audio/video sync timestamp
1250 	const uint64_t tickTimeLen64 = audio.tickLengthTable[bpm];
1251 	const uint32_t tickTimeLen = tickTimeLen64 >> 32;
1252 	const uint32_t tickTimeLenFrac = (uint32_t)tickTimeLen64;
1253 
1254 	setSyncTickTimeLen(tickTimeLen, tickTimeLenFrac);
1255 
1256 	if (doLockAudio && audioWasntLocked)
1257 		unlockAudio();
1258 }
1259 
modStop(void)1260 void modStop(void)
1261 {
1262 	editor.songPlaying = false;
1263 	turnOffVoices();
1264 
1265 	if (song != NULL)
1266 	{
1267 		moduleChannel_t *c = song->channels;
1268 		for (int32_t i = 0; i < AMIGA_VOICES; i++, c++)
1269 		{
1270 			c->n_wavecontrol = 0;
1271 			c->n_glissfunk = 0;
1272 			c->n_finetune = 0;
1273 			c->n_loopcount = 0;
1274 		}
1275 	}
1276 
1277 	pBreakFlag = false;
1278 	pattDelTime = 0;
1279 	pattDelTime2 = 0;
1280 	pBreakPosition = 0;
1281 	posJumpAssert = false;
1282 	modRenderDone = true;
1283 
1284 	doStopSong = false; // just in case this flag was stuck from command F00 (stop song)
1285 }
1286 
playPattern(int8_t startRow)1287 void playPattern(int8_t startRow)
1288 {
1289 	if (!editor.stepPlayEnabled)
1290 		pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
1291 
1292 	audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick
1293 	song->currRow = song->row = startRow & 0x3F;
1294 	song->tick = song->speed;
1295 	ciaSetBPM = -1;
1296 
1297 	editor.playMode = PLAY_MODE_PATTERN;
1298 	editor.currMode = MODE_PLAY;
1299 	editor.didQuantize = false;
1300 	editor.songPlaying = true;
1301 }
1302 
incPatt(void)1303 void incPatt(void)
1304 {
1305 	modPattern++;
1306 	if (modPattern > MAX_PATTERNS-1)
1307 		modPattern = 0;
1308 
1309 	song->currPattern = modPattern;
1310 
1311 	ui.updatePatternData = true;
1312 	ui.updateCurrPattText = true;
1313 }
1314 
decPatt(void)1315 void decPatt(void)
1316 {
1317 	modPattern--;
1318 	if (modPattern < 0)
1319 		modPattern = MAX_PATTERNS - 1;
1320 
1321 	song->currPattern = modPattern;
1322 
1323 	ui.updatePatternData = true;
1324 	ui.updateCurrPattText = true;
1325 }
1326 
modPlay(int16_t patt,int16_t order,int8_t row)1327 void modPlay(int16_t patt, int16_t order, int8_t row)
1328 {
1329 	uint8_t oldPlayMode, oldMode;
1330 
1331 	const bool audioWasntLocked = !audio.locked;
1332 	if (audioWasntLocked)
1333 		lockAudio();
1334 
1335 	doStopIt(false);
1336 	turnOffVoices();
1337 	audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick
1338 	ciaSetBPM = -1;
1339 
1340 	if (row != -1)
1341 	{
1342 		if (row >= 0 && row <= 63)
1343 		{
1344 			song->row = row;
1345 			song->currRow = row;
1346 		}
1347 	}
1348 	else
1349 	{
1350 		song->row = 0;
1351 		song->currRow = 0;
1352 	}
1353 
1354 	if (editor.playMode != PLAY_MODE_PATTERN)
1355 	{
1356 		if (modOrder >= song->header.numOrders)
1357 		{
1358 			modOrder = 0;
1359 			song->currOrder = 0;
1360 		}
1361 
1362 		if (order >= 0 && order < song->header.numOrders)
1363 		{
1364 			modOrder = order;
1365 			song->currOrder = order;
1366 		}
1367 
1368 		if (order >= song->header.numOrders)
1369 		{
1370 			modOrder = 0;
1371 			song->currOrder = 0;
1372 		}
1373 	}
1374 
1375 	if (patt >= 0 && patt <= MAX_PATTERNS-1)
1376 		song->currPattern = modPattern = (int8_t)patt;
1377 	else
1378 		song->currPattern = modPattern = (int8_t)song->header.order[modOrder];
1379 
1380 	editor.currPatternDisp = &song->header.order[modOrder];
1381 	editor.currPosEdPattDisp = &song->header.order[modOrder];
1382 
1383 	oldPlayMode = editor.playMode;
1384 	oldMode = editor.currMode;
1385 
1386 	editor.playMode = oldPlayMode;
1387 	editor.currMode = oldMode;
1388 
1389 	song->tick = song->speed-1;
1390 	modRenderDone = false;
1391 	editor.songPlaying = true;
1392 	editor.didQuantize = false;
1393 
1394 	if (editor.playMode != PLAY_MODE_PATTERN)
1395 		editor.musicTime64 = 0; // don't reset playback counter in "play/rec pattern" mode
1396 
1397 	if (audioWasntLocked)
1398 		unlockAudio();
1399 
1400 	if (!editor.isSMPRendering && !editor.isWAVRendering)
1401 	{
1402 		ui.updateSongPos = true;
1403 		ui.updatePatternData = true;
1404 		ui.updateSongPattern = true;
1405 		ui.updateCurrPattText = true;
1406 	}
1407 }
1408 
clearSong(void)1409 void clearSong(void)
1410 {
1411 	uint8_t i;
1412 	moduleChannel_t *ch;
1413 
1414 	assert(song != NULL);
1415 	if (song == NULL)
1416 		return;
1417 
1418 	memset(song->header.order, 0, sizeof (song->header.order));
1419 	memset(song->header.name, 0, sizeof (song->header.name));
1420 
1421 	editor.muted[0] = false;
1422 	editor.muted[1] = false;
1423 	editor.muted[2] = false;
1424 	editor.muted[3] = false;
1425 
1426 	editor.f6Pos = 0;
1427 	editor.f7Pos = 16;
1428 	editor.f8Pos = 32;
1429 	editor.f9Pos = 48;
1430 	editor.f10Pos = 63;
1431 
1432 	editor.musicTime64 = 0;
1433 
1434 	editor.metroFlag = false;
1435 	editor.currSample = 0;
1436 	editor.editMoveAdd = 1;
1437 	editor.blockMarkFlag = false;
1438 	editor.swapChannelFlag = false;
1439 
1440 	song->header.numOrders = 1;
1441 
1442 	for (i = 0; i < MAX_PATTERNS; i++)
1443 		memset(song->patterns[i], 0, (MOD_ROWS * AMIGA_VOICES) * sizeof (note_t));
1444 
1445 	for (i = 0; i < AMIGA_VOICES; i++)
1446 	{
1447 		ch = &song->channels[i];
1448 
1449 		ch->n_wavecontrol = 0;
1450 		ch->n_glissfunk = 0;
1451 		ch->n_finetune = 0;
1452 		ch->n_loopcount = 0;
1453 	}
1454 
1455 	modSetPos(0, 0); // this also refreshes pattern data
1456 
1457 	song->currOrder = 0;
1458 	song->currPattern = 0;
1459 	editor.currPatternDisp = &song->header.order[0];
1460 	editor.currPosEdPattDisp = &song->header.order[0];
1461 
1462 	modSetTempo(editor.initialTempo, true);
1463 	modSetSpeed(editor.initialSpeed);
1464 
1465 	setLEDFilter(false, true); // real PT doesn't do this there, but that's insane
1466 	updateCurrSample();
1467 
1468 	ui.updateSongSize = true;
1469 	renderMuteButtons();
1470 	updateWindowTitle(MOD_IS_MODIFIED);
1471 }
1472 
clearSamples(void)1473 void clearSamples(void)
1474 {
1475 	moduleSample_t *s;
1476 
1477 	assert(song != NULL);
1478 	if (song == NULL)
1479 		return;
1480 
1481 	for (uint8_t i = 0; i < MOD_SAMPLES; i++)
1482 	{
1483 		s = &song->samples[i];
1484 
1485 		s->fineTune = 0;
1486 		s->length = 0;
1487 		s->loopLength = 2;
1488 		s->loopStart = 0;
1489 		s->volume = 0;
1490 
1491 		memset(s->text, 0, sizeof (s->text));
1492 	}
1493 
1494 	memset(song->sampleData, 0, (MOD_SAMPLES + 1) * MAX_SAMPLE_LEN);
1495 
1496 	editor.currSample = 0;
1497 	editor.hiLowInstr = 0;
1498 	editor.sampleZero = false;
1499 	ui.editOpScreenShown = false;
1500 	ui.aboutScreenShown = false;
1501 	editor.blockMarkFlag = false;
1502 
1503 	editor.samplePos = 0;
1504 	updateCurrSample();
1505 
1506 	updateWindowTitle(MOD_IS_MODIFIED);
1507 }
1508 
clearAll(void)1509 void clearAll(void)
1510 {
1511 	clearSamples();
1512 	clearSong();
1513 
1514 	updateWindowTitle(MOD_NOT_MODIFIED);
1515 }
1516 
modFree(void)1517 void modFree(void)
1518 {
1519 	uint8_t i;
1520 
1521 	if (song == NULL)
1522 		return; // not allocated
1523 
1524 	const bool audioWasntLocked = !audio.locked;
1525 	if (audioWasntLocked)
1526 		lockAudio();
1527 
1528 	turnOffVoices();
1529 
1530 	for (i = 0; i < MAX_PATTERNS; i++)
1531 	{
1532 		if (song->patterns[i] != NULL)
1533 			free(song->patterns[i]);
1534 	}
1535 
1536 	if (song->sampleData != NULL)
1537 		free(song->sampleData);
1538 
1539 	free(song);
1540 	song = NULL;
1541 
1542 	if (audioWasntLocked)
1543 		unlockAudio();
1544 }
1545 
restartSong(void)1546 void restartSong(void) // for the beginning of MOD2WAV/PAT2SMP
1547 {
1548 	if (editor.songPlaying)
1549 		modStop();
1550 
1551 	editor.playMode = PLAY_MODE_NORMAL;
1552 	editor.blockMarkFlag = false;
1553 	audio.forceSoundCardSilence = true;
1554 
1555 	song->row = 0;
1556 	song->currRow = 0;
1557 	song->rowsCounter = 0;
1558 
1559 	memset(editor.rowVisitTable, 0, MOD_ORDERS * MOD_ROWS); // for MOD2WAV
1560 
1561 	if (editor.isSMPRendering)
1562 	{
1563 		modPlay(DONT_SET_PATTERN, DONT_SET_ORDER, DONT_SET_ROW);
1564 	}
1565 	else
1566 	{
1567 		song->currSpeed = 6;
1568 		song->currBPM = 125;
1569 		modSetSpeed(6);
1570 		modSetTempo(125, true);
1571 
1572 		modPlay(DONT_SET_PATTERN, 0, 0);
1573 	}
1574 }
1575 
1576 // this function is meant for the end of MOD2WAV/PAT2SMP
resetSong(void)1577 void resetSong(void) // only call this after storeTempVariables() has been called!
1578 {
1579 	modStop();
1580 
1581 	editor.songPlaying = false;
1582 	editor.playMode = PLAY_MODE_NORMAL;
1583 	editor.currMode = MODE_IDLE;
1584 
1585 	turnOffVoices();
1586 
1587 	memset((int8_t *)editor.vuMeterVolumes,     0, sizeof (editor.vuMeterVolumes));
1588 	memset((int8_t *)editor.realVuMeterVolumes, 0, sizeof (editor.realVuMeterVolumes));
1589 	memset((int8_t *)editor.spectrumVolumes,    0, sizeof (editor.spectrumVolumes));
1590 
1591 	memset(song->channels, 0, sizeof (song->channels));
1592 	for (uint8_t i = 0; i < AMIGA_VOICES; i++)
1593 		song->channels[i].n_chanindex = i;
1594 
1595 	modOrder = oldOrder;
1596 	modPattern = (int8_t)oldPattern;
1597 
1598 	song->row = oldRow;
1599 	song->currRow = oldRow;
1600 	song->currBPM = oldBPM;
1601 	song->currOrder = oldOrder;
1602 	song->currPattern = oldPattern;
1603 
1604 	editor.currPosDisp = &song->currOrder;
1605 	editor.currEditPatternDisp = &song->currPattern;
1606 	editor.currPatternDisp = &song->header.order[song->currOrder];
1607 	editor.currPosEdPattDisp = &song->header.order[song->currOrder];
1608 
1609 	modSetSpeed(oldSpeed);
1610 	modSetTempo(oldBPM, true);
1611 
1612 	doStopIt(true);
1613 
1614 	song->tick = 0;
1615 	modRenderDone = false;
1616 	audio.forceSoundCardSilence = false;
1617 }
1618