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, ¬e);
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