1 // for finding memory leaks in debug mode with Visual Studio
2 #if defined _DEBUG && defined _MSC_VER
3 #include <crtdbg.h>
4 #endif
5
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <math.h>
9 #include "ft2_header.h"
10 #include "ft2_config.h"
11 #include "ft2_gui.h"
12 #include "ft2_video.h"
13 #include "ft2_pattern_ed.h"
14 #include "ft2_sample_ed.h"
15 #include "ft2_inst_ed.h"
16 #include "ft2_diskop.h"
17 #include "ft2_midi.h"
18 #include "scopes/ft2_scopes.h"
19 #include "ft2_mouse.h"
20 #include "ft2_sample_loader.h"
21 #include "ft2_tables.h"
22 #include "ft2_structs.h"
23 #include "mixer/ft2_windowed_sinc.h"
24
25 static double dLogTab[4*12*16], dExp2MulTab[32];
26 static bool bxxOverflow;
27 static note_t nilPatternLine[MAX_CHANNELS];
28
29 typedef void (*volColumnEfxRoutine)(channel_t *ch);
30 typedef void (*volColumnEfxRoutine2)(channel_t *ch, uint8_t *volColumnData);
31 typedef void (*efxRoutine)(channel_t *ch, uint8_t param);
32
33 // globally accessed
34 int8_t playMode = 0;
35 bool songPlaying = false, audioPaused = false, musicPaused = false;
36 volatile bool replayerBusy = false;
37 const uint16_t *note2Period = NULL;
38 int16_t patternNumRows[MAX_PATTERNS];
39 channel_t channel[MAX_CHANNELS];
40 song_t song;
41 instr_t *instr[128+4];
42 note_t *pattern[MAX_PATTERNS];
43 // ----------------------------------
44
fixString(char * str,int32_t lastChrPos)45 void fixString(char *str, int32_t lastChrPos) // removes leading spaces and 0x1A chars
46 {
47 for (int32_t i = lastChrPos; i >= 0; i--)
48 {
49 if (str[i] == ' ' || str[i] == 0x1A)
50 str[i] = '\0';
51 else if (str[i] != '\0')
52 break;
53 }
54 str[lastChrPos+1] = '\0';
55 }
56
fixSongName(void)57 void fixSongName(void)
58 {
59 fixString(song.name, 19);
60 }
61
fixInstrAndSampleNames(int16_t insNum)62 void fixInstrAndSampleNames(int16_t insNum)
63 {
64 fixString(song.instrName[insNum], 21);
65 if (instr[insNum] != NULL)
66 {
67 sample_t *s = instr[insNum]->smp;
68 for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++)
69 fixString(s->name, 21);
70 }
71 }
72
resetChannels(void)73 void resetChannels(void)
74 {
75 const bool audioWasntLocked = !audio.locked;
76 if (audioWasntLocked)
77 lockAudio();
78
79 memset(channel, 0, sizeof (channel));
80
81 channel_t *ch = channel;
82 for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++)
83 {
84 ch->instrPtr = instr[0];
85 ch->status = IS_Vol;
86 ch->oldPan = 128;
87 ch->outPan = 128;
88 ch->finalPan = 128;
89
90 ch->channelOff = !editor.chnMode[i]; // set channel mute flag from global mute flag
91 }
92
93 if (audioWasntLocked)
94 unlockAudio();
95 }
96
setSongModifiedFlag(void)97 void setSongModifiedFlag(void)
98 {
99 song.isModified = true;
100 editor.updateWindowTitle = true;
101 }
102
removeSongModifiedFlag(void)103 void removeSongModifiedFlag(void)
104 {
105 song.isModified = false;
106 editor.updateWindowTitle = true;
107 }
108
109 // used on external sample load and during sample loading (on some module formats)
tuneSample(sample_t * s,const int32_t midCFreq,bool linearPeriodsFlag)110 void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag)
111 {
112 #define MIN_PERIOD (0)
113 #define MAX_PERIOD (((10*12*16)-1)-1) /* -1 (because of bugged amigaPeriods table values) */
114
115 double (*dGetHzFromPeriod)(int32_t) = linearPeriodsFlag ? dLinearPeriod2Hz : dAmigaPeriod2Hz;
116 const uint16_t *periodTab = linearPeriodsFlag ? linearPeriods : amigaPeriods;
117
118 if (midCFreq <= 0 || periodTab == NULL)
119 {
120 s->finetune = s->relativeNote = 0;
121 return;
122 }
123
124 // handle frequency boundaries first...
125
126 if (midCFreq <= (int32_t)dGetHzFromPeriod(periodTab[MIN_PERIOD]))
127 {
128 s->finetune = -128;
129 s->relativeNote = -48;
130 return;
131 }
132
133 if (midCFreq >= (int32_t)dGetHzFromPeriod(periodTab[MAX_PERIOD]))
134 {
135 s->finetune = 127;
136 s->relativeNote = 71;
137 return;
138 }
139
140 // check if midCFreq is matching any of the non-finetuned note frequencies (C-0..B-9)
141
142 for (int8_t i = 0; i < 10*12; i++)
143 {
144 if (midCFreq == (int32_t)dGetHzFromPeriod(periodTab[16 + (i<<4)]))
145 {
146 s->finetune = 0;
147 s->relativeNote = i - NOTE_C4;
148 return;
149 }
150 }
151
152 // find closest frequency in period table
153
154 int32_t period = MAX_PERIOD;
155 for (; period >= MIN_PERIOD; period--)
156 {
157 const int32_t curr = (int32_t)dGetHzFromPeriod(periodTab[period]);
158 if (midCFreq == curr)
159 break;
160
161 if (midCFreq > curr)
162 {
163 const int32_t next = (int32_t)dGetHzFromPeriod(periodTab[period+1]);
164 const int32_t errorCurr = ABS(curr-midCFreq);
165 const int32_t errorNext = ABS(next-midCFreq);
166
167 if (errorCurr <= errorNext)
168 break; // current is the closest
169
170 period++;
171 break; // current+1 is the closest
172 }
173 }
174
175 s->finetune = ((period & 31) - 16) << 3;
176 s->relativeNote = (int8_t)(((period & ~31) >> 4) - NOTE_C4);
177 }
178
setPatternLen(uint16_t pattNum,int16_t numRows)179 void setPatternLen(uint16_t pattNum, int16_t numRows)
180 {
181 assert(pattNum < MAX_PATTERNS);
182 if ((numRows < 1 || numRows > MAX_PATT_LEN) || numRows == patternNumRows[pattNum])
183 return;
184
185 const bool audioWasntLocked = !audio.locked;
186 if (audioWasntLocked)
187 lockAudio();
188
189 patternNumRows[pattNum] = numRows;
190
191 if (pattern[pattNum] != NULL)
192 killPatternIfUnused(pattNum);
193
194 // non-FT2 security
195 song.pattDelTime = 0;
196 song.pattDelTime2 = 0;
197 song.pBreakFlag = false;
198 song.posJumpFlag = false;
199 song.pBreakPos = 0;
200
201 song.currNumRows = numRows;
202 if (song.row >= song.currNumRows)
203 {
204 song.row = song.currNumRows - 1;
205 editor.row = song.row;
206 }
207
208 checkMarkLimits();
209
210 if (audioWasntLocked)
211 unlockAudio();
212
213 ui.updatePatternEditor = true;
214 ui.updatePosSections = true;
215 }
216
getUsedSamples(int16_t smpNum)217 int16_t getUsedSamples(int16_t smpNum)
218 {
219 if (instr[smpNum] == NULL)
220 return 0;
221
222 instr_t *ins = instr[smpNum];
223
224 int16_t i = 16 - 1;
225 while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0')
226 i--;
227
228 /* Yes, 'i' can be -1 here, and will be set to at least 0
229 ** because of ins->ta values. Possibly an FT2 bug...
230 */
231 for (int16_t j = 0; j < 96; j++)
232 {
233 if (ins->note2SampleLUT[j] > i)
234 i = ins->note2SampleLUT[j];
235 }
236
237 return i+1;
238 }
239
getRealUsedSamples(int16_t smpNum)240 int16_t getRealUsedSamples(int16_t smpNum)
241 {
242 if (instr[smpNum] == NULL)
243 return 0;
244
245 int8_t i = 16 - 1;
246 while (i >= 0 && instr[smpNum]->smp[i].dataPtr == NULL)
247 i--;
248
249 return i+1;
250 }
251
dLinearPeriod2Hz(int32_t period)252 double dLinearPeriod2Hz(int32_t period)
253 {
254 period &= 0xFFFF; // just in case (actual period range is 0..65535)
255
256 if (period == 0)
257 return 0.0; // in FT2, a period of 0 results in 0Hz
258
259 const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk
260
261 const uint32_t quotient = invPeriod / (12 * 16 * 4);
262 const uint32_t remainder = invPeriod % (12 * 16 * 4);
263
264 return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y >> ((14-quotient) & 31);
265 }
266
dAmigaPeriod2Hz(int32_t period)267 double dAmigaPeriod2Hz(int32_t period)
268 {
269 period &= 0xFFFF; // just in case (actual period range is 0..65535)
270
271 if (period == 0)
272 return 0.0; // in FT2, a period of 0 results in 0Hz
273
274 return (double)(8363 * 1712) / period;
275 }
276
dPeriod2Hz(int32_t period)277 double dPeriod2Hz(int32_t period)
278 {
279 return audio.linearPeriodsFlag ? dLinearPeriod2Hz(period) : dAmigaPeriod2Hz(period);
280 }
281
282 // returns *exact* FT2 C-4 voice rate (depending on finetune, relativeNote and linear/Amiga period mode)
getSampleC4Rate(sample_t * s)283 double getSampleC4Rate(sample_t *s)
284 {
285 int32_t note = NOTE_C4 + s->relativeNote;
286 if (note < 0)
287 return -1; // shouldn't happen (just in case...)
288
289 if (note >= (10*12)-1)
290 return -1; // B-9 (after relativeTone add) = illegal! (won't play in replayer)
291
292 const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16);
293
294 const int32_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period];
295 return dPeriod2Hz(period);
296 }
297
setFrequencyTable(bool linearPeriodsFlag)298 void setFrequencyTable(bool linearPeriodsFlag)
299 {
300 pauseAudio();
301
302 audio.linearPeriodsFlag = linearPeriodsFlag;
303
304 if (audio.linearPeriodsFlag)
305 note2Period = linearPeriods;
306 else
307 note2Period = amigaPeriods;
308
309 resumeAudio();
310
311 if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES)
312 {
313 // update "frequency table" radiobutton, if it's shown
314 setConfigIORadioButtonStates();
315
316 // update mid-C freq. in instr. editor (it can slightly differ between Amiga/linear)
317 if (ui.instEditorShown)
318 drawC4Rate();
319 }
320 }
321
retrigVolume(channel_t * ch)322 static void retrigVolume(channel_t *ch)
323 {
324 ch->realVol = ch->oldVol;
325 ch->outVol = ch->oldVol;
326 ch->outPan = ch->oldPan;
327 ch->status |= IS_Vol + IS_Pan + IS_QuickVol;
328 }
329
retrigEnvelopeVibrato(channel_t * ch)330 static void retrigEnvelopeVibrato(channel_t *ch)
331 {
332 if (!(ch->waveCtrl & 0x04)) ch->vibPos = 0;
333 if (!(ch->waveCtrl & 0x40)) ch->tremPos = 0;
334
335 ch->retrigCnt = 0;
336 ch->tremorPos = 0;
337
338 ch->envSustainActive = true;
339
340 instr_t *ins = ch->instrPtr;
341 assert(ins != NULL);
342
343 if (ins->volEnvFlags & ENV_ENABLED)
344 {
345 ch->volEnvTick = 65535;
346 ch->volEnvPos = 0;
347 }
348
349 if (ins->panEnvFlags & ENV_ENABLED)
350 {
351 ch->panEnvTick = 65535;
352 ch->panEnvPos = 0;
353 }
354
355 ch->fadeoutSpeed = ins->fadeout; // FT2 doesn't check if fadeout is more than 4095!
356 ch->fadeoutVol = 32768;
357
358 if (ins->vibDepth > 0)
359 {
360 ch->eVibPos = 0;
361
362 if (ins->vibSweep > 0)
363 {
364 ch->eVibAmp = 0;
365 ch->eVibSweep = (ins->vibDepth << 8) / ins->vibSweep;
366 }
367 else
368 {
369 ch->eVibAmp = ins->vibDepth << 8;
370 ch->eVibSweep = 0;
371 }
372 }
373 }
374
keyOff(channel_t * ch)375 void keyOff(channel_t *ch)
376 {
377 ch->envSustainActive = false;
378
379 instr_t *ins = ch->instrPtr;
380 assert(ins != NULL);
381
382 if (ins->volEnvFlags & ENV_ENABLED)
383 {
384 if (ch->volEnvTick >= (uint16_t)ins->volEnvPoints[ch->volEnvPos][0])
385 ch->volEnvTick = ins->volEnvPoints[ch->volEnvPos][0] - 1;
386 }
387 else
388 {
389 ch->realVol = 0;
390 ch->outVol = 0;
391 ch->status |= IS_Vol + IS_QuickVol;
392 }
393
394 if (!(ins->panEnvFlags & ENV_ENABLED)) // What..? Probably an FT2 bug.
395 {
396 if (ch->panEnvTick >= (uint16_t)ins->panEnvPoints[ch->panEnvPos][0])
397 ch->panEnvTick = ins->panEnvPoints[ch->panEnvPos][0] - 1;
398 }
399 }
400
calcReplayerLogTab(void)401 void calcReplayerLogTab(void) // for linear period -> hz calculation
402 {
403 for (int32_t i = 0; i < 32; i++)
404 dExp2MulTab[i] = 1.0 / exp2(i); // 1/(2^i)
405
406 for (int32_t i = 0; i < 4*12*16; i++)
407 dLogTab[i] = 8363.0 * exp2(i / 768.0) * 256.0;
408 }
409
calcReplayerVars(int32_t audioFreq)410 void calcReplayerVars(int32_t audioFreq)
411 {
412 assert(audioFreq > 0);
413 if (audioFreq <= 0)
414 return;
415
416 audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq;
417 audio.quickVolRampSamples = (int32_t)round(audioFreq / (double)FT2_QUICKRAMP_SAMPLES);
418 audio.fRampQuickVolMul = (float)(1.0 / audio.quickVolRampSamples);
419
420 for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++)
421 {
422 const int32_t i = bpm - MIN_BPM; // index for tables
423
424 const double dBpmHz = bpm / 2.5;
425 const double dSamplesPerTick = audioFreq / dBpmHz;
426
427 // convert to 32.32 fixed-point
428 audio.samplesPerTick64Tab[i] = (int64_t)((dSamplesPerTick * (UINT32_MAX+1.0)) + 0.5); // rounded
429
430 // BPM Hz -> tick length for performance counter (syncing visuals to audio)
431 double dTimeInt;
432 double dTimeFrac = modf(editor.dPerfFreq / dBpmHz, &dTimeInt);
433 const int32_t timeInt = (int32_t)dTimeInt;
434
435 dTimeFrac = floor((UINT32_MAX+1.0) * dTimeFrac); // fractional part (scaled to 0..2^32-1)
436
437 audio.tickTimeTab[i] = (uint32_t)timeInt;
438 audio.tickTimeFracTab[i] = (uint32_t)dTimeFrac;
439
440 // for calculating volume ramp length for tick-lenghted ramps
441 const int32_t samplesPerTickRounded = (int32_t)(dSamplesPerTick + 0.5); // must be rounded
442 audio.fRampTickMulTab[i] = (float)(1.0 / samplesPerTickRounded);
443 }
444 }
445
446 // for piano in Instr. Ed. (values outside 0..95 can happen)
getPianoKey(uint16_t period,int8_t finetune,int8_t relativeNote)447 int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote)
448 {
449 assert(note2Period != NULL);
450 if (period > note2Period[0])
451 return -1; // outside left piano edge
452
453 finetune = ((int8_t)finetune >> 3) + 16; // -128..127 -> 0..31
454
455 int32_t hiPeriod = 10*12*16;
456 int32_t loPeriod = 0;
457
458 for (int32_t i = 0; i < 7; i++)
459 {
460 const int32_t tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + finetune;
461
462 int32_t lookUp = tmpPeriod - 16;
463 if (lookUp < 0)
464 lookUp = 0;
465
466 if (period >= note2Period[lookUp])
467 hiPeriod = (tmpPeriod - finetune) & ~15;
468 else
469 loPeriod = (tmpPeriod - finetune) & ~15;
470 }
471
472 return (loPeriod >> 4) - relativeNote;
473 }
474
startTone(uint8_t note,uint8_t efx,uint8_t efxData,channel_t * ch)475 static void startTone(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch)
476 {
477 if (note == NOTE_OFF)
478 {
479 keyOff(ch);
480 return;
481 }
482
483 // if we came from Rxy (retrig), we didn't check note (Ton) yet
484 if (note == 0)
485 {
486 note = ch->noteNum;
487 if (note == 0)
488 return; // if still no note, exit from routine
489 }
490
491 ch->noteNum = note;
492
493 assert(ch->instrNum <= 130);
494 instr_t *ins = instr[ch->instrNum];
495 if (ins == NULL)
496 ins = instr[0]; // empty instruments use this placeholder instrument
497
498 ch->instrPtr = ins;
499 ch->mute = ins->mute;
500
501 if (note > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now)
502 note = 96;
503
504 ch->smpNum = ins->note2SampleLUT[note-1] & 0xF; // FT2 doesn't AND here, but let's do it for safety
505 sample_t *s = &ins->smp[ch->smpNum];
506
507 ch->smpPtr = s;
508 ch->relativeNote = s->relativeNote;
509
510 note += ch->relativeNote;
511 if (note >= 10*12) // unsigned check (also handles note < 0)
512 return;
513
514 ch->oldVol = s->volume;
515 ch->oldPan = s->panning;
516
517 if (efx == 0xE && (efxData & 0xF0) == 0x50)
518 ch->finetune = ((efxData & 0x0F) << 4) - 128;
519 else
520 ch->finetune = s->finetune;
521
522 if (note != 0)
523 {
524 const uint16_t noteIndex = ((note-1) << 4) + (((int8_t)ch->finetune >> 3) + 16); // 0..1920
525
526 assert(note2Period != NULL);
527 ch->outPeriod = ch->realPeriod = note2Period[noteIndex];
528 }
529
530 ch->status |= IS_Period + IS_Vol + IS_Pan + IS_Trigger + IS_QuickVol;
531
532 if (efx == 9)
533 {
534 if (efxData > 0)
535 ch->smpOffset = ch->efxData;
536
537 ch->smpStartPos = ch->smpOffset << 8;
538 }
539 else
540 {
541 ch->smpStartPos = 0;
542 }
543 }
544
545 static void volume(channel_t *ch, uint8_t param); // volume slide
546 static void vibrato2(channel_t *ch);
547 static void tonePorta(channel_t *ch, uint8_t param);
548
dummy(channel_t * ch,uint8_t param)549 static void dummy(channel_t *ch, uint8_t param)
550 {
551 (void)ch;
552 (void)param;
553 return;
554 }
555
finePortaUp(channel_t * ch,uint8_t param)556 static void finePortaUp(channel_t *ch, uint8_t param)
557 {
558 if (param == 0)
559 param = ch->fPortaUpSpeed;
560
561 ch->fPortaUpSpeed = param;
562
563 ch->realPeriod -= param << 2;
564 if ((int16_t)ch->realPeriod < 1)
565 ch->realPeriod = 1;
566
567 ch->outPeriod = ch->realPeriod;
568 ch->status |= IS_Period;
569 }
570
finePortaDown(channel_t * ch,uint8_t param)571 static void finePortaDown(channel_t *ch, uint8_t param)
572 {
573 if (param == 0)
574 param = ch->fPortaDownSpeed;
575
576 ch->fPortaDownSpeed = param;
577
578 ch->realPeriod += param << 2;
579 if ((int16_t)ch->realPeriod > MAX_FRQ-1) // FT2 bug, should've been unsigned comparison
580 ch->realPeriod = MAX_FRQ-1;
581
582 ch->outPeriod = ch->realPeriod;
583 ch->status |= IS_Period;
584 }
585
setGlissCtrl(channel_t * ch,uint8_t param)586 static void setGlissCtrl(channel_t *ch, uint8_t param)
587 {
588 ch->glissFunk = param;
589 }
590
setVibratoCtrl(channel_t * ch,uint8_t param)591 static void setVibratoCtrl(channel_t *ch, uint8_t param)
592 {
593 ch->waveCtrl = (ch->waveCtrl & 0xF0) | param;
594 }
595
jumpLoop(channel_t * ch,uint8_t param)596 static void jumpLoop(channel_t *ch, uint8_t param)
597 {
598 if (param == 0)
599 {
600 ch->jumpToRow = song.row & 0xFF;
601 }
602 else if (ch->patLoopCounter == 0)
603 {
604 ch->patLoopCounter = param;
605
606 song.pBreakPos = ch->jumpToRow;
607 song.pBreakFlag = true;
608 }
609 else if (--ch->patLoopCounter > 0)
610 {
611 song.pBreakPos = ch->jumpToRow;
612 song.pBreakFlag = true;
613 }
614 }
615
setTremoloCtrl(channel_t * ch,uint8_t param)616 static void setTremoloCtrl(channel_t *ch, uint8_t param)
617 {
618 ch->waveCtrl = (param << 4) | (ch->waveCtrl & 0x0F);
619 }
620
volFineUp(channel_t * ch,uint8_t param)621 static void volFineUp(channel_t *ch, uint8_t param)
622 {
623 if (param == 0)
624 param = ch->fVolSlideUpSpeed;
625
626 ch->fVolSlideUpSpeed = param;
627
628 ch->realVol += param;
629 if (ch->realVol > 64)
630 ch->realVol = 64;
631
632 ch->outVol = ch->realVol;
633 ch->status |= IS_Vol;
634 }
635
volFineDown(channel_t * ch,uint8_t param)636 static void volFineDown(channel_t *ch, uint8_t param)
637 {
638 if (param == 0)
639 param = ch->fVolSlideDownSpeed;
640
641 ch->fVolSlideDownSpeed = param;
642
643 ch->realVol -= param;
644 if ((int8_t)ch->realVol < 0)
645 ch->realVol = 0;
646
647 ch->outVol = ch->realVol;
648 ch->status |= IS_Vol;
649 }
650
noteCut0(channel_t * ch,uint8_t param)651 static void noteCut0(channel_t *ch, uint8_t param)
652 {
653 if (param == 0) // only a parameter of zero is handled here
654 {
655 ch->realVol = 0;
656 ch->outVol = 0;
657 ch->status |= IS_Vol + IS_QuickVol;
658 }
659 }
660
pattDelay(channel_t * ch,uint8_t param)661 static void pattDelay(channel_t *ch, uint8_t param)
662 {
663 if (song.pattDelTime2 == 0)
664 song.pattDelTime = param + 1;
665
666 (void)ch;
667 }
668
669 static const efxRoutine EJumpTab_TickZero[16] =
670 {
671 dummy, // 0
672 finePortaUp, // 1
673 finePortaDown, // 2
674 setGlissCtrl, // 3
675 setVibratoCtrl, // 4
676 dummy, // 5
677 jumpLoop, // 6
678 setTremoloCtrl, // 7
679 dummy, // 8
680 dummy, // 9
681 volFineUp, // A
682 volFineDown, // B
683 noteCut0, // C
684 dummy, // D
685 pattDelay, // E
686 dummy // F
687 };
688
E_Effects_TickZero(channel_t * ch,uint8_t param)689 static void E_Effects_TickZero(channel_t *ch, uint8_t param)
690 {
691 const uint8_t efx = param >> 4;
692 param &= 0x0F;
693
694 if (ch->channelOff) // channel is muted, only handle some E effects
695 {
696 if (efx == 0x6) jumpLoop(ch, param);
697 else if (efx == 0xE) pattDelay(ch, param);
698
699 return;
700 }
701
702 EJumpTab_TickZero[efx](ch, param);
703 }
704
posJump(channel_t * ch,uint8_t param)705 static void posJump(channel_t *ch, uint8_t param)
706 {
707 if (playMode != PLAYMODE_PATT && playMode != PLAYMODE_RECPATT)
708 {
709 const int16_t pos = (int16_t)param - 1;
710 if (pos < 0 || pos >= song.songLength)
711 bxxOverflow = true; // non-FT2 security fix...
712 else
713 song.songPos = pos;
714 }
715
716 song.pBreakPos = 0;
717 song.posJumpFlag = true;
718
719 (void)ch;
720 }
721
pattBreak(channel_t * ch,uint8_t param)722 static void pattBreak(channel_t *ch, uint8_t param)
723 {
724 param = ((param >> 4) * 10) + (param & 0x0F);
725 if (param <= 63)
726 song.pBreakPos = param;
727 else
728 song.pBreakPos = 0;
729
730 song.posJumpFlag = true;
731
732 (void)ch;
733 }
734
setSpeed(channel_t * ch,uint8_t param)735 static void setSpeed(channel_t *ch, uint8_t param)
736 {
737 if (param >= 32)
738 {
739 song.BPM = param;
740 setMixerBPM(song.BPM);
741 }
742 else
743 {
744 song.tick = song.speed = param;
745 }
746
747 (void)ch;
748 }
749
setGlobalVolume(channel_t * ch,uint8_t param)750 static void setGlobalVolume(channel_t *ch, uint8_t param)
751 {
752 if (param > 64)
753 param = 64;
754
755 song.globalVolume = param;
756
757 channel_t *c = channel;
758 for (int32_t i = 0; i < song.numChannels; i++, c++) // update all voice volumes
759 c->status |= IS_Vol;
760
761 (void)ch;
762 }
763
setEnvelopePos(channel_t * ch,uint8_t param)764 static void setEnvelopePos(channel_t *ch, uint8_t param)
765 {
766 bool envUpdate;
767 int8_t point;
768 int16_t tick;
769
770 instr_t *ins = ch->instrPtr;
771 assert(ins != NULL);
772
773 // (envelope precision has been updated from x.8fp to x.16fp)
774
775 // *** VOLUME ENVELOPE ***
776 if (ins->volEnvFlags & ENV_ENABLED)
777 {
778 ch->volEnvTick = param-1;
779
780 point = 0;
781 envUpdate = true;
782 tick = param;
783
784 if (ins->volEnvLength > 1)
785 {
786 point++;
787 for (int32_t i = 0; i < ins->volEnvLength-1; i++)
788 {
789 if (tick < ins->volEnvPoints[point][0])
790 {
791 point--;
792
793 tick -= ins->volEnvPoints[point][0];
794 if (tick == 0)
795 {
796 envUpdate = false;
797 break;
798 }
799
800 if (ins->volEnvPoints[point+1][0] <= ins->volEnvPoints[point][0])
801 {
802 envUpdate = true;
803 break;
804 }
805
806 int32_t delta = (int8_t)((ins->volEnvPoints[point+1][1] - ins->volEnvPoints[point][1]) & 0xFF) << 16;
807 delta /= (ins->volEnvPoints[point+1][0]-ins->volEnvPoints[point][0]);
808
809 ch->volEnvDelta = delta;
810 ch->volEnvValue = (ch->volEnvDelta * (tick-1)) + ((int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16);
811
812 point++;
813
814 envUpdate = false;
815 break;
816 }
817
818 point++;
819 }
820
821 if (envUpdate)
822 point--;
823 }
824
825 if (envUpdate)
826 {
827 ch->volEnvDelta = 0;
828 ch->volEnvValue = (int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16;
829 }
830
831 if (point >= ins->volEnvLength)
832 {
833 point = ins->volEnvLength-1;
834 if (point < 0)
835 point = 0;
836 }
837
838 ch->volEnvPos = point;
839 }
840
841 // *** PANNING ENVELOPE ***
842 if (ins->volEnvFlags & ENV_SUSTAIN) // What..? An FT2 bug!?
843 {
844 ch->panEnvTick = param-1;
845
846 point = 0;
847 envUpdate = true;
848 tick = param;
849
850 if (ins->panEnvLength > 1)
851 {
852 point++;
853 for (int32_t i = 0; i < ins->panEnvLength-1; i++)
854 {
855 if (tick < ins->panEnvPoints[point][0])
856 {
857 point--;
858
859 tick -= ins->panEnvPoints[point][0];
860 if (tick == 0)
861 {
862 envUpdate = false;
863 break;
864 }
865
866 if (ins->panEnvPoints[point+1][0] <= ins->panEnvPoints[point][0])
867 {
868 envUpdate = true;
869 break;
870 }
871
872 int32_t delta = (int8_t)((ins->panEnvPoints[point+1][1] - ins->panEnvPoints[point][1]) & 0xFF) << 16;
873 delta /= (ins->panEnvPoints[point+1][0]-ins->panEnvPoints[point][0]);
874
875 ch->panEnvDelta = delta;
876 ch->panEnvValue = (ch->panEnvDelta * (tick-1)) + ((int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16);
877
878 point++;
879
880 envUpdate = false;
881 break;
882 }
883
884 point++;
885 }
886
887 if (envUpdate)
888 point--;
889 }
890
891 if (envUpdate)
892 {
893 ch->panEnvDelta = 0;
894 ch->panEnvValue = (int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16;
895 }
896
897 if (point >= ins->panEnvLength)
898 {
899 point = ins->panEnvLength-1;
900 if (point < 0)
901 point = 0;
902 }
903
904 ch->panEnvPos = point;
905 }
906 }
907
908 static const efxRoutine JumpTab_TickZero[36] =
909 {
910 dummy, // 0
911 dummy, // 1
912 dummy, // 2
913 dummy, // 3
914 dummy, // 4
915 dummy, // 5
916 dummy, // 6
917 dummy, // 7
918 dummy, // 8
919 dummy, // 9
920 dummy, // A
921 posJump, // B
922 dummy, // C
923 pattBreak, // D
924 E_Effects_TickZero, // E
925 setSpeed, // F
926 setGlobalVolume, // G
927 dummy, // H
928 dummy, // I
929 dummy, // J
930 dummy, // K
931 setEnvelopePos, // L
932 dummy, // M
933 dummy, // N
934 dummy, // O
935 dummy, // P
936 dummy, // Q
937 dummy, // R
938 dummy, // S
939 dummy, // T
940 dummy, // U
941 dummy, // V
942 dummy, // W
943 dummy, // X
944 dummy, // Y
945 dummy // Z
946 };
947
handleMoreEffects_TickZero(channel_t * ch)948 static void handleMoreEffects_TickZero(channel_t *ch) // called even if channel is muted
949 {
950 if (ch->efx > 35)
951 return;
952
953 JumpTab_TickZero[ch->efx](ch, ch->efxData);
954 }
955
956 /* -- tick-zero volume column effects --
957 ** 2nd parameter is used for a volume column quirk with the Rxy command (multiretrig)
958 */
959
v_SetVibSpeed(channel_t * ch,uint8_t * volColumnData)960 static void v_SetVibSpeed(channel_t *ch, uint8_t *volColumnData)
961 {
962 *volColumnData = (ch->volColumnVol & 0x0F) << 2;
963 if (*volColumnData != 0)
964 ch->vibSpeed = *volColumnData;
965 }
966
v_Volume(channel_t * ch,uint8_t * volColumnData)967 static void v_Volume(channel_t *ch, uint8_t *volColumnData)
968 {
969 *volColumnData -= 16;
970 if (*volColumnData > 64) // no idea why FT2 has this check...
971 *volColumnData = 64;
972
973 ch->outVol = ch->realVol = *volColumnData;
974 ch->status |= IS_Vol + IS_QuickVol;
975 }
976
v_FineSlideDown(channel_t * ch,uint8_t * volColumnData)977 static void v_FineSlideDown(channel_t *ch, uint8_t *volColumnData)
978 {
979 *volColumnData = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol;
980 if ((int8_t)*volColumnData < 0)
981 *volColumnData = 0;
982
983 ch->outVol = ch->realVol = *volColumnData;
984 ch->status |= IS_Vol;
985 }
986
v_FineSlideUp(channel_t * ch,uint8_t * volColumnData)987 static void v_FineSlideUp(channel_t *ch, uint8_t *volColumnData)
988 {
989 *volColumnData = (ch->volColumnVol & 0x0F) + ch->realVol;
990 if (*volColumnData > 64)
991 *volColumnData = 64;
992
993 ch->outVol = ch->realVol = *volColumnData;
994 ch->status |= IS_Vol;
995 }
996
v_SetPan(channel_t * ch,uint8_t * volColumnData)997 static void v_SetPan(channel_t *ch, uint8_t *volColumnData)
998 {
999 *volColumnData <<= 4;
1000
1001 ch->outPan = *volColumnData;
1002 ch->status |= IS_Pan;
1003 }
1004
1005 // -- non-tick-zero volume column effects --
1006
v_SlideDown(channel_t * ch)1007 static void v_SlideDown(channel_t *ch)
1008 {
1009 uint8_t newVol = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol;
1010 if ((int8_t)newVol < 0)
1011 newVol = 0;
1012
1013 ch->outVol = ch->realVol = newVol;
1014 ch->status |= IS_Vol;
1015 }
1016
v_SlideUp(channel_t * ch)1017 static void v_SlideUp(channel_t *ch)
1018 {
1019 uint8_t newVol = (ch->volColumnVol & 0x0F) + ch->realVol;
1020 if (newVol > 64)
1021 newVol = 64;
1022
1023 ch->outVol = ch->realVol = newVol;
1024 ch->status |= IS_Vol;
1025 }
1026
v_Vibrato(channel_t * ch)1027 static void v_Vibrato(channel_t *ch)
1028 {
1029 const uint8_t param = ch->volColumnVol & 0xF;
1030 if (param > 0)
1031 ch->vibDepth = param;
1032
1033 vibrato2(ch);
1034 }
1035
v_PanSlideLeft(channel_t * ch)1036 static void v_PanSlideLeft(channel_t *ch)
1037 {
1038 uint16_t tmp16 = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->outPan;
1039 if (tmp16 < 256) // includes an FT2 bug: pan-slide-left of 0 = set pan to 0
1040 tmp16 = 0;
1041
1042 ch->outPan = (uint8_t)tmp16;
1043 ch->status |= IS_Pan;
1044 }
1045
v_PanSlideRight(channel_t * ch)1046 static void v_PanSlideRight(channel_t *ch)
1047 {
1048 uint16_t tmp16 = (ch->volColumnVol & 0x0F) + ch->outPan;
1049 if (tmp16 > 255)
1050 tmp16 = 255;
1051
1052 ch->outPan = (uint8_t)tmp16;
1053 ch->status |= IS_Pan;
1054 }
1055
v_TonePorta(channel_t * ch)1056 static void v_TonePorta(channel_t *ch)
1057 {
1058 tonePorta(ch, 0); // the last parameter is actually not used in tonePorta()
1059 }
1060
v_dummy(channel_t * ch)1061 static void v_dummy(channel_t *ch)
1062 {
1063 (void)ch;
1064 return;
1065 }
1066
v_dummy2(channel_t * ch,uint8_t * volColumnData)1067 static void v_dummy2(channel_t *ch, uint8_t *volColumnData)
1068 {
1069 (void)ch;
1070 (void)volColumnData;
1071 return;
1072 }
1073
1074 static const volColumnEfxRoutine VJumpTab_TickNonZero[16] =
1075 {
1076 v_dummy, v_dummy, v_dummy, v_dummy,
1077 v_dummy, v_dummy, v_SlideDown, v_SlideUp,
1078 v_dummy, v_dummy, v_dummy, v_Vibrato,
1079 v_dummy, v_PanSlideLeft, v_PanSlideRight, v_TonePorta
1080 };
1081
1082 static const volColumnEfxRoutine2 VJumpTab_TickZero[16] =
1083 {
1084 v_dummy2, v_Volume, v_Volume, v_Volume,
1085 v_Volume, v_Volume, v_dummy2, v_dummy2,
1086 v_FineSlideDown, v_FineSlideUp, v_SetVibSpeed, v_dummy2,
1087 v_SetPan, v_dummy2, v_dummy2, v_dummy2
1088 };
1089
setPan(channel_t * ch,uint8_t param)1090 static void setPan(channel_t *ch, uint8_t param)
1091 {
1092 ch->outPan = param;
1093 ch->status |= IS_Pan;
1094 }
1095
setVol(channel_t * ch,uint8_t param)1096 static void setVol(channel_t *ch, uint8_t param)
1097 {
1098 if (param > 64)
1099 param = 64;
1100
1101 ch->outVol = ch->realVol = param;
1102 ch->status |= IS_Vol + IS_QuickVol;
1103 }
1104
xFinePorta(channel_t * ch,uint8_t param)1105 static void xFinePorta(channel_t *ch, uint8_t param)
1106 {
1107 const uint8_t type = param >> 4;
1108 param &= 0x0F;
1109
1110 if (type == 0x1) // extra fine porta up
1111 {
1112 if (param == 0)
1113 param = ch->ePortaUpSpeed;
1114
1115 ch->ePortaUpSpeed = param;
1116
1117 uint16_t newPeriod = ch->realPeriod;
1118
1119 newPeriod -= param;
1120 if ((int16_t)newPeriod < 1)
1121 newPeriod = 1;
1122
1123 ch->outPeriod = ch->realPeriod = newPeriod;
1124 ch->status |= IS_Period;
1125 }
1126 else if (type == 0x2) // extra fine porta down
1127 {
1128 if (param == 0)
1129 param = ch->ePortaDownSpeed;
1130
1131 ch->ePortaDownSpeed = param;
1132
1133 uint16_t newPeriod = ch->realPeriod;
1134
1135 newPeriod += param;
1136 if ((int16_t)newPeriod > MAX_FRQ-1) // FT2 bug, should've been unsigned comparison
1137 newPeriod = MAX_FRQ-1;
1138
1139 ch->outPeriod = ch->realPeriod = newPeriod;
1140 ch->status |= IS_Period;
1141 }
1142 }
1143
doMultiRetrig(channel_t * ch,uint8_t param)1144 static void doMultiRetrig(channel_t *ch, uint8_t param) // "param" is never used (needed for efx jumptable structure)
1145 {
1146 uint8_t cnt = ch->retrigCnt + 1;
1147 if (cnt < ch->retrigSpeed)
1148 {
1149 ch->retrigCnt = cnt;
1150 return;
1151 }
1152
1153 ch->retrigCnt = 0;
1154
1155 int16_t vol = ch->realVol;
1156 switch (ch->retrigVol)
1157 {
1158 case 0x1: vol -= 1; break;
1159 case 0x2: vol -= 2; break;
1160 case 0x3: vol -= 4; break;
1161 case 0x4: vol -= 8; break;
1162 case 0x5: vol -= 16; break;
1163 case 0x6: vol = (vol >> 1) + (vol >> 3) + (vol >> 4); break;
1164 case 0x7: vol >>= 1; break;
1165 case 0x8: break; // does not change the volume
1166 case 0x9: vol += 1; break;
1167 case 0xA: vol += 2; break;
1168 case 0xB: vol += 4; break;
1169 case 0xC: vol += 8; break;
1170 case 0xD: vol += 16; break;
1171 case 0xE: vol = (vol >> 1) + vol; break;
1172 case 0xF: vol += vol; break;
1173 default: break;
1174 }
1175 vol = CLAMP(vol, 0, 64);
1176
1177 ch->realVol = (uint8_t)vol;
1178 ch->outVol = ch->realVol;
1179
1180 if (ch->volColumnVol >= 0x10 && ch->volColumnVol <= 0x50)
1181 {
1182 ch->outVol = ch->volColumnVol - 0x10;
1183 ch->realVol = ch->outVol;
1184 }
1185 else if (ch->volColumnVol >= 0xC0 && ch->volColumnVol <= 0xCF)
1186 {
1187 ch->outPan = (ch->volColumnVol & 0x0F) << 4;
1188 }
1189
1190 startTone(0, 0, 0, ch);
1191
1192 (void)param;
1193 }
1194
multiRetrig(channel_t * ch,uint8_t param,uint8_t volumeColumnData)1195 static void multiRetrig(channel_t *ch, uint8_t param, uint8_t volumeColumnData)
1196 {
1197 uint8_t tmpParam;
1198
1199 tmpParam = param & 0x0F;
1200 if (tmpParam == 0)
1201 tmpParam = ch->retrigSpeed;
1202
1203 ch->retrigSpeed = tmpParam;
1204
1205 tmpParam = param >> 4;
1206 if (tmpParam == 0)
1207 tmpParam = ch->retrigVol;
1208
1209 ch->retrigVol = tmpParam;
1210
1211 if (volumeColumnData == 0)
1212 doMultiRetrig(ch, 0); // the second parameter is never used (needed for efx jumptable structure)
1213 }
1214
handleEffects_TickZero(channel_t * ch)1215 static void handleEffects_TickZero(channel_t *ch)
1216 {
1217 // volume column effects
1218 uint8_t newVolCol = ch->volColumnVol; // manipulated by vol. column effects, then used for multiretrig check (FT2 quirk)
1219 VJumpTab_TickZero[ch->volColumnVol >> 4](ch, &newVolCol);
1220
1221 // normal effects
1222 const uint8_t param = ch->efxData;
1223 if (ch->efx == 0 && param == 0)
1224 return; // no effect
1225
1226 if (ch->efx == 8) setPan(ch, param);
1227 else if (ch->efx == 12) setVol(ch, param);
1228 else if (ch->efx == 27) multiRetrig(ch, param, newVolCol);
1229 else if (ch->efx == 33) xFinePorta(ch, param);
1230
1231 handleMoreEffects_TickZero(ch);
1232 }
1233
fixTonePorta(channel_t * ch,const note_t * p,uint8_t inst)1234 static void fixTonePorta(channel_t *ch, const note_t *p, uint8_t inst)
1235 {
1236 if (p->note > 0)
1237 {
1238 if (p->note == NOTE_OFF)
1239 {
1240 keyOff(ch);
1241 }
1242 else
1243 {
1244 const uint16_t note = (((p->note-1) + ch->relativeNote) << 4) + (((int8_t)ch->finetune >> 3) + 16);
1245 if (note < MAX_NOTES)
1246 {
1247 assert(note2Period != NULL);
1248 ch->wantPeriod = note2Period[note];
1249
1250 if (ch->wantPeriod == ch->realPeriod)
1251 ch->portaDirection = 0;
1252 else if (ch->wantPeriod > ch->realPeriod)
1253 ch->portaDirection = 1;
1254 else
1255 ch->portaDirection = 2;
1256 }
1257 }
1258 }
1259
1260 if (inst > 0)
1261 {
1262 retrigVolume(ch);
1263 if (p->note != NOTE_OFF)
1264 retrigEnvelopeVibrato(ch);
1265 }
1266 }
1267
getNewNote(channel_t * ch,const note_t * p)1268 static void getNewNote(channel_t *ch, const note_t *p)
1269 {
1270 ch->volColumnVol = p->vol;
1271
1272 if (ch->efx == 0)
1273 {
1274 if (ch->efxData > 0) // we have an arpeggio running, set period back
1275 {
1276 ch->outPeriod = ch->realPeriod;
1277 ch->status |= IS_Period;
1278 }
1279 }
1280 else
1281 {
1282 // if we have a vibrato on previous row (ch) that ends at current row (p), set period back
1283 if ((ch->efx == 4 || ch->efx == 6) && (p->efx != 4 && p->efx != 6))
1284 {
1285 ch->outPeriod = ch->realPeriod;
1286 ch->status |= IS_Period;
1287 }
1288 }
1289
1290 ch->efx = p->efx;
1291 ch->efxData = p->efxData;
1292 ch->noteData = (p->instr << 8) | p->note;
1293
1294 if (ch->channelOff) // channel is muted, only handle some effects
1295 {
1296 handleMoreEffects_TickZero(ch);
1297 return;
1298 }
1299
1300 // 'inst' var is used for later if checks...
1301 uint8_t inst = p->instr;
1302 if (inst > 0)
1303 {
1304 if (inst <= MAX_INST)
1305 ch->instrNum = inst;
1306 else
1307 inst = 0;
1308 }
1309
1310 bool checkEfx = true;
1311 if (p->efx == 0x0E)
1312 {
1313 if (p->efxData >= 0xD1 && p->efxData <= 0xDF)
1314 return; // we have a note delay (ED1..EDF)
1315 else if (p->efxData == 0x90)
1316 checkEfx = false;
1317 }
1318
1319 if (checkEfx)
1320 {
1321 if ((ch->volColumnVol & 0xF0) == 0xF0) // gxx
1322 {
1323 const uint8_t volColumnData = ch->volColumnVol & 0x0F;
1324 if (volColumnData > 0)
1325 ch->portaSpeed = volColumnData << 6;
1326
1327 fixTonePorta(ch, p, inst);
1328 handleEffects_TickZero(ch);
1329 return;
1330 }
1331
1332 if (p->efx == 3 || p->efx == 5) // 3xx or 5xx
1333 {
1334 if (p->efx != 5 && p->efxData != 0)
1335 ch->portaSpeed = p->efxData << 2;
1336
1337 fixTonePorta(ch, p, inst);
1338 handleEffects_TickZero(ch);
1339 return;
1340 }
1341
1342 if (p->efx == 0x14 && p->efxData == 0) // K00 (KeyOff - only handle tick 0 here)
1343 {
1344 keyOff(ch);
1345
1346 if (inst)
1347 retrigVolume(ch);
1348
1349 handleEffects_TickZero(ch);
1350 return;
1351 }
1352
1353 if (p->note == 0)
1354 {
1355 if (inst > 0)
1356 {
1357 retrigVolume(ch);
1358 retrigEnvelopeVibrato(ch);
1359 }
1360
1361 handleEffects_TickZero(ch);
1362 return;
1363 }
1364 }
1365
1366 if (p->note == NOTE_OFF)
1367 keyOff(ch);
1368 else
1369 startTone(p->note, p->efx, p->efxData, ch);
1370
1371 if (inst > 0)
1372 {
1373 retrigVolume(ch);
1374 if (p->note != NOTE_OFF)
1375 retrigEnvelopeVibrato(ch);
1376 }
1377
1378 handleEffects_TickZero(ch);
1379 }
1380
updateChannel(channel_t * ch)1381 static void updateChannel(channel_t *ch)
1382 {
1383 bool envInterpolateFlag, envDidInterpolate;
1384 uint8_t envPos;
1385 int16_t autoVibVal;
1386 uint16_t autoVibAmp;
1387 int32_t envVal;
1388 float fVol;
1389
1390 instr_t *ins = ch->instrPtr;
1391 assert(ins != NULL);
1392
1393 // *** FADEOUT ***
1394 if (!ch->envSustainActive)
1395 {
1396 ch->status |= IS_Vol;
1397
1398 // unsigned clamp + reset
1399 if (ch->fadeoutVol >= ch->fadeoutSpeed)
1400 {
1401 ch->fadeoutVol -= ch->fadeoutSpeed;
1402 }
1403 else
1404 {
1405 ch->fadeoutVol = 0;
1406 ch->fadeoutSpeed = 0;
1407 }
1408 }
1409
1410 if (!ch->mute)
1411 {
1412 // (envelope precision has been updated from x.8fp to x.16fp)
1413
1414 // *** VOLUME ENVELOPE ***
1415 envVal = 0;
1416 if (ins->volEnvFlags & ENV_ENABLED)
1417 {
1418 envDidInterpolate = false;
1419 envPos = ch->volEnvPos;
1420
1421 if (++ch->volEnvTick == ins->volEnvPoints[envPos][0])
1422 {
1423 ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16;
1424
1425 envPos++;
1426 if (ins->volEnvFlags & ENV_LOOP)
1427 {
1428 envPos--;
1429
1430 if (envPos == ins->volEnvLoopEnd)
1431 {
1432 if (!(ins->volEnvFlags & ENV_SUSTAIN) || envPos != ins->volEnvSustain || ch->envSustainActive)
1433 {
1434 envPos = ins->volEnvLoopStart;
1435 ch->volEnvTick = ins->volEnvPoints[envPos][0];
1436 ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16;
1437 }
1438 }
1439
1440 envPos++;
1441 }
1442
1443 if (envPos < ins->volEnvLength)
1444 {
1445 envInterpolateFlag = true;
1446 if ((ins->volEnvFlags & ENV_SUSTAIN) && ch->envSustainActive)
1447 {
1448 if (envPos-1 == ins->volEnvSustain)
1449 {
1450 envPos--;
1451 ch->volEnvDelta = 0;
1452 envInterpolateFlag = false;
1453 }
1454 }
1455
1456 if (envInterpolateFlag)
1457 {
1458 ch->volEnvPos = envPos;
1459
1460 ch->volEnvDelta = 0;
1461 if (ins->volEnvPoints[envPos][0] > ins->volEnvPoints[envPos-1][0])
1462 {
1463 int32_t delta = (int8_t)((ins->volEnvPoints[envPos][1] - ins->volEnvPoints[envPos-1][1]) & 0xFF) << 16;
1464 delta /= (ins->volEnvPoints[envPos][0]-ins->volEnvPoints[envPos-1][0]);
1465 ch->volEnvDelta = delta;
1466
1467 envVal = ch->volEnvValue;
1468 envDidInterpolate = true;
1469 }
1470 }
1471 }
1472 else
1473 {
1474 ch->volEnvDelta = 0;
1475 }
1476 }
1477
1478 if (!envDidInterpolate)
1479 {
1480 ch->volEnvValue += ch->volEnvDelta;
1481
1482 envVal = ch->volEnvValue;
1483 if (envVal > 64<<16)
1484 {
1485 if (envVal > 128<<16)
1486 envVal = 64<<16;
1487 else
1488 envVal = 0;
1489
1490 ch->volEnvDelta = 0;
1491 }
1492 }
1493
1494 const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
1495
1496 fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
1497 fVol *= (int32_t)envVal * (1.0f / (64.0f * (1 << 16))); // volume envelope value
1498
1499 ch->status |= IS_Vol; // update mixer vol every tick when vol envelope is enabled
1500 }
1501 else
1502 {
1503 const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
1504 fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
1505 }
1506
1507 /* FT2 doesn't clamp here, but it's actually important if you
1508 ** move envelope points with the mouse while playing the instrument.
1509 */
1510 ch->fFinalVol = CLAMP(fVol, 0.0f, 1.0f);
1511 }
1512 else
1513 {
1514 ch->fFinalVol = 0.0f;
1515 }
1516
1517 // *** PANNING ENVELOPE ***
1518
1519 envVal = 0;
1520 if (ins->panEnvFlags & ENV_ENABLED)
1521 {
1522 envDidInterpolate = false;
1523 envPos = ch->panEnvPos;
1524
1525 if (++ch->panEnvTick == ins->panEnvPoints[envPos][0])
1526 {
1527 ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16;
1528
1529 envPos++;
1530 if (ins->panEnvFlags & ENV_LOOP)
1531 {
1532 envPos--;
1533
1534 if (envPos == ins->panEnvLoopEnd)
1535 {
1536 if (!(ins->panEnvFlags & ENV_SUSTAIN) || envPos != ins->panEnvSustain || ch->envSustainActive)
1537 {
1538 envPos = ins->panEnvLoopStart;
1539
1540 ch->panEnvTick = ins->panEnvPoints[envPos][0];
1541 ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16;
1542 }
1543 }
1544
1545 envPos++;
1546 }
1547
1548 if (envPos < ins->panEnvLength)
1549 {
1550 envInterpolateFlag = true;
1551 if ((ins->panEnvFlags & ENV_SUSTAIN) && ch->envSustainActive)
1552 {
1553 if (envPos-1 == ins->panEnvSustain)
1554 {
1555 envPos--;
1556 ch->panEnvDelta = 0;
1557 envInterpolateFlag = false;
1558 }
1559 }
1560
1561 if (envInterpolateFlag)
1562 {
1563 ch->panEnvPos = envPos;
1564
1565 ch->panEnvDelta = 0;
1566 if (ins->panEnvPoints[envPos][0] > ins->panEnvPoints[envPos-1][0])
1567 {
1568 int32_t delta = (int8_t)((ins->panEnvPoints[envPos][1] - ins->panEnvPoints[envPos-1][1]) & 0xFF) << 16;
1569 delta /= (ins->panEnvPoints[envPos][0]-ins->panEnvPoints[envPos-1][0]);
1570 ch->panEnvDelta = delta;
1571
1572 envVal = ch->panEnvValue;
1573 envDidInterpolate = true;
1574 }
1575 }
1576 }
1577 else
1578 {
1579 ch->panEnvDelta = 0;
1580 }
1581 }
1582
1583 if (!envDidInterpolate)
1584 {
1585 ch->panEnvValue += ch->panEnvDelta;
1586
1587 envVal = ch->panEnvValue;
1588 if (envVal > 64<<16)
1589 {
1590 if (envVal > 128<<16)
1591 envVal = 64<<16;
1592 else
1593 envVal = 0;
1594
1595 ch->panEnvDelta = 0;
1596 }
1597 }
1598
1599 envVal -= 32<<16; // center panning envelope value
1600
1601 const int32_t pan = 128 - ABS(ch->outPan-128);
1602 const int32_t panAdd = (pan * envVal) >> (16+5);
1603 ch->finalPan = (uint8_t)(ch->outPan + panAdd);
1604
1605 ch->status |= IS_Pan; // update pan every tick because pan envelope is enabled
1606 }
1607 else
1608 {
1609 ch->finalPan = ch->outPan;
1610 }
1611
1612 // *** AUTO VIBRATO ***
1613 #ifdef HAS_MIDI
1614 if (ch->midiVibDepth > 0 || ins->vibDepth > 0)
1615 #else
1616 if (ins->vibDepth > 0)
1617 #endif
1618 {
1619 if (ch->eVibSweep > 0)
1620 {
1621 autoVibAmp = ch->eVibSweep;
1622 if (ch->envSustainActive)
1623 {
1624 autoVibAmp += ch->eVibAmp;
1625 if ((autoVibAmp >> 8) > ins->vibDepth)
1626 {
1627 autoVibAmp = ins->vibDepth << 8;
1628 ch->eVibSweep = 0;
1629 }
1630
1631 ch->eVibAmp = autoVibAmp;
1632 }
1633 }
1634 else
1635 {
1636 autoVibAmp = ch->eVibAmp;
1637 }
1638
1639 #ifdef HAS_MIDI
1640 // non-FT2 hack to make modulation wheel work when auto vibrato rate is zero
1641 if (ch->midiVibDepth > 0 && ins->vibRate == 0)
1642 ins->vibRate = 0x20;
1643
1644 autoVibAmp += ch->midiVibDepth;
1645 #endif
1646 ch->eVibPos += ins->vibRate;
1647
1648 if (ins->vibType == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square
1649 else if (ins->vibType == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up
1650 else if (ins->vibType == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down
1651 else autoVibVal = vibSineTab[ch->eVibPos]; // sine
1652
1653 autoVibVal <<= 2;
1654 uint16_t tmpPeriod = (autoVibVal * (int16_t)autoVibAmp) >> 16;
1655
1656 tmpPeriod += ch->outPeriod;
1657 if (tmpPeriod > MAX_FRQ-1)
1658 tmpPeriod = 0; // yes, FT2 does this (!)
1659
1660 #ifdef HAS_MIDI
1661 if (midi.enable)
1662 tmpPeriod -= ch->midiPitch;
1663 #endif
1664
1665 ch->finalPeriod = tmpPeriod;
1666 ch->status |= IS_Period;
1667 }
1668 else
1669 {
1670 ch->finalPeriod = ch->outPeriod;
1671
1672 #ifdef HAS_MIDI
1673 if (midi.enable)
1674 {
1675 ch->finalPeriod -= ch->midiPitch;
1676 ch->status |= IS_Period;
1677 }
1678 #endif
1679 }
1680 }
1681
1682 // for arpeggio and portamento (semitone-slide mode)
relocateTon(uint16_t period,uint8_t arpNote,channel_t * ch)1683 static uint16_t relocateTon(uint16_t period, uint8_t arpNote, channel_t *ch)
1684 {
1685 int32_t tmpPeriod;
1686
1687 const int32_t fineTune = ((int8_t)ch->finetune >> 3) + 16;
1688
1689 /* FT2 bug, should've been 10*12*16. Notes above B-7 (95) will have issues.
1690 ** You can only achieve such high notes by having a high relative note setting.
1691 */
1692 int32_t hiPeriod = 8*12*16;
1693
1694 int32_t loPeriod = 0;
1695
1696 for (int32_t i = 0; i < 8; i++)
1697 {
1698 tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + fineTune;
1699
1700 int32_t lookUp = tmpPeriod - 8;
1701 if (lookUp < 0)
1702 lookUp = 0; // safety fix (C-0 w/ f.tune <= -65). This seems to result in 0 in FT2 (TODO: verify)
1703
1704 if (period >= note2Period[lookUp])
1705 hiPeriod = (tmpPeriod - fineTune) & ~15;
1706 else
1707 loPeriod = (tmpPeriod - fineTune) & ~15;
1708 }
1709
1710 tmpPeriod = loPeriod + fineTune + (arpNote << 4);
1711 if (tmpPeriod >= (8*12*16+15)-1) // FT2 bug, should've been 10*12*16+16 (also notice the +2 difference)
1712 tmpPeriod = (8*12*16+16)-1;
1713
1714 return note2Period[tmpPeriod];
1715 }
1716
vibrato2(channel_t * ch)1717 static void vibrato2(channel_t *ch)
1718 {
1719 uint8_t tmpVib = (ch->vibPos >> 2) & 0x1F;
1720
1721 switch (ch->waveCtrl & 3)
1722 {
1723 // 0: sine
1724 case 0: tmpVib = vibTab[tmpVib]; break;
1725
1726 // 1: ramp
1727 case 1:
1728 {
1729 tmpVib <<= 3;
1730 if ((int8_t)ch->vibPos < 0)
1731 tmpVib = ~tmpVib;
1732 }
1733 break;
1734
1735 // 2/3: square
1736 default: tmpVib = 255; break;
1737 }
1738
1739 tmpVib = (tmpVib * ch->vibDepth) >> 5; // logical shift (unsigned calc.), not arithmetic shift
1740
1741 if ((int8_t)ch->vibPos < 0)
1742 ch->outPeriod = ch->realPeriod - tmpVib;
1743 else
1744 ch->outPeriod = ch->realPeriod + tmpVib;
1745
1746 ch->status |= IS_Period;
1747 ch->vibPos += ch->vibSpeed;
1748 }
1749
arp(channel_t * ch,uint8_t param)1750 static void arp(channel_t *ch, uint8_t param)
1751 {
1752 uint8_t note;
1753
1754 const uint8_t tick = arpTab[song.tick & 0xFF]; // non-FT2 protection (we have 248 extra overflow bytes in LUT, but not more!)
1755 if (tick == 0)
1756 {
1757 ch->outPeriod = ch->realPeriod;
1758 }
1759 else
1760 {
1761 if (tick == 1)
1762 note = param >> 4;
1763 else
1764 note = param & 0x0F; // tick 2
1765
1766 ch->outPeriod = relocateTon(ch->realPeriod, note, ch);
1767 }
1768
1769 ch->status |= IS_Period;
1770 }
1771
portaUp(channel_t * ch,uint8_t param)1772 static void portaUp(channel_t *ch, uint8_t param)
1773 {
1774 if (param == 0)
1775 param = ch->portaUpSpeed;
1776
1777 ch->portaUpSpeed = param;
1778
1779 ch->realPeriod -= param << 2;
1780 if ((int16_t)ch->realPeriod < 1)
1781 ch->realPeriod = 1;
1782
1783 ch->outPeriod = ch->realPeriod;
1784 ch->status |= IS_Period;
1785 }
1786
portaDown(channel_t * ch,uint8_t param)1787 static void portaDown(channel_t *ch, uint8_t param)
1788 {
1789 if (param == 0)
1790 param = ch->portaDownSpeed;
1791
1792 ch->portaDownSpeed = param;
1793
1794 ch->realPeriod += param << 2;
1795 if ((int16_t)ch->realPeriod > MAX_FRQ-1) // FT2 bug, should've been unsigned comparison
1796 ch->realPeriod = MAX_FRQ-1;
1797
1798 ch->outPeriod = ch->realPeriod;
1799 ch->status |= IS_Period;
1800 }
1801
tonePorta(channel_t * ch,uint8_t param)1802 static void tonePorta(channel_t *ch, uint8_t param)
1803 {
1804 if (ch->portaDirection == 0)
1805 return;
1806
1807 if (ch->portaDirection > 1)
1808 {
1809 ch->realPeriod -= ch->portaSpeed;
1810 if ((int16_t)ch->realPeriod <= (int16_t)ch->wantPeriod)
1811 {
1812 ch->portaDirection = 1;
1813 ch->realPeriod = ch->wantPeriod;
1814 }
1815 }
1816 else
1817 {
1818 ch->realPeriod += ch->portaSpeed;
1819 if (ch->realPeriod >= ch->wantPeriod)
1820 {
1821 ch->portaDirection = 1;
1822 ch->realPeriod = ch->wantPeriod;
1823 }
1824 }
1825
1826 if (ch->glissFunk) // semitone-slide flag
1827 ch->outPeriod = relocateTon(ch->realPeriod, 0, ch);
1828 else
1829 ch->outPeriod = ch->realPeriod;
1830
1831 ch->status |= IS_Period;
1832
1833 (void)param;
1834 }
1835
vibrato(channel_t * ch,uint8_t param)1836 static void vibrato(channel_t *ch, uint8_t param)
1837 {
1838 uint8_t tmp8;
1839
1840 if (param > 0)
1841 {
1842 tmp8 = param & 0x0F;
1843 if (tmp8 > 0)
1844 ch->vibDepth = tmp8;
1845
1846 tmp8 = (param & 0xF0) >> 2;
1847 if (tmp8 > 0)
1848 ch->vibSpeed = tmp8;
1849 }
1850
1851 vibrato2(ch);
1852 }
1853
tonePlusVol(channel_t * ch,uint8_t param)1854 static void tonePlusVol(channel_t *ch, uint8_t param)
1855 {
1856 tonePorta(ch, 0); // the last parameter is actually not used in tonePorta()
1857 volume(ch, param);
1858
1859 (void)param;
1860 }
1861
vibratoPlusVol(channel_t * ch,uint8_t param)1862 static void vibratoPlusVol(channel_t *ch, uint8_t param)
1863 {
1864 vibrato2(ch);
1865 volume(ch, param);
1866
1867 (void)param;
1868 }
1869
tremolo(channel_t * ch,uint8_t param)1870 static void tremolo(channel_t *ch, uint8_t param)
1871 {
1872 uint8_t tmp8;
1873 int16_t tremVol;
1874
1875 const uint8_t tmpEff = param;
1876 if (tmpEff > 0)
1877 {
1878 tmp8 = tmpEff & 0x0F;
1879 if (tmp8 > 0)
1880 ch->tremDepth = tmp8;
1881
1882 tmp8 = (tmpEff & 0xF0) >> 2;
1883 if (tmp8 > 0)
1884 ch->tremSpeed = tmp8;
1885 }
1886
1887 uint8_t tmpTrem = (ch->tremPos >> 2) & 0x1F;
1888 switch ((ch->waveCtrl >> 4) & 3)
1889 {
1890 // 0: sine
1891 case 0: tmpTrem = vibTab[tmpTrem]; break;
1892
1893 // 1: ramp
1894 case 1:
1895 {
1896 tmpTrem <<= 3;
1897 if ((int8_t)ch->vibPos < 0) // FT2 bug, should've been ch->tremPos
1898 tmpTrem = ~tmpTrem;
1899 }
1900 break;
1901
1902 // 2/3: square
1903 default: tmpTrem = 255; break;
1904 }
1905 tmpTrem = (tmpTrem * ch->tremDepth) >> 6; // logical shift (unsigned calc.), not arithmetic shift
1906
1907 if ((int8_t)ch->tremPos < 0)
1908 {
1909 tremVol = ch->realVol - tmpTrem;
1910 if (tremVol < 0)
1911 tremVol = 0;
1912 }
1913 else
1914 {
1915 tremVol = ch->realVol + tmpTrem;
1916 if (tremVol > 64)
1917 tremVol = 64;
1918 }
1919
1920 ch->outVol = (uint8_t)tremVol;
1921 ch->status |= IS_Vol;
1922 ch->tremPos += ch->tremSpeed;
1923 }
1924
volume(channel_t * ch,uint8_t param)1925 static void volume(channel_t *ch, uint8_t param) // volume slide
1926 {
1927 if (param == 0)
1928 param = ch->volSlideSpeed;
1929
1930 ch->volSlideSpeed = param;
1931
1932 uint8_t newVol = ch->realVol;
1933 if ((param & 0xF0) == 0)
1934 {
1935 newVol -= param;
1936 if ((int8_t)newVol < 0)
1937 newVol = 0;
1938 }
1939 else
1940 {
1941 param >>= 4;
1942
1943 newVol += param;
1944 if (newVol > 64)
1945 newVol = 64;
1946 }
1947
1948 ch->outVol = ch->realVol = newVol;
1949 ch->status |= IS_Vol;
1950 }
1951
globalVolSlide(channel_t * ch,uint8_t param)1952 static void globalVolSlide(channel_t *ch, uint8_t param)
1953 {
1954 if (param == 0)
1955 param = ch->globVolSlideSpeed;
1956
1957 ch->globVolSlideSpeed = param;
1958
1959 uint8_t newVol = (uint8_t)song.globalVolume;
1960 if ((param & 0xF0) == 0)
1961 {
1962 newVol -= param;
1963 if ((int8_t)newVol < 0)
1964 newVol = 0;
1965 }
1966 else
1967 {
1968 param >>= 4;
1969
1970 newVol += param;
1971 if (newVol > 64)
1972 newVol = 64;
1973 }
1974
1975 song.globalVolume = newVol;
1976
1977 channel_t *c = channel;
1978 for (int32_t i = 0; i < song.numChannels; i++, c++) // update all voice volumes
1979 c->status |= IS_Vol;
1980 }
1981
keyOffCmd(channel_t * ch,uint8_t param)1982 static void keyOffCmd(channel_t *ch, uint8_t param)
1983 {
1984 if ((uint8_t)(song.speed-song.tick) == (param & 31))
1985 keyOff(ch);
1986 }
1987
panningSlide(channel_t * ch,uint8_t param)1988 static void panningSlide(channel_t *ch, uint8_t param)
1989 {
1990 if (param == 0)
1991 param = ch->panningSlideSpeed;
1992
1993 ch->panningSlideSpeed = param;
1994
1995 int16_t newPan = (int16_t)ch->outPan;
1996 if ((param & 0xF0) == 0)
1997 {
1998 newPan -= param;
1999 if (newPan < 0)
2000 newPan = 0;
2001 }
2002 else
2003 {
2004 param >>= 4;
2005
2006 newPan += param;
2007 if (newPan > 255)
2008 newPan = 255;
2009 }
2010
2011 ch->outPan = (uint8_t)newPan;
2012 ch->status |= IS_Pan;
2013 }
2014
tremor(channel_t * ch,uint8_t param)2015 static void tremor(channel_t *ch, uint8_t param)
2016 {
2017 if (param == 0)
2018 param = ch->tremorSave;
2019
2020 ch->tremorSave = param;
2021
2022 uint8_t tremorSign = ch->tremorPos & 0x80;
2023 uint8_t tremorData = ch->tremorPos & 0x7F;
2024
2025 tremorData--;
2026 if ((int8_t)tremorData < 0)
2027 {
2028 if (tremorSign == 0x80)
2029 {
2030 tremorSign = 0x00;
2031 tremorData = param & 0x0F;
2032 }
2033 else
2034 {
2035 tremorSign = 0x80;
2036 tremorData = param >> 4;
2037 }
2038 }
2039
2040 ch->tremorPos = tremorSign | tremorData;
2041 ch->outVol = (tremorSign == 0x80) ? ch->realVol : 0;
2042 ch->status |= IS_Vol + IS_QuickVol;
2043 }
2044
retrigNote(channel_t * ch,uint8_t param)2045 static void retrigNote(channel_t *ch, uint8_t param)
2046 {
2047 if (param == 0) // E9x with a param of zero is handled in getNewNote()
2048 return;
2049
2050 if ((song.speed-song.tick) % param == 0)
2051 {
2052 startTone(0, 0, 0, ch);
2053 retrigEnvelopeVibrato(ch);
2054 }
2055 }
2056
noteCut(channel_t * ch,uint8_t param)2057 static void noteCut(channel_t *ch, uint8_t param)
2058 {
2059 if ((uint8_t)(song.speed-song.tick) == param)
2060 {
2061 ch->outVol = ch->realVol = 0;
2062 ch->status |= IS_Vol + IS_QuickVol;
2063 }
2064 }
2065
noteDelay(channel_t * ch,uint8_t param)2066 static void noteDelay(channel_t *ch, uint8_t param)
2067 {
2068 if ((uint8_t)(song.speed-song.tick) == param)
2069 {
2070 startTone(ch->noteData & 0xFF, 0, 0, ch);
2071
2072 if ((ch->noteData & 0xFF00) > 0)
2073 retrigVolume(ch);
2074
2075 retrigEnvelopeVibrato(ch);
2076
2077 if (ch->volColumnVol >= 0x10 && ch->volColumnVol <= 0x50)
2078 {
2079 ch->outVol = ch->volColumnVol - 16;
2080 ch->realVol = ch->outVol;
2081 }
2082 else if (ch->volColumnVol >= 0xC0 && ch->volColumnVol <= 0xCF)
2083 {
2084 ch->outPan = (ch->volColumnVol & 0x0F) << 4;
2085 }
2086 }
2087 }
2088
2089 static const efxRoutine EJumpTab_TickNonZero[16] =
2090 {
2091 dummy, // 0
2092 dummy, // 1
2093 dummy, // 2
2094 dummy, // 3
2095 dummy, // 4
2096 dummy, // 5
2097 dummy, // 6
2098 dummy, // 7
2099 dummy, // 8
2100 retrigNote, // 9
2101 dummy, // A
2102 dummy, // B
2103 noteCut, // C
2104 noteDelay, // D
2105 dummy, // E
2106 dummy // F
2107 };
2108
E_Effects_TickNonZero(channel_t * ch,uint8_t param)2109 static void E_Effects_TickNonZero(channel_t *ch, uint8_t param)
2110 {
2111 EJumpTab_TickNonZero[param >> 4](ch, param & 0xF);
2112 }
2113
2114 static const efxRoutine JumpTab_TickNonZero[36] =
2115 {
2116 arp, // 0
2117 portaUp, // 1
2118 portaDown, // 2
2119 tonePorta, // 3
2120 vibrato, // 4
2121 tonePlusVol, // 5
2122 vibratoPlusVol, // 6
2123 tremolo, // 7
2124 dummy, // 8
2125 dummy, // 9
2126 volume, // A
2127 dummy, // B
2128 dummy, // C
2129 dummy, // D
2130 E_Effects_TickNonZero, // E
2131 dummy, // F
2132 dummy, // G
2133 globalVolSlide, // H
2134 dummy, // I
2135 dummy, // J
2136 keyOffCmd, // K
2137 dummy, // L
2138 dummy, // M
2139 dummy, // N
2140 dummy, // O
2141 panningSlide, // P
2142 dummy, // Q
2143 doMultiRetrig, // R
2144 dummy, // S
2145 tremor, // T
2146 dummy, // U
2147 dummy, // V
2148 dummy, // W
2149 dummy, // X
2150 dummy, // Y
2151 dummy // Z
2152 };
2153
handleEffects_TickNonZero(channel_t * ch)2154 static void handleEffects_TickNonZero(channel_t *ch)
2155 {
2156 if (ch->channelOff)
2157 return; // muted
2158
2159 // volume column effects
2160 VJumpTab_TickNonZero[ch->volColumnVol >> 4](ch);
2161
2162 // normal effects
2163 if ((ch->efx == 0 && ch->efxData == 0) || ch->efx > 35)
2164 return; // no effect
2165
2166 JumpTab_TickNonZero[ch->efx](ch, ch->efxData);
2167 }
2168
getNextPos(void)2169 static void getNextPos(void)
2170 {
2171 if (song.tick != 1)
2172 return;
2173
2174 song.row++;
2175
2176 if (song.pattDelTime > 0)
2177 {
2178 song.pattDelTime2 = song.pattDelTime;
2179 song.pattDelTime = 0;
2180 }
2181
2182 if (song.pattDelTime2 > 0)
2183 {
2184 song.pattDelTime2--;
2185 if (song.pattDelTime2 > 0)
2186 song.row--;
2187 }
2188
2189 if (song.pBreakFlag)
2190 {
2191 song.pBreakFlag = false;
2192 song.row = song.pBreakPos;
2193 }
2194
2195 if (song.row >= song.currNumRows || song.posJumpFlag)
2196 {
2197 song.row = song.pBreakPos;
2198 song.pBreakPos = 0;
2199 song.posJumpFlag = false;
2200
2201 if (playMode != PLAYMODE_PATT && playMode != PLAYMODE_RECPATT)
2202 {
2203 if (bxxOverflow)
2204 {
2205 song.songPos = 0;
2206 bxxOverflow = false;
2207 }
2208 else if (++song.songPos >= song.songLength)
2209 {
2210 editor.wavReachedEndFlag = true;
2211 song.songPos = song.songLoopStart;
2212 }
2213
2214 assert(song.songPos <= 255);
2215 song.pattNum = song.orders[song.songPos & 0xFF];
2216 song.currNumRows = patternNumRows[song.pattNum & 0xFF];
2217 }
2218 }
2219 }
2220
pauseMusic(void)2221 void pauseMusic(void) // stops reading pattern data
2222 {
2223 musicPaused = true;
2224 while (replayerBusy);
2225 }
2226
resumeMusic(void)2227 void resumeMusic(void) // starts reading pattern data
2228 {
2229 musicPaused = false;
2230 }
2231
2232
tickReplayer(void)2233 void tickReplayer(void) // periodically called from audio callback
2234 {
2235 int32_t i;
2236 channel_t *ch;
2237
2238 if (musicPaused || !songPlaying)
2239 {
2240 ch = channel;
2241 for (i = 0; i < song.numChannels; i++, ch++)
2242 updateChannel(ch);
2243
2244 return;
2245 }
2246
2247 // for song playback counter (hh:mm:ss)
2248 if (song.BPM >= MIN_BPM && song.BPM <= MAX_BPM)
2249 song.musicTime64 += musicTimeTab64[song.BPM-MIN_BPM];
2250
2251 bool tickZero = false;
2252 if (--song.tick == 0)
2253 {
2254 song.tick = song.speed;
2255 tickZero = true;
2256 }
2257
2258 song.curReplayerTick = (uint8_t)song.tick; // for audio/video syncing (and recording)
2259
2260 const bool readNewNote = tickZero && (song.pattDelTime2 == 0);
2261 if (readNewNote)
2262 {
2263 // set audio/video syncing variables
2264 song.curReplayerRow = (uint8_t)song.row;
2265 song.curReplayerPattNum = (uint8_t)song.pattNum;
2266 song.curReplayerSongPos = (uint8_t)song.songPos;
2267 // ----------------------------------------------
2268
2269 const note_t *p = nilPatternLine;
2270 if (pattern[song.pattNum] != NULL)
2271 p = &pattern[song.pattNum][song.row * MAX_CHANNELS];
2272
2273 ch = channel;
2274 for (i = 0; i < song.numChannels; i++, ch++, p++)
2275 {
2276 getNewNote(ch, p);
2277 updateChannel(ch);
2278 }
2279 }
2280 else
2281 {
2282 ch = channel;
2283 for (i = 0; i < song.numChannels; i++, ch++)
2284 {
2285 handleEffects_TickNonZero(ch);
2286 updateChannel(ch);
2287 }
2288 }
2289
2290 getNextPos();
2291 }
2292
resetMusic(void)2293 void resetMusic(void)
2294 {
2295 const bool audioWasntLocked = !audio.locked;
2296 if (audioWasntLocked)
2297 lockAudio();
2298
2299 song.tick = 1;
2300 stopVoices();
2301
2302 if (audioWasntLocked)
2303 unlockAudio();
2304
2305 setPos(0, 0, false);
2306
2307 if (!songPlaying)
2308 {
2309 setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
2310 setScrollBarPos(SB_POS_ED, 0, false);
2311 }
2312 }
2313
setPos(int16_t songPos,int16_t row,bool resetTimer)2314 void setPos(int16_t songPos, int16_t row, bool resetTimer)
2315 {
2316 const bool audioWasntLocked = !audio.locked;
2317 if (audioWasntLocked)
2318 lockAudio();
2319
2320 if (songPos > -1)
2321 {
2322 song.songPos = songPos;
2323 if (song.songLength > 0 && song.songPos >= song.songLength)
2324 song.songPos = song.songLength - 1;
2325
2326 song.pattNum = song.orders[song.songPos];
2327 assert(song.pattNum < MAX_PATTERNS);
2328 song.currNumRows = patternNumRows[song.pattNum];
2329
2330 checkMarkLimits(); // non-FT2 safety
2331 }
2332
2333 if (row > -1)
2334 {
2335 song.row = row;
2336 if (song.row >= song.currNumRows)
2337 song.row = song.currNumRows-1;
2338 }
2339
2340 // if not playing, update local position variables
2341 if (!songPlaying)
2342 {
2343 if (row > -1)
2344 {
2345 editor.row = (uint8_t)row;
2346 ui.updatePatternEditor = true;
2347 }
2348
2349 if (songPos > -1)
2350 {
2351 editor.editPattern = (uint8_t)song.pattNum;
2352 editor.songPos = song.songPos;
2353 ui.updatePosSections = true;
2354 }
2355 }
2356
2357 if (resetTimer)
2358 song.tick = 1;
2359
2360 if (audioWasntLocked)
2361 unlockAudio();
2362 }
2363
delta2Samp(int8_t * p,int32_t length,uint8_t smpFlags)2364 void delta2Samp(int8_t *p, int32_t length, uint8_t smpFlags)
2365 {
2366 bool sample16Bit = !!(smpFlags & SAMPLE_16BIT);
2367 bool stereo = !!(smpFlags & SAMPLE_STEREO);
2368
2369 if (stereo)
2370 {
2371 length >>= 1;
2372
2373 if (sample16Bit)
2374 {
2375 int16_t *p16L = (int16_t *)p;
2376 int16_t *p16R = (int16_t *)p + length;
2377
2378 int16_t olds16L = 0;
2379 int16_t olds16R = 0;
2380
2381 for (int32_t i = 0; i < length; i++)
2382 {
2383 const int16_t news16L = p16L[i] + olds16L;
2384 p16L[i] = news16L;
2385 olds16L = news16L;
2386
2387 const int16_t news16R = p16R[i] + olds16R;
2388 p16R[i] = news16R;
2389 olds16R = news16R;
2390
2391 p16L[i] = (int16_t)((olds16L + olds16R) >> 1);
2392 }
2393 }
2394 else // 8-bit
2395 {
2396 int8_t *p8L = (int8_t *)p;
2397 int8_t *p8R = (int8_t *)p + length;
2398 int8_t olds8L = 0;
2399 int8_t olds8R = 0;
2400
2401 for (int32_t i = 0; i < length; i++)
2402 {
2403 const int8_t news8L = p8L[i] + olds8L;
2404 p8L[i] = news8L;
2405 olds8L = news8L;
2406
2407 const int8_t news8R = p8R[i] + olds8R;
2408 p8R[i] = news8R;
2409 olds8R = news8R;
2410
2411 p8L[i] = (int8_t)((olds8L + olds8R) >> 1);
2412 }
2413 }
2414 }
2415 else // mono (normal sample)
2416 {
2417 if (sample16Bit)
2418 {
2419 int16_t *p16 = (int16_t *)p;
2420
2421 int16_t olds16L = 0;
2422 for (int32_t i = 0; i < length; i++)
2423 {
2424 const int16_t news16 = p16[i] + olds16L;
2425 p16[i] = news16;
2426 olds16L = news16;
2427 }
2428 }
2429 else // 8-bit
2430 {
2431 int8_t *p8 = (int8_t *)p;
2432
2433 int8_t olds8L = 0;
2434 for (int32_t i = 0; i < length; i++)
2435 {
2436 const int8_t news8 = p8[i] + olds8L;
2437 p8[i] = news8;
2438 olds8L = news8;
2439 }
2440 }
2441 }
2442 }
2443
samp2Delta(int8_t * p,int32_t length,uint8_t smpFlags)2444 void samp2Delta(int8_t *p, int32_t length, uint8_t smpFlags)
2445 {
2446 if (smpFlags & SAMPLE_16BIT)
2447 {
2448 int16_t *p16 = (int16_t *)p;
2449
2450 int16_t newS16 = 0;
2451 for (int32_t i = 0; i < length; i++)
2452 {
2453 const int16_t oldS16 = p16[i];
2454 p16[i] -= newS16;
2455 newS16 = oldS16;
2456 }
2457 }
2458 else // 8-bit
2459 {
2460 int8_t *p8 = (int8_t *)p;
2461
2462 int8_t newS8 = 0;
2463 for (int32_t i = 0; i < length; i++)
2464 {
2465 const int8_t oldS8 = p8[i];
2466 p8[i] -= newS8;
2467 newS8 = oldS8;
2468 }
2469 }
2470 }
2471
allocateInstr(int16_t insNum)2472 bool allocateInstr(int16_t insNum)
2473 {
2474 if (instr[insNum] != NULL)
2475 return false; // already allocated
2476
2477 instr_t *p = (instr_t *)malloc(sizeof (instr_t));
2478 if (p == NULL)
2479 return false;
2480
2481 memset(p, 0, sizeof (instr_t));
2482 for (int32_t i = 0; i < MAX_SMP_PER_INST; i++)
2483 {
2484 p->smp[i].panning = 128;
2485 p->smp[i].volume = 64;
2486 }
2487
2488 setStdEnvelope(p, 0, 3);
2489
2490 const bool audioWasntLocked = !audio.locked;
2491 if (audioWasntLocked)
2492 lockAudio();
2493
2494 instr[insNum] = p;
2495
2496 if (audioWasntLocked)
2497 unlockAudio();
2498
2499 return true;
2500 }
2501
freeInstr(int32_t insNum)2502 void freeInstr(int32_t insNum)
2503 {
2504 if (instr[insNum] == NULL)
2505 return; // not allocated
2506
2507 pauseAudio(); // channel instrument pointers are now cleared
2508
2509 sample_t *s = instr[insNum]->smp;
2510 for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) // free sample data
2511 freeSmpData(s);
2512
2513 free(instr[insNum]);
2514 instr[insNum] = NULL;
2515
2516 resumeAudio();
2517 }
2518
freeAllInstr(void)2519 void freeAllInstr(void)
2520 {
2521 pauseAudio(); // channel instrument pointers are now cleared
2522 for (int32_t i = 1; i <= MAX_INST; i++)
2523 {
2524 if (instr[i] != NULL)
2525 {
2526 sample_t *s = instr[i]->smp;
2527 for (int32_t j = 0; j < MAX_SMP_PER_INST; j++, s++) // free sample data
2528 freeSmpData(s);
2529
2530 free(instr[i]);
2531 instr[i] = NULL;
2532 }
2533 }
2534 resumeAudio();
2535 }
2536
freeSample(int16_t insNum,int16_t smpNum)2537 void freeSample(int16_t insNum, int16_t smpNum)
2538 {
2539 if (instr[insNum] == NULL)
2540 return; // instrument not allocated
2541
2542 pauseAudio(); // voice sample pointers are now cleared
2543
2544 sample_t *s = &instr[insNum]->smp[smpNum];
2545 freeSmpData(s);
2546
2547 memset(s, 0, sizeof (sample_t));
2548 s->panning = 128;
2549 s->volume = 64;
2550
2551 resumeAudio();
2552 }
2553
freeAllPatterns(void)2554 void freeAllPatterns(void)
2555 {
2556 pauseAudio();
2557 for (int32_t i = 0; i < MAX_PATTERNS; i++)
2558 {
2559 if (pattern[i] != NULL)
2560 {
2561 free(pattern[i]);
2562 pattern[i] = NULL;
2563 }
2564 }
2565 resumeAudio();
2566 }
2567
setStdEnvelope(instr_t * ins,int16_t i,uint8_t type)2568 void setStdEnvelope(instr_t *ins, int16_t i, uint8_t type)
2569 {
2570 if (ins == NULL)
2571 return;
2572
2573 pauseMusic();
2574
2575 if (type & 1)
2576 {
2577 memcpy(ins->volEnvPoints, config.stdEnvPoints[i][0], 2*2*12);
2578 ins->volEnvLength = (uint8_t)config.stdVolEnvLength[i];
2579 ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[i];
2580 ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[i];
2581 ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[i];
2582 ins->fadeout = config.stdFadeout[i];
2583 ins->vibRate = (uint8_t)config.stdVibRate[i];
2584 ins->vibDepth = (uint8_t)config.stdVibDepth[i];
2585 ins->vibSweep = (uint8_t)config.stdVibSweep[i];
2586 ins->vibType = (uint8_t)config.stdVibType[i];
2587 ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[i];
2588 }
2589
2590 if (type & 2)
2591 {
2592 memcpy(ins->panEnvPoints, config.stdEnvPoints[i][1], 2*2*12);
2593 ins->panEnvLength = (uint8_t)config.stdPanEnvLength[0];
2594 ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[0];
2595 ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[0];
2596 ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[0];
2597 ins->panEnvFlags = (uint8_t)config.stdPanEnvFlags[0];
2598 }
2599
2600 resumeMusic();
2601 }
2602
setNoEnvelope(instr_t * ins)2603 void setNoEnvelope(instr_t *ins)
2604 {
2605 if (ins == NULL)
2606 return;
2607
2608 pauseMusic();
2609
2610 memcpy(ins->volEnvPoints, config.stdEnvPoints[0][0], 2*2*12);
2611 ins->volEnvLength = (uint8_t)config.stdVolEnvLength[0];
2612 ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[0];
2613 ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[0];
2614 ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[0];
2615 ins->volEnvFlags = 0;
2616
2617 memcpy(ins->panEnvPoints, config.stdEnvPoints[0][1], 2*2*12);
2618 ins->panEnvLength = (uint8_t)config.stdPanEnvLength[0];
2619 ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[0];
2620 ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[0];
2621 ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[0];
2622 ins->panEnvFlags = 0;
2623
2624 ins->fadeout = 0;
2625 ins->vibRate = 0;
2626 ins->vibDepth = 0;
2627 ins->vibSweep = 0;
2628 ins->vibType = 0;
2629
2630 resumeMusic();
2631 }
2632
patternEmpty(uint16_t pattNum)2633 bool patternEmpty(uint16_t pattNum)
2634 {
2635 if (pattern[pattNum] == NULL)
2636 return true;
2637
2638 const uint8_t *scanPtr = (const uint8_t *)pattern[pattNum];
2639 const uint32_t scanLen = patternNumRows[pattNum] * TRACK_WIDTH;
2640
2641 for (uint32_t i = 0; i < scanLen; i++)
2642 {
2643 if (scanPtr[i] != 0)
2644 return false;
2645 }
2646
2647 return true;
2648 }
2649
updateChanNums(void)2650 void updateChanNums(void)
2651 {
2652 assert(!(song.numChannels & 1));
2653
2654 const int32_t maxChannelsShown = getMaxVisibleChannels();
2655
2656 int32_t channelsShown = song.numChannels;
2657 if (channelsShown > maxChannelsShown)
2658 channelsShown = maxChannelsShown;
2659
2660 ui.numChannelsShown = (uint8_t)channelsShown;
2661 ui.pattChanScrollShown = (song.numChannels > maxChannelsShown);
2662
2663 if (ui.patternEditorShown)
2664 {
2665 if (ui.channelOffset > song.numChannels-ui.numChannelsShown)
2666 setScrollBarPos(SB_CHAN_SCROLL, song.numChannels - ui.numChannelsShown, true);
2667 }
2668
2669 if (ui.pattChanScrollShown)
2670 {
2671 if (ui.patternEditorShown)
2672 {
2673 showScrollBar(SB_CHAN_SCROLL);
2674 showPushButton(PB_CHAN_SCROLL_LEFT);
2675 showPushButton(PB_CHAN_SCROLL_RIGHT);
2676 }
2677
2678 setScrollBarEnd(SB_CHAN_SCROLL, song.numChannels);
2679 setScrollBarPageLength(SB_CHAN_SCROLL, ui.numChannelsShown);
2680 }
2681 else
2682 {
2683 hideScrollBar(SB_CHAN_SCROLL);
2684 hidePushButton(PB_CHAN_SCROLL_LEFT);
2685 hidePushButton(PB_CHAN_SCROLL_RIGHT);
2686
2687 setScrollBarPos(SB_CHAN_SCROLL, 0, false);
2688
2689 ui.channelOffset = 0;
2690 }
2691
2692 if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
2693 cursor.ch = ui.channelOffset+ui.numChannelsShown - 1;
2694 }
2695
conv8BitSample(int8_t * p,int32_t length,bool stereo)2696 void conv8BitSample(int8_t *p, int32_t length, bool stereo) // changes sample sign
2697 {
2698 if (stereo)
2699 {
2700 length >>= 1;
2701
2702 int8_t *p2 = &p[length];
2703 for (int32_t i = 0; i < length; i++)
2704 {
2705 const int8_t l = p[i] ^ 0x80;
2706 const int8_t r = p2[i] ^ 0x80;
2707
2708 p[i] = (int8_t)((l + r) >> 1);
2709 }
2710 }
2711 else
2712 {
2713 for (int32_t i = 0; i < length; i++)
2714 p[i] ^= 0x80;
2715 }
2716 }
2717
conv16BitSample(int8_t * p,int32_t length,bool stereo)2718 void conv16BitSample(int8_t *p, int32_t length, bool stereo) // changes sample sign
2719 {
2720 int16_t *p16_1 = (int16_t *)p;
2721
2722 if (stereo)
2723 {
2724 length >>= 1;
2725
2726 int16_t *p16_2 = (int16_t *)p + length;
2727 for (int32_t i = 0; i < length; i++)
2728 {
2729 const int16_t l = p16_1[i] ^ 0x8000;
2730 const int16_t r = p16_2[i] ^ 0x8000;
2731
2732 p16_1[i] = (int16_t)((l + r) >> 1);
2733 }
2734 }
2735 else
2736 {
2737 for (int32_t i = 0; i < length; i++)
2738 p16_1[i] ^= 0x8000;
2739 }
2740 }
2741
closeReplayer(void)2742 void closeReplayer(void)
2743 {
2744 freeAllInstr();
2745 freeAllPatterns();
2746
2747 // free reserved instruments
2748
2749 if (instr[0] != NULL)
2750 {
2751 free(instr[0]);
2752 instr[0] = NULL;
2753 }
2754
2755 if (instr[130] != NULL)
2756 {
2757 free(instr[130]);
2758 instr[130] = NULL;
2759 }
2760
2761 if (instr[131] != NULL)
2762 {
2763 free(instr[131]);
2764 instr[131] = NULL;
2765 }
2766
2767 freeWindowedSincTables();
2768 }
2769
setupReplayer(void)2770 bool setupReplayer(void)
2771 {
2772 for (int32_t i = 0; i < MAX_PATTERNS; i++)
2773 patternNumRows[i] = 64;
2774
2775 playMode = PLAYMODE_IDLE;
2776 songPlaying = false;
2777
2778 // unmute all channels (must be done before resetChannels() call)
2779 for (int32_t i = 0; i < MAX_CHANNELS; i++)
2780 editor.chnMode[i] = 1;
2781
2782 resetChannels();
2783
2784 song.songLength = 1;
2785 song.numChannels = 8;
2786 editor.BPM = song.BPM = 125;
2787 editor.speed = song.initialSpeed = song.speed = 6;
2788 editor.globalVolume = song.globalVolume = 64;
2789 audio.linearPeriodsFlag = true;
2790 note2Period = linearPeriods;
2791
2792 if (!calcWindowedSincTables())
2793 {
2794 showErrorMsgBox("Not enough memory!");
2795 return false;
2796 }
2797
2798 calcPanningTable();
2799
2800 setPos(0, 0, true);
2801
2802 if (!allocateInstr(0))
2803 {
2804 showErrorMsgBox("Not enough memory!");
2805 return false;
2806 }
2807 instr[0]->smp[0].volume = 0;
2808
2809 if (!allocateInstr(130))
2810 {
2811 showErrorMsgBox("Not enough memory!");
2812 return false;
2813 }
2814 memset(instr[130], 0, sizeof (instr_t));
2815
2816 if (!allocateInstr(131)) // Instr. Ed. display instrument for unallocated/empty instruments
2817 {
2818 showErrorMsgBox("Not enough memory!");
2819 return false;
2820 }
2821
2822 memset(instr[131], 0, sizeof (instr_t));
2823 for (int32_t i = 0; i < 16; i++)
2824 instr[131]->smp[i].panning = 128;
2825
2826 editor.tmpPattern = 65535; // pattern editor update/redraw kludge
2827 return true;
2828 }
2829
startPlaying(int8_t mode,int16_t row)2830 void startPlaying(int8_t mode, int16_t row)
2831 {
2832 lockMixerCallback();
2833
2834 assert(mode != PLAYMODE_IDLE && mode != PLAYMODE_EDIT);
2835 if (mode == PLAYMODE_PATT || mode == PLAYMODE_RECPATT)
2836 setPos(-1, row, true);
2837 else
2838 setPos(editor.songPos, row, true);
2839
2840 playMode = mode;
2841 songPlaying = true;
2842
2843 resetReplayerState();
2844 resetPlaybackTime();
2845
2846 // non-FT2 fix: If song speed was 0, set it back to initial speed on play
2847 if (song.speed == 0)
2848 song.speed = song.initialSpeed;
2849
2850 audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick
2851
2852 unlockMixerCallback();
2853
2854 ui.updatePosSections = true;
2855 ui.updatePatternEditor = true;
2856 }
2857
stopPlaying(void)2858 void stopPlaying(void)
2859 {
2860 bool songWasPlaying = songPlaying;
2861 playMode = PLAYMODE_IDLE;
2862 songPlaying = false;
2863
2864 if (config.killNotesOnStopPlay)
2865 {
2866 // safely kills all voices
2867 lockMixerCallback();
2868 unlockMixerCallback();
2869 }
2870 else
2871 {
2872 for (uint8_t i = 0; i < MAX_CHANNELS; i++)
2873 playTone(i, 0, NOTE_OFF, -1, 0, 0);
2874 }
2875
2876 // if song was playing, update local row (fixes certain glitches)
2877 if (songWasPlaying)
2878 editor.row = song.row;
2879
2880 #ifdef HAS_MIDI
2881 midi.currMIDIVibDepth = 0;
2882 midi.currMIDIPitch = 0;
2883 #endif
2884
2885 memset(editor.keyOnTab, 0, sizeof (editor.keyOnTab));
2886
2887 // kludge to prevent UI from being out of sync with replayer vars on stop
2888 song.songPos = editor.songPos;
2889 song.row = editor.row;
2890 song.pattNum = editor.editPattern;
2891
2892 ui.updatePosSections = true;
2893 ui.updatePatternEditor = true;
2894
2895 // certain non-FT2 fixes
2896 song.tick = editor.tick = 1;
2897 song.globalVolume = editor.globalVolume = 64;
2898 ui.drawGlobVolFlag = true;
2899 }
2900
2901 // from keyboard/smp. ed.
playTone(uint8_t chNum,uint8_t insNum,uint8_t note,int8_t vol,uint16_t midiVibDepth,uint16_t midiPitch)2902 void playTone(uint8_t chNum, uint8_t insNum, uint8_t note, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch)
2903 {
2904 instr_t *ins = instr[insNum];
2905 if (ins == NULL)
2906 return;
2907
2908 assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && note <= NOTE_OFF);
2909 channel_t *ch = &channel[chNum];
2910
2911 // FT2 bugfix: Don't play tone if certain requirements are not met
2912 if (note != NOTE_OFF)
2913 {
2914 if (note == 0 || note > 96)
2915 return;
2916
2917 sample_t *s = &ins->smp[ins->note2SampleLUT[note-1] & 0xF];
2918
2919 int16_t finalNote = (int16_t)note + s->relativeNote;
2920 if (s->dataPtr == NULL || s->length == 0 || finalNote <= 0 || finalNote >= 12*10)
2921 return;
2922 }
2923 // -------------------
2924
2925 lockAudio();
2926
2927 if (insNum != 0 && note != NOTE_OFF)
2928 {
2929 ch->noteData = (insNum << 8) | (ch->noteData & 0xFF);
2930 ch->instrNum = insNum;
2931 }
2932
2933 ch->noteData = (ch->noteData & 0xFF00) | note;
2934 ch->efx = 0;
2935 ch->efxData = 0;
2936
2937 startTone(note, 0, 0, ch);
2938
2939 if (note != NOTE_OFF)
2940 {
2941 retrigVolume(ch);
2942 retrigEnvelopeVibrato(ch);
2943
2944 if (vol != -1) // if jamming note keys, vol -1 = use sample's volume
2945 {
2946 ch->realVol = vol;
2947 ch->outVol = vol;
2948 ch->oldVol = vol;
2949 }
2950 }
2951
2952 ch->midiVibDepth = midiVibDepth;
2953 ch->midiPitch = midiPitch;
2954
2955 updateChannel(ch);
2956
2957 unlockAudio();
2958 }
2959
2960 // smp. ed.
playSample(uint8_t chNum,uint8_t insNum,uint8_t smpNum,uint8_t note,uint16_t midiVibDepth,uint16_t midiPitch)2961 void playSample(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch)
2962 {
2963 if (instr[insNum] == NULL)
2964 return;
2965
2966 // for sampling playback line in Smp. Ed.
2967 lastChInstr[chNum].instrNum = 255;
2968 lastChInstr[chNum].smpNum = 255;
2969 editor.curPlayInstr = 255;
2970 editor.curPlaySmp = 255;
2971
2972 assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && smpNum < MAX_SMP_PER_INST && note <= NOTE_OFF);
2973 channel_t *ch = &channel[chNum];
2974
2975 memcpy(&instr[130]->smp[0], &instr[insNum]->smp[smpNum], sizeof (sample_t));
2976
2977 uint8_t vol = instr[insNum]->smp[smpNum].volume;
2978
2979 lockAudio();
2980
2981 ch->instrNum = 130;
2982 ch->noteData = (ch->instrNum << 8) | note;
2983 ch->efx = 0;
2984
2985 startTone(note, 0, 0, ch);
2986
2987 if (note != NOTE_OFF)
2988 {
2989 retrigVolume(ch);
2990 retrigEnvelopeVibrato(ch);
2991
2992 ch->realVol = vol;
2993 ch->outVol = vol;
2994 ch->oldVol = vol;
2995 }
2996
2997 ch->midiVibDepth = midiVibDepth;
2998 ch->midiPitch = midiPitch;
2999
3000 updateChannel(ch);
3001
3002 unlockAudio();
3003
3004 while (ch->status & IS_Trigger); // wait for sample to latch in mixer
3005
3006 // for sampling playback line in Smp. Ed.
3007 editor.curPlayInstr = editor.curInstr;
3008 editor.curPlaySmp = editor.curSmp;
3009 }
3010
3011 // smp. ed.
playRange(uint8_t chNum,uint8_t insNum,uint8_t smpNum,uint8_t note,uint16_t midiVibDepth,uint16_t midiPitch,int32_t smpOffset,int32_t length)3012 void playRange(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch, int32_t smpOffset, int32_t length)
3013 {
3014 if (instr[insNum] == NULL)
3015 return;
3016
3017 // for sampling playback line in Smp. Ed.
3018 lastChInstr[chNum].instrNum = 255;
3019 lastChInstr[chNum].smpNum = 255;
3020 editor.curPlayInstr = 255;
3021 editor.curPlaySmp = 255;
3022
3023 assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && smpNum < MAX_SMP_PER_INST && note <= NOTE_OFF);
3024
3025 channel_t *ch = &channel[chNum];
3026 sample_t *s = &instr[130]->smp[0];
3027
3028 memcpy(s, &instr[insNum]->smp[smpNum], sizeof (sample_t));
3029
3030 uint8_t vol = instr[insNum]->smp[smpNum].volume;
3031
3032 lockAudio();
3033
3034 s->length = smpOffset + length;
3035 s->loopStart = 0;
3036 s->loopLength = 0;
3037 DISABLE_LOOP(s->flags); // disable loop on sample #129 (placeholder)
3038
3039 int32_t samplePlayOffset = smpOffset;
3040
3041 ch->instrNum = 130;
3042 ch->noteData = (ch->instrNum << 8) | note;
3043 ch->efx = 0;
3044 ch->efxData = 0;
3045
3046 startTone(note, 0, 0, ch);
3047
3048 ch->smpStartPos = samplePlayOffset;
3049
3050 if (note != NOTE_OFF)
3051 {
3052 retrigVolume(ch);
3053 retrigEnvelopeVibrato(ch);
3054
3055 ch->realVol = vol;
3056 ch->outVol = vol;
3057 ch->oldVol = vol;
3058 }
3059
3060 ch->midiVibDepth = midiVibDepth;
3061 ch->midiPitch = midiPitch;
3062
3063 updateChannel(ch);
3064
3065 unlockAudio();
3066
3067 while (ch->status & IS_Trigger); // wait for sample to latch in mixer
3068
3069 // for sampling playback line in Smp. Ed.
3070 editor.curPlayInstr = editor.curInstr;
3071 editor.curPlaySmp = editor.curSmp;
3072 }
3073
stopVoices(void)3074 void stopVoices(void)
3075 {
3076 const bool audioWasntLocked = !audio.locked;
3077 if (audioWasntLocked)
3078 lockAudio();
3079
3080 channel_t *ch = channel;
3081 for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++)
3082 {
3083 lastChInstr[i].smpNum = 255;
3084 lastChInstr[i].instrNum = 255;
3085
3086 ch->noteData = 0;
3087 ch->relativeNote = 0;
3088 ch->smpNum = 0;
3089 ch->smpPtr = NULL;
3090 ch->instrNum = 0;
3091 ch->instrPtr = instr[0]; // important: set instrument pointer to instr 0 (placeholder instrument)
3092 ch->status = IS_Vol;
3093 ch->realVol = 0;
3094 ch->outVol = 0;
3095 ch->oldVol = 0;
3096 ch->fFinalVol = 0.0f;
3097 ch->oldPan = 128;
3098 ch->outPan = 128;
3099 ch->finalPan = 128;
3100 ch->vibDepth = 0;
3101 ch->midiVibDepth = 0;
3102 ch->midiPitch = 0;
3103 ch->portaDirection = 0; // FT2 bugfix: weird 3xx behavior if not used with note
3104
3105 stopVoice(i);
3106 }
3107
3108 // for sampling playback line in Smp. Ed.
3109 editor.curPlayInstr = 255;
3110 editor.curPlaySmp = 255;
3111
3112 stopAllScopes();
3113 resetAudioDither();
3114 resetCachedMixerVars();
3115
3116 // wait for scope thread to finish, so that we know pointers aren't deprecated
3117 while (editor.scopeThreadBusy);
3118
3119 if (audioWasntLocked)
3120 unlockAudio();
3121 }
3122
resetReplayerState(void)3123 void resetReplayerState(void)
3124 {
3125 song.pattDelTime = song.pattDelTime2 = 0;
3126 song.posJumpFlag = false;
3127 song.pBreakPos = 0;
3128 song.pBreakFlag = false;
3129
3130 if (songPlaying)
3131 {
3132 song.globalVolume = 64;
3133
3134 channel_t *ch = channel;
3135 for (int32_t i = 0; i < song.numChannels; i++, ch++)
3136 ch->status |= IS_Vol;
3137 }
3138 }
3139
setNewSongPos(int32_t pos)3140 void setNewSongPos(int32_t pos)
3141 {
3142 resetReplayerState(); // FT2 bugfix
3143 setPos((int16_t)pos, 0, true);
3144
3145 // non-FT2 fix: If song speed was 0, set it back to initial speed
3146 if (song.speed == 0)
3147 song.speed = song.initialSpeed;
3148 }
3149
decSongPos(void)3150 void decSongPos(void)
3151 {
3152 if (song.songPos == 0)
3153 return;
3154
3155 const bool audioWasntLocked = !audio.locked;
3156 if (audioWasntLocked)
3157 lockAudio();
3158
3159 if (song.songPos > 0)
3160 setNewSongPos(song.songPos - 1);
3161
3162 if (audioWasntLocked)
3163 unlockAudio();
3164 }
3165
incSongPos(void)3166 void incSongPos(void)
3167 {
3168 if (song.songPos == song.songLength-1)
3169 return;
3170
3171 const bool audioWasntLocked = !audio.locked;
3172 if (audioWasntLocked)
3173 lockAudio();
3174
3175 if (song.songPos < song.songLength-1)
3176 setNewSongPos(song.songPos + 1);
3177
3178 if (audioWasntLocked)
3179 unlockAudio();
3180 }
3181
decCurIns(void)3182 void decCurIns(void)
3183 {
3184 if (editor.curInstr <= 1)
3185 return;
3186
3187 editor.curInstr--;
3188 if ((editor.curInstr > 0x40 && !editor.instrBankSwapped) || (editor.curInstr <= 0x40 && editor.instrBankSwapped))
3189 pbSwapInstrBank();
3190
3191 editor.instrBankOffset = ((editor.curInstr - 1) / 8) * 8;
3192
3193 updateTextBoxPointers();
3194 updateNewInstrument();
3195
3196 if (ui.advEditShown)
3197 updateAdvEdit();
3198 }
3199
incCurIns(void)3200 void incCurIns(void)
3201 {
3202 if (editor.curInstr >= MAX_INST)
3203 return;
3204
3205 editor.curInstr++;
3206 if ((editor.curInstr > 0x40 && !editor.instrBankSwapped) || (editor.curInstr <= 0x40 && editor.instrBankSwapped))
3207 pbSwapInstrBank();
3208
3209 editor.instrBankOffset = ((editor.curInstr - 1) / 8) * 8;
3210 if (editor.instrBankOffset > MAX_INST-8)
3211 editor.instrBankOffset = MAX_INST-8;
3212
3213 updateTextBoxPointers();
3214 updateNewInstrument();
3215
3216 if (ui.advEditShown)
3217 updateAdvEdit();
3218 }
3219
decCurSmp(void)3220 void decCurSmp(void)
3221 {
3222 if (editor.curSmp == 0)
3223 return;
3224
3225 editor.curSmp--;
3226 editor.sampleBankOffset = (editor.curSmp / 5) * 5;
3227 setScrollBarPos(SB_SAMPLE_LIST, editor.sampleBankOffset, true);
3228
3229 updateTextBoxPointers();
3230 updateNewSample();
3231 }
3232
incCurSmp(void)3233 void incCurSmp(void)
3234 {
3235 if (editor.curSmp >= MAX_SMP_PER_INST-1)
3236 return;
3237
3238 editor.curSmp++;
3239
3240 editor.sampleBankOffset = (editor.curSmp / 5) * 5;
3241 if (editor.sampleBankOffset > MAX_SMP_PER_INST-5)
3242 editor.sampleBankOffset = MAX_SMP_PER_INST-5;
3243
3244 setScrollBarPos(SB_SAMPLE_LIST, editor.sampleBankOffset, true);
3245
3246 updateTextBoxPointers();
3247 updateNewSample();
3248 }
3249
pbPlaySong(void)3250 void pbPlaySong(void)
3251 {
3252 startPlaying(PLAYMODE_SONG, 0);
3253 }
3254
pbPlayPtn(void)3255 void pbPlayPtn(void)
3256 {
3257 startPlaying(PLAYMODE_PATT, 0);
3258 }
3259
pbRecSng(void)3260 void pbRecSng(void)
3261 {
3262 startPlaying(PLAYMODE_RECSONG, 0);
3263 }
3264
pbRecPtn(void)3265 void pbRecPtn(void)
3266 {
3267 startPlaying(PLAYMODE_RECPATT, 0);
3268 }
3269
setSyncedReplayerVars(void)3270 void setSyncedReplayerVars(void)
3271 {
3272 uint8_t scopeUpdateStatus[MAX_CHANNELS];
3273
3274 pattSyncEntry = NULL;
3275 chSyncEntry = NULL;
3276
3277 memset(scopeUpdateStatus, 0, sizeof (scopeUpdateStatus)); // this is needed
3278
3279 uint64_t frameTime64 = SDL_GetPerformanceCounter();
3280
3281 // handle channel sync queue
3282
3283 while (chQueueClearing);
3284 while (chQueueReadSize() > 0)
3285 {
3286 if (frameTime64 < getChQueueTimestamp())
3287 break; // we have no more stuff to render for now
3288
3289 chSyncEntry = chQueuePeek();
3290 if (chSyncEntry == NULL)
3291 break;
3292
3293 for (int32_t i = 0; i < song.numChannels; i++)
3294 scopeUpdateStatus[i] |= chSyncEntry->channels[i].status; // yes, OR the status
3295
3296 if (!chQueuePop())
3297 break;
3298 }
3299
3300 /* Extra validation because of possible issues when the buffer is full
3301 ** and positions are being reset, which is not entirely thread safe.
3302 */
3303 if (chSyncEntry != NULL && chSyncEntry->timestamp == 0)
3304 chSyncEntry = NULL;
3305
3306 // handle pattern sync queue
3307
3308 while (pattQueueClearing);
3309 while (pattQueueReadSize() > 0)
3310 {
3311 if (frameTime64 < getPattQueueTimestamp())
3312 break; // we have no more stuff to render for now
3313
3314 pattSyncEntry = pattQueuePeek();
3315 if (pattSyncEntry == NULL)
3316 break;
3317
3318 if (!pattQueuePop())
3319 break;
3320 }
3321
3322 /* Extra validation because of possible issues when the buffer is full
3323 ** and positions are being reset, which is not entirely thread safe.
3324 */
3325 if (pattSyncEntry != NULL && pattSyncEntry->timestamp == 0)
3326 pattSyncEntry = NULL;
3327
3328 // do actual updates
3329
3330 if (chSyncEntry != NULL)
3331 {
3332 handleScopesFromChQueue(chSyncEntry, scopeUpdateStatus);
3333 ui.drawReplayerPianoFlag = true;
3334 }
3335
3336 if (!songPlaying || pattSyncEntry == NULL)
3337 return;
3338
3339 // we have a new tick
3340
3341 editor.tick = pattSyncEntry->tick;
3342
3343 if (editor.BPM != pattSyncEntry->BPM)
3344 {
3345 editor.BPM = pattSyncEntry->BPM;
3346 ui.drawBPMFlag = true;
3347 }
3348
3349 if (editor.speed != pattSyncEntry->speed)
3350 {
3351 editor.speed = pattSyncEntry->speed;
3352 ui.drawSpeedFlag = true;
3353 }
3354
3355 if (editor.globalVolume != pattSyncEntry->globalVolume)
3356 {
3357 editor.globalVolume = pattSyncEntry->globalVolume;
3358 ui.drawGlobVolFlag = true;
3359 }
3360
3361 if (editor.songPos != pattSyncEntry->songPos)
3362 {
3363 editor.songPos = pattSyncEntry->songPos;
3364 ui.drawPosEdFlag = true;
3365 }
3366
3367 // somewhat of a kludge...
3368 if (editor.tmpPattern != pattSyncEntry->pattNum || editor.row != pattSyncEntry->row)
3369 {
3370 // set pattern number
3371 editor.editPattern = editor.tmpPattern = pattSyncEntry->pattNum;
3372 checkMarkLimits();
3373 ui.drawPattNumLenFlag = true;
3374
3375 // set row
3376 editor.row = (uint8_t)pattSyncEntry->row;
3377 ui.updatePatternEditor = true;
3378 }
3379 }
3380